Commit graph

7154 commits

Author SHA1 Message Date
bf446626a8 BufferProvider: draft for the basic global allocator interface
This implementation and the accompanying test might seem pointless,
yet the point is that the interface is aligned to the emerging
concepts for memory allocators in Lumiera, while also directly
delegating to the std::allocator for the time being.

Before we're able to observe the actual allocator trend and behaviour,
it is pretty much moot to engage into any kind of optimisation;
the generic heap allocator performs quite well on recent C++ runtimes,
and, furthermore, the most important performance tweak is to use
a thread-local pool of recent allocations, which should allow
our jobs to run without impeding each other.
2026-04-28 00:59:50 +02:00
76dbb980b5 BufferProvider: more testing and refinements of the heuristic
Seems to work now roughly as expected, so that the next layers
of functionality can be built on top. It seems impossible
to determine the viability of this whole approach without
taking the full buffer memory management into use with
a real-world load scenario.
2026-04-27 21:26:50 +02:00
077ef891ca BufferProvider: testing of heuristics... 2026-04-27 01:52:21 +02:00
5fafb16807 BufferProvider: decide to use only a simple heuristics for reservation
At the begin of each Render Node `pull()`, the render job is expected
to ''announce'' a maximum expected allocation requirement to the `BufferProvider`,
which information is passed-on to the `LocalMemPool` to determine
to which degree it can be satisfied with allocations from that local pool.

Without actual empiric observation, it is impossible to determine
the relevance of this step, how it should be resolved heuristically
against the known contents of the pool, and how much effort to spend
on that step.

Thus I'll settle for the ''simplest thing that can possibly work'' —
which is to check only for close matches and rather rely on additional
allocations, forwarded asynchronously, assuming that the clean-up
heuristics will deal with excess allocations quickly.
2026-04-27 00:25:10 +02:00
b59cb12974 BufferProvider: devise a heuristic for buffer selection
- take into account how much memory will be wasted when
  using some allocation to satisfy a (smaller) allocation request
- furthermore prefer allocations used frequently in the past
- when an allocation is used, tag it with a score that also
  reflects this degree of waste
- select buffers with low score first for clean-up
2026-04-26 01:36:35 +02:00
dd37600c4d BufferProvider: setup implementation structure for the pool
- use boost::lockfree to implement the in-queue with new allocations
- use a std::list with an internal administrative record
- automatically ingest the in-queue prior to accessing the pool
2026-04-25 01:41:49 +02:00
e328018b05 BufferProvider: decision to use a free-standing pool with lock-free queue
After several fruitless attempts, I have decided now
to settle for self-contained ''memory pool'' component,
that can be operated **thread-locally** and employs a
**lock-free** message queue for the allotment of memory.

Seemingly the question of how this relates to `BufferMetadata`
must remain unresolved for the time being, until the entire
buffer memory subsystem is available for empirical investigation.
2026-04-24 22:55:25 +02:00
e277913e22 BufferProvider: start planning for the memory handling part
As staring point, it is clear that the `BufferStore` side of the
implementation will rely on a similar structure with a thread-local
base service — which in this case will have to maintain a small pool
of recently used buffer allocations...
2026-04-21 23:30:35 +02:00
3b7877204d BufferProvider: implement and verify the second case for synchronisation
...which happens when a buffer type is referred for the first time
from within a new worker thread, while it is already stored in the
global registry; the full key-chain is down-synced then.
2026-04-20 19:48:20 +02:00
abecd2cbde BufferProvider: implement locking and data synchronisation
...using the first usage situation as guideline,
and verify proper operation through unit-test

- the LocalBufferStage gets a set of thread-local BufferMetadata
- furthermore, it accesses the central hub via dependency-injection
- after each type registration ''from a worker thread'' data is published

As an asside: ensure most metadata registration can ''move'' the TypeHandler
2026-04-20 01:40:15 +02:00
606d98bf9c BufferProvider: establish a new buffer type from within a worker
This test setup defines one of the problematic situations
where a synchronisation is necessary.

__TODO__: actually add locking and that synchronisation to LocalBufferStage
2026-04-19 19:25:30 +02:00
57cd51e3a2 BufferProvider: draft setup for engine-wide coordination hub
What I am building step by step right now is the first draft
of a production-grade render buffer management scheme.
For this I need suitable implementations of `BufferStag` (and `BufferStore`)
These will include a `LocalSlice` to hold thread-local »satelitte services«,
which in turn need a connection to some central coordination hub.

The thread-local instances can access the hub via our `lib::Depend` (I have just
verified that this works as intended, with on-demand creation of the
central instance on first access from some thread. This is important,
since very likely we need to install a dedicated and speficically wired
service instance, which must happen prior to first dependency-request
via `lib::Depend`. And since the worker-threads are created by the Engine,
we get ample opportunity to do so before starting those threads.

Included in this changeset is a first rough draft of the central registry
`EngineBufferMetadata` — I have modelled it analogous to the `SimpleBufferStateRegistry`,
which is the test-level implementation of the `BufferStage` API.
However, we do not want to expose direct buffer allocation operations
on the central hub, and thus I have left out the respecive parts of the API.

Furthermore, I have added an `abandon()` operation, without there beeing
a need for such an operation right now. But since this operation is tricky
and potentially dangerous, I prefer implementing it the correct way,
since I have a clear understanding what this entails at the moment.
2026-04-19 18:02:58 +02:00
1601838037 BufferProvider: reduce synchronisation effort
The recursive synchronisation up the parent-chain can in most cases be skipped,
by relying on the fact that inconsistent data is never stored in the BufferMetadata
table. If thus an entry is locally present and matches the entry from the
other side, then it can be concluded that its parent must also be already in sync.
2026-04-19 15:14:31 +02:00
c05a73d68a BufferProvider: draft mechanism for buffer metadata synchronisation
Since the intention is to run several thread-local instances of `BufferMetadata`
in a concurrent setup, we need a way to publish and exchange chains of
registered type-keys and metadata entries.

The first step is to develop a mechanic for data exchange,
and to define the rules when such synchronisation is (not) allowed.
2026-04-19 01:21:24 +02:00
9bd3e550dc BufferProvider: improve configuration of thread-local dispatch
Since thread_local implies a static setup, we need a way how to
build a new service instance. Usually this would be a default construction,
yet there might be services with special init requirements.

Using an explicit template parameter for this setup configuration
allows to make the primary template parameter just the service itself,
which makes the default usage much clearer.
2026-04-18 16:25:14 +02:00
a06ef95e94 BufferProvider: skeleton for thread-local service dispatch
- Use a ''monostate'' as access point to thread-local service instances
 - rely on a configuration-context to setup the actual service
 - investigate runtime overhead using a multithreaded microbenchmark
2026-04-18 01:10:27 +02:00
8d563d1ea9 BufferProvider: lift the family-ID to the BufferStage level
During the last days, I started with analysis to find a way
to operate the `BufferProvider`, as implemented thus far,
within a massively concurrent environment.

Since I intend to create thread-local caches,
the initialisation of the `BufferMetadata` store must be static.

It turns out that the root-ID, which acts like a mandator ID,
is only used at a single code path, when creating or accessing
an abstract type-Key. It is thus easy to extract and relocate
this distinguishing ID into the actual `BufferStage` implementation.
2026-04-17 19:08:46 +02:00
4dc6885665 BufferProvider: complete the clean-up effort
Looking through all my work notes, it seems that all the
problematic aspects are now solved in a satisfactory way.
 * it is clear how to dependency-inject a `BufferProvider`
 * the internals of the `BufferProvider` front-End have been restructured
 * there are now two ''pillars'' of the implementation: `BufferStage` and `BufferStore`
 * both can be subclassed independently
 * `BufferProviderSetup` exposes a proxying-mechanism (used for `DiagnosticBufferProvider`)
 * the `BufferMetadata` have been reviewed and some minor details were rectified
 * after a re-Design of `OutputSlot`, `BuffHandle` can now be used as unified front-End
 * both `DiagnosticBufferProvider` and `DiagnosticOutputSlot` have been rewritten
 * they are now independent, self-contained specialisations, layered on top

Overall, what might seem like a detour, was an important step towards
well-balanced structures and a better understanding of the Render Engine.
2026-04-14 01:22:55 +02:00
44b3dbe682 BufferProvider: investigate and fix broken NodeDevel_test
The actual mystery, that could ''not'' be clarified,
is why that test did pass previously. Because it was
always broken, insofar it pretended that the output buffer
does hold a valid test frame, while in fact the buffer is
just newly allocated.

The deeper reason behind this mismatch lies in the concept of
''in-place processing'' — which is a very obvious, nullary optimisation,
and thus we want our engine to be ''in-place capable'' in the long run.
Which explains why I did write the dummy-test operations with in-place
processing in mind. Yet the actual version of the render engine leaves
out a lot of future improvements, amongst others the in-place processing.

This implies that — at least at the moment — it is clearly the responsibility
of the processing function to prepare the output buffer, in cases where such
preparation is necessary (and this applies in the case of `TestFrame`,
which needs to be primed with a valid header mark)

This change touches on another unsolved problem in the `TestDomainOntology`:
we need a mechanism to allow the generic builder front-end to set
specific configuration for some kind of processing. Generally speaking,
such things are possible in C++, yet somewhat tricky — and thus I postponed
that aspect and just included a configuration variant, hard-coded for the moment.
2026-04-13 01:08:48 +02:00
ce5f7d5e06 BufferProvider: new reworked implementation validated and complete (closes: #1410)
After an extended round of analysis and cascading reworks,
the long-standing discrepancy between `OutputSlot` and the
''Buffer Provider Protocol'' (#1395) has finally been resolved.
The foundation for this success was laid by means of a restructured
implementation within `BufferProvider` – which has now been
successfully completed and validated.
2026-04-12 23:17:53 +02:00
5686694999 BufferProvider: remove rest of the old implementation
__note__: regarding hash-standard.hpp see Ticket #1422
Mostly this hack worked -- but it is very likely not needed any more.
Today I had to re-wire a partial specialisation for hashing pointers,
that was added somewhere in the past into the STL.
2026-04-12 19:19:32 +02:00
7652ca05cd BufferProvider: add simplistic heap-based BufferStore implementation
Now able to reap the rewards: since all that convoluted and tangled
old ''state tracking'' has been resolved and replaced by dedicated
and self-contained diagnostic subclasses, the old dummy BufferProvider
implementation can be detached, using a similar yet very simple
fake implementation.

For context: the `HeapMemBufferStore` is part of the default configuration
and thus automatically used whenever a unit test needs a `BufferProvider`.
This default implementation is not optimised for speed, and actually
never releases any memory, which allows to verify proper handling
of buffer memory allocation.
2026-04-12 01:31:33 +02:00
2c8ee05a23 OutputSlot: validate and complete redesign (see #1395)
''This is a breakthrough...''
Using the realigned implementation structure in `BufferProvider`,
and especially the ability to establish an overarching context by
passing an connection-ID through the `LocalTag` in the `BufferMetadata`,
it was possible to resolve the longstanding mismatch buried deep within
the `OutputSlot` implementation.

Based on the new internal APIs in `BufferProvider`, it was thus possible
to setup an `OututBufferProxy` that acts as a hub to connect the
`DataSink` handles with the actual `OutputSlot::Connection` implementation.
This change allowed to rectify the convoluted control structure in
`OutputSlot` and finally to manage the Lifecycle properly. Furthermore,
one level of unnecessary implementation-inheritance could be removed,
so that it is no longer necessary to subclass `OutputSlot` in order to
configure the link to a specific output backend.

Using this redesigned internal structure, it was now fairly simple to
re-implement a tracking `DiagnosticOutputSlot` and then to verify proper
integration by passing the `OutputSlotProtocol_test` again.
2026-04-11 01:45:15 +02:00
a0d83b4fe8 Library: investigate design for an opaque inline functor
This is a design study to find out what would be required
to build an opaque function wrapper, similar to `std::function`,
yet without using any heap memory ever.

Such a building block would allow for some interesting options
for a well structured design of elaborate data structures.

This changeset provides an implementation draft.

__Two important findings to note__:
 * it is impossible to get away with just a single function pointer
 * because at the very least we need to invoke the destructor of the
   embedded delegate. Thus something akin to a "manager" is required.
 * this implies that the component would not be so efficient as I hoped,
   since it requires two pointers and requires to emit code for each
   type of delegate.
 * by making the component **NonCopyable**, I avoided the complexities
   of virtual copy support. In case the latter is desired, it would be
   much simpler to switch to a classical OO approach with a VTable
   and use our `PolymorphicValue` wrapper to handle copy and cloning.
2026-04-08 01:53:53 +02:00
0760525ea5 Library: deep dive into type reconciliation and tuple handling
This commit is the aggregated result of extended research
that is only loosely related to current development topics.

One theme was the construction of nested data structures using tuples,
which faces some difficulties due to tuple manipulations being limited
almost entirely to compile-time processing. However, the barrier between
compile-time and runtime can be overcome with the help of a dispatcher
table -- which leads to the secondary problem to handle the diverse
tuple elements with a common, generic functor. Such a functor can be
constructed, if and only if those element types are ''reconcilable''

The STDLIB provides two metafunctions, `std::common_type` and
`std::common_refernce`, which however pose some challenges for the
task at hand, since they include additional processing. In the end,
a custom version of the ''basic type reconciliation'' was implemented
and tested, and then used as foundation to build a dispatcher table
for runtime-indexed access and iteration over tuples.

As an aside, several deeply hidden bugs in our library code were resolved,
and the tuple-helpers, tuple string rendering and associated utilities
were regrouped and modernised.

As result of this research work, the following components are provided:
- meta/common-result
- meta/fun-trampoline
- meta/tuple-index-adaptor
2026-04-07 06:32:23 +02:00
a835a796b2 Library: investigate further ramifications and clean-up Itertools
The detailed analysis of corner cases indicated a lot of hidden dangers
when stretching the `IterExplorer` design beyond its limits.

By deliberate choice, this design was kept open so that advanced cohesive
implementation structures can be built. It is even possible ''and tolerated''
to rely on ''references'' to some degree -- assuming however that the
referred memory location remains stable.

As it turned out, these basic assumptions can be broken by a combination
of read-ahead processing (as is necessary for grouping and filtering),
together with adaptor objects embedded as value, especially when
the resulting pipeline object is then moved or copied during evaluation.

While my general attitude towards such extremely clever implementation
constructs is "don't shoot yourself into your foot unless you need to" --
it seems prudent to clean-up some crappy code from the first attempts
towards an iterator pipeline -- notably some of the »Itertools«.
2026-04-01 01:49:36 +02:00
40d3167d1a Library: integrate automatic adapters to flatten iterable sources
This expands the processing scheme developed thus far, so that
an arbitrary nested iterable can be handled, as long as it can be
adapted by the generic mechanisms provided by IterExplorer.

Notably there is now a type pattern match to distinguish various cases,
even spanning the ''rather esoteric'' possibility that an underlying
iterator produces STL collections by-value.

By examining these special cases, the limitations of the `IteratorExplorer`
design were highlighted, and a number of tricky bugs could be fixed.
2026-03-28 23:27:23 +01:00
d3407094be Library: prepare two wrapper-helpers to cover corner cases
Since the new `flatten()` operation was implemented in the `IterExplorer` framework,
some non-obvious corner cases must be considered; notably the source iterator
might yield something that is not by itself a Lumiera Iterator, yet could be
adapted by applying those generic mechanisms already in place to handle such cases;
however there is the special twist in the case at hand that we do not have to adapt
the ''source iterator'', yet rather the ''product'' of the source iterator —
a requirement that, once understood, can be easily adopted with
yet another iterator-decorator layered below.
2026-03-28 18:09:24 +01:00
00098dd815 Library: develop a flattening primitive for IterExplorer
This is a feature I wasn’t sure for a long time how best to tackle.
It’s worth noting that an initial attempt at `IterExplorer` was based
on the monad pattern, which turned out to be a mistake, since monads
are more of an anti-pattern, than being genuinely useful.

The actual use-case that made me reconsider such a feature it
to build an seamlessly chained sequence from several iterators.

It would be simple to develop a suitable utility as a stand-alone helper,
yet it seemed worth the effort to integrate this into the `IterExplorer`
ecosystem, due to the flexible automatic adaptation to iterable source
pipelines provided therein.

Going this route incurred some cost however, unveiling several defects
related to handling nested iterable types in the generic processing framework.
2026-03-27 23:32:55 +01:00
0326f9e85d Library: further clean-up of type definitions related to iterators
More documentation and occasional rearrangements made as an aside of
debugging issues around handling of Lumiera iterators vs. STL iterators...
2026-03-26 23:39:02 +01:00
f05402c04e Library: a stricter criterion for Lumiera iterators
On occasion of the recent improvements, I noticed that somewhere
in the past I had misunderstood the meaning of the typedefs in iterators:
 * `reference` is the central definition; this is what the iterator yields
 * `value_type` is secondary; this is the type you use to store results

So at times I defined only a `value_type`, wrongly assuming this to be
the primary definition, and all the other ones to be ephemeral.

Consequently the trait to detect a »Lumiera Forward Iterator« should be sharpened...

...which unveils a long-standing bug in our ''Duck Detector''
(it is not possible to define a pointer to a reference, causing
a check for the `reference` typedef erroneously to fail.
2026-03-26 19:08:54 +01:00
fbda9baeb3 Library: streamline value-type bindings
- use uniform naming scheme
- extract type-binding from RangeIter into a trait-template
- use the C++20 iterator concept and trait to detect a STL iterator
2026-03-26 00:46:43 +01:00
68460d4900 Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:

e176e54004

The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.

In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".

Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...

Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
 * if the `IT` parameter is a Lumiera iterator, we need to retrieve
   the type bindings defined therein
 * otherwise, we can assume that `IT` is an STL iterator and thus
   essentially a pointer, and we must strip that pointer and work from there.
2026-03-25 21:07:26 +01:00
bf77e53b4c BufferProvider: obvious convenience function
...to emplace some object into the buffer space.
I wouldn't expect that to be necessary for the real usage,
but it comes in handy for tests, where we often want to place
some sentinel value into a buffer.
2026-03-24 23:48:13 +01:00
a7c3a8d80d Testsuite: fix breakage
- visible Signature of TestFrame changed due to being relocated into the Vault

- attempt to make the WorkForce_test more robust;
  frequently fails on testsuite run after full build;
  so better ''wait'' for the workers to have started working,
  instead of using a fixed delay (that must be extremely short
  and is hard to predict)
2026-03-24 23:19:05 +01:00
54c2a30f54 OutputSlot: fill in the tracking implementation
Straight-forward:
 - add a map keyed by observed frame numbers
 - individual connection needs to know its own seqNr :-(
 - also need to store and recall the frameNr in the buffer-Index

Fix bug with DataSink constructor
(value constructor shadows copy constructor => Stackoverflow)

''Complete construction and wiring sequence works as intended''
2026-03-24 23:19:05 +01:00
ec9b73d418 OutputSlot: skeleton for a lightweight new tracking implementation
- can now throw away the old convoluted `TrackingInMemoryBlockSequence`
- instead use a simplistic implementation of the Connection interface
- do not perform any kind of validation, allocate new buffer for each frame
- use the `NaiveBufferSetup` (`BufferProvider`) as implementation backend.
2026-03-24 00:48:07 +01:00
bd09d9fbbb OutputSlot: draft new setup for the DiagnosticOutputSlot
This will be the first ''reality test'' for the new `OutputSlot` internal design:
Is it possible to implement a tracking test implementation in a clean
and straight-forward way, avoiding all the tangling and indirection
that plagued the old prototype implementation from 2012?
2026-03-23 23:44:44 +01:00
c87bc7fe27 OutputSlot: complete connection of redesigned implementation (see #1415)
After resolving the inner tensions in the old prototype from 2012,
it was now possible to resolve all issues in a single strike
 * `DataSink` becomes a functor, yet embeds a shared ref-count
 * in the λ-closure a ''specific Buffer Descriptor'' is prepared for each feed
 * building upon the prototyping work, a dedicated `OutputBufferProxy`
   exposes a `BuffHandle` to the client, with `emit()` and `release()` calls
 * the implementation ''delegates directly'' to the `OutputSlot::Connection`
 * an this is where all actual output technology is encapsulated
2026-03-23 00:31:12 +01:00
f29ec44fda OutputSlot: setup a DataSink wired to the proxy-BufferProvider
...this is the gordian λ

Note
 - embedding a copy of the shared-ptr in the closure
 - pre-configure a buffer descriptor (buffer type)
   * with the buffer size as retrieved from the connection
   * and pass the connection itself marked as LocalTag

Thus, on invocation of the DataSink (functor), it suffices
to invoke the lock() function on the BufferDescriptor,
using the new additional argument introduced with the
preceding commit
2026-03-22 17:36:38 +01:00
eeedb7a028 OutputSlot: recast the proxy BufferProvider to use the Connection directly
Reaping the benefits of the preceding refactorings, most of the complex
wiring within `OutputBufferProxy` can be omitted, and the proxy calls
can be delegated directly to the actual `Connection`

There is a catch however: we need to create an additional pathway
to feed the frame-number information into the `BufferStore` backend.
2026-03-22 00:44:02 +01:00
7bae52d1f8 OutputSlot: forge a path towards a clean delegation structure
The solution adopted here is the result of extensive reflection.
Taken literally, or naively, the reworked architecture would imply
to route every call through a delegation-chain that is dispatched
in the state-managing "PImpl" (`AllocState<CON>`).

However, in keeping with the idea of an opaque functor embedded into
`DataSink`, direct and explicitly typed calls can be bound in the
associated closure. Doing so will strengthen the role of the
interface `Connection` — which defines the boundary to the
actual domain of output technology.
2026-03-21 23:15:51 +01:00
473299fb5a OutputSlot: attempt to address deeply rooted structural problems
This changeset attempts to breach into the implementation design of OutputSlot
(while largely retaining the API and the »Output Slot Protocol«).

While the actual problem that brought me to this point can be traced back to some
rather obvious mistake, namely to conflate the `BufferProvider` used as
backing implementation with a `BufferProvider` used as front-end API,
creating the new connections to use an `OutputBufferProxy` unveils some
further long-standing structural mismatch; notably this leads to the
difficulties to get the lifecylce management of `OutputSlot` and `DataSink`
under control.

So I remove one entire level from the implementation,
in a bold move to realign the design with the prospective usage pattern.

Obviously, this breaks a lot of further code, and I need to rewire
the Allocation implementation altogether, and provide a new functor based
implementation for `DataSink`
2026-03-21 00:51:40 +01:00
0438571b73 OutputSlot: setup a dedicated proxy-provider structure
Now the »pathfinder« prototype, the `BufferProxyProvider` turns out as a good starting point;
just can simplify a lot and omit the outer capsule.
2026-03-19 00:48:19 +01:00
2ee6bb3b04 OutputSlot: look for alternative ways to implement the proxy
After a lot of pondering over various solution schemes,
it seems that `BufferProxyAdaptor` is too rigid for the task at hand.
It could be retained as a technology demonstration, and used as blueprint
for a custom-made implementation. In a nutshell, we need a `BufferStore`
that directly delegates to the respective `Connection` — which also implies
to use the `OutputSlot::AllocState` as an intermediate to hendle that delegation.
2026-03-19 00:32:02 +01:00
9b6933b572 OutputSlot: consider how to integrate the proxy provider
...which still looks like a nasty task
 * the good part is, we have a working `BufferProxyAdaptor` now
 * yet this setup does not seem to fit into the `OutputSlot::AllocState`

As an aside, several test definitions need to be relocated to the Vault,
since the whole compound around `BufferProvider` migrated down
2026-03-17 00:48:08 +01:00
ea0ae5f78e BufferProvider: propagate the actual buffer-size info
This is a nasty new requirement, discovered as an aside while looking
into ways to resolve the ''crucial mismatch'' between the `OutputSlot`
and `BufferProvider`, using the proxy-implementation of `BufferProvider`
(thereby expanding on the recent draft with the `BufferProxyAdaptor`)

The point is, if the BufferMetadata will be used beyond the internal coordination
within BufferProvider, then it will be used as part of actual buffer storage.
And this implies that the actual size of the allocation may differ (be greater)
than the nominally required size (since we'll very likely employ some kind
of memory tiling, for reasons of performance).

This leads again to an analysis of `BufferMetadata` usage,
with the conclusion to add the actual storage size directly into the API,
but treat it as an ''extra'' and not integrated into the ''buffer type''

Phew ... quite complex alltogether
2026-03-16 23:45:24 +01:00
94320d2d23 BufferProvider: remove the lock-onlyNew feature
This might have seemed like a good idea, adding an additional ''consistency check''...
However — we ''do have'' a generic check, insofar `BufferMetadata` is a **state engine**.

As long as we're able to prevent, reliably, that the same buffer address
ends up being classified as a different buffer type, this should be enough
to prevent double-use of any buffer (letting aside that `BufferProvider` is
backed by an implementing memory allocator, which in itself should prevent
any double-use.

Removing this feature resolves some ambiguity in the overall design,
and allows to turn `BufferMetadata::lock(type, address)` into a private
implementation function.

In the old code, there were some external access paths directly entering
into that implementation function, which caused a lot of confusion (at least for me...)
2026-03-16 20:37:24 +01:00
7463228d38 BufferProvider: reorganise internal BufferStore API
This refacotring prepares for the planned changes in `OutputSlot`
 * introduce a new compound parameter (tuple) BufferAlloc
 * wire this through all calls involved with storage handling

__Background__:
 * `BufferMetadata` deemed important for the implementation of buffer storage
 * ''new requirement detected'': must be able to handle an actual storage block
   that is ''larger'' than the requested buffer size (⟼ tiling)
 * need compact and uniform info, also for the `BufferProxyAdaptor` callbacks
2026-03-16 00:41:16 +01:00
e0d6362bad BufferProvider: document the adapted structure in UML
There was an old UML diagram created at the time of the first draft.
It is integrated into the »OutputSlot« page in TiddlyWiki. This description
and the diagram are highly relevant for understanding the design in this case.

Unfortunately, Umbrello was "improved" with a lot of features since I used it
for the last time, but the **quality** of the software has been **degraded**,
it is now buggy and the layout is imprecise and gets messed up easily.
2026-03-14 20:44:27 +01:00