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.
This commit is contained in:
Fischlurch 2026-03-21 23:46:58 +01:00
parent 7bae52d1f8
commit eeedb7a028
12 changed files with 173 additions and 63 deletions

View file

@ -134,6 +134,9 @@ namespace mem {
* automatic tracking and clean-up. The client is explicitly
* responsible to invoke #releaseBuffer (either on the
* BufferProvider, or directly on the BuffHandle).
* @param type the _generic buffer type_ to base this buffer allocation on;
* at minimum this implicitly defines the buffer size, possibly also a TypeHandler
* @param arg (optional) custom argument to pass-through to the backing buffer manager
* @return a copyable handle, representing this buffer and this usage transaction.
* @throw error::State when unable to provide this buffer
* @note this function may be used right away, without prior announcing, but then
@ -146,11 +149,11 @@ namespace mem {
* return a metadata::Entry
*/
BuffHandle
BufferProvider::lockBuffer (BuffDescr const& type)
BufferProvider::lockBuffer (BuffDescr const& type, int64_t arg)
{
REQUIRE (was_created_by_this_provider (type));
auto& typeKey = bufferStage_->lookup (type);
BuffAlloc alloc= bufferStore_->provideBuffer (type,typeKey.storageSize(),typeKey.localTag());
BuffAlloc alloc= bufferStore_->provideBuffer (type,typeKey.storageSize(),typeKey.localTag(),arg);
auto& stateKey = bufferStage_->mark_locked (typeKey, alloc);
return BuffHandle (buildDescriptor(stateKey), getAddr(alloc));
@ -299,15 +302,15 @@ namespace mem {
BuffDescr::announce (uint count)
{
ENSURE (provider_);
return provider_->announce(count, *this);
return provider_->announce (count, *this);
}
BuffHandle
BuffDescr::lockBuffer()
BuffDescr::lockBuffer (int64_t arg)
{
ENSURE (provider_);
return provider_->lockBuffer(*this);
return provider_->lockBuffer (*this, arg);
}

View file

@ -182,7 +182,7 @@ namespace mem {
uint announce (uint count, BuffDescr const&);
/** claim storage for exclusive use, based on a descriptor */
BuffHandle lockBuffer (BuffDescr const&);
BuffHandle lockBuffer (BuffDescr const&, int64_t=0);
/** directly claim storage and construct an object,
* without preparing a descriptor beforehand */
@ -236,10 +236,10 @@ namespace mem {
public:
virtual ~BufferStore() { } ///< this is an interface
virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) =0;
virtual BuffAlloc provideBuffer (HashVal typeID, size_t, LocalTag) =0;
virtual void mark_emitted (HashVal typeID, BuffAlloc) =0;
virtual void detachBuffer (HashVal typeID, BuffAlloc) =0;
virtual uint prepareBuffers (HashVal typeID, uint cnt,size_t) =0;
virtual BuffAlloc provideBuffer (HashVal typeID, size_t,LocalTag,int64_t) =0;
virtual void mark_emitted (HashVal typeID, BuffAlloc) =0;
virtual void detachBuffer (HashVal typeID, BuffAlloc) =0;
};
unique_ptr<BufferStage> bufferStage_;

View file

@ -87,7 +87,7 @@ namespace mem {
}
BuffAlloc
provideBuffer (HashVal,size_t siz,LocalTag targetMarker) override
provideBuffer (HashVal,size_t siz,LocalTag targetMarker, int64_t customArg) override
{
BuffAlloc storageSlot{asBuffer(targetMarker), siz, targetMarker};
CONF::on_lock (storageSlot);

View file

@ -115,7 +115,11 @@ namespace mem {
/** callback to engage buffer handling cycle */
uint announce (uint count);
BuffHandle lockBuffer();
/** claim an actual buffer for exclusive use.
* @param arg a custom argument to pass to the buffer manager
*/
BuffHandle lockBuffer (int64_t arg=0);
};

View file

@ -289,7 +289,7 @@ namespace mem {
BuffAlloc
HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag)
HeapMemBufferStore::provideBuffer (HashVal typeID, size_t buffSiz, LocalTag, int64_t)
{
BlockPool& blocks = getBlockPoolFor (buffSiz, typeID);
Block& newBlock = blocks.createBlock();

View file

@ -66,10 +66,10 @@ namespace mem {
public:
/* === BufferStore interface === */
virtual uint prepareBuffers (HashVal typeID, uint cnt, size_t) override;
virtual BuffAlloc provideBuffer (HashVal,size_t,LocalTag) override;
virtual void mark_emitted (HashVal typeID, BuffAlloc) override;
virtual void detachBuffer (HashVal typeID, BuffAlloc) override;
uint prepareBuffers (HashVal typeID, uint cnt, size_t) override;
BuffAlloc provideBuffer (HashVal,size_t,LocalTag,int64_t) override;
void mark_emitted (HashVal typeID, BuffAlloc) override;
void detachBuffer (HashVal typeID, BuffAlloc) override;
public:
~HeapMemBufferStore();

View file

@ -135,19 +135,21 @@ namespace out {
/* === Connection API === */
BuffHandle
claimBufferFor(FrameID frameNr)
Buff*
claimBufferFor (FrameID frameNr) override
{
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring
TRACE (test, "Con=%p : lock buffer for frame-#%lu", this, frameNr);
REQUIRE (!closed_);
return trackFrame (frameNr,
buffProvider_.lockBuffer (bufferType_));
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
}
bool
isTimely (FrameID frameNr, TimeValue currentTime)
isTimely (FrameID frameNr, TimeValue currentTime) override
{
TRACE (test, "Con=%p : timely? frame-#%lu"
, this, frameNr);
@ -159,32 +161,38 @@ namespace out {
}
void
transfer (BuffHandle & filledBuffer)
transfer (Buff* filledBuffer) override
{
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring
TRACE (test, "Con=%p : transfer buffer %zu"
, this, HashVal(filledBuffer));
REQUIRE (!closed_);
pushout (filledBuffer);
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
}
void
pushout (BuffHandle & data4output)
pushout (Buff* data4output) override
{
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring
REQUIRE (!closed_);
buffProvider_.emitBuffer (data4output);
buffProvider_.releaseBuffer(data4output);
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
}
void
discard (BuffHandle & superseededData)
discard (Buff* superseededData) override
{
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring
REQUIRE (!closed_);
buffProvider_.releaseBuffer (superseededData);
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
}
void
shutDown ()
shutDown() override
{
closed_ = true;
}

View file

@ -66,7 +66,7 @@ namespace out {
* - callback functors for the lifecycle stages related to the client's access
* @todo 3/2026 this is prototyping code and was retained for demonstration purposes.
*/
template<class HUB>
template<class CON>
class OutputBufferProxy
: public BufferProviderSetup
{
@ -74,13 +74,11 @@ namespace out {
class OutputBufferStore
: public BufferProviderSetup::Store
{
HUB& output_;
Buff*
asBuffer (LocalTag targetMarker)
CON&
asTarget (LocalTag targetMarker)
{
void* buffMem{targetMarker};
return static_cast<Buff*> (buffMem);
void* targetAdr{targetMarker};
return * static_cast<CON*> (targetAdr);
}
uint
@ -91,9 +89,10 @@ namespace out {
}
BuffAlloc
provideBuffer (HashVal,size_t siz,LocalTag targetMarker) override
provideBuffer (HashVal,size_t siz, LocalTag targetMarker, int64_t frameNr) override
{
BuffAlloc storageSlot{asBuffer(targetMarker), siz, targetMarker};
Buff* buffer = asTarget(targetMarker).claimBufferFor (frameNr);
BuffAlloc storageSlot{buffer, siz, targetMarker};
TODO ("delegate buffer lock");
return storageSlot;
}
@ -109,19 +108,12 @@ namespace out {
{
TODO ("delegate buffer release");
}
public:
OutputBufferStore(HUB& outputHub)
: output_{outputHub}
{ }
};
struct Setup
{
HUB& outputHub_;
auto buildStage() { return std::make_unique<vault::mem::SimpleBufferStateRegistry>("OutputBufferProxy"); }
auto buildStore() { return std::make_unique<OutputBufferStore> (outputHub_); }
auto buildStore() { return std::make_unique<OutputBufferStore>(); }
};
BuffDescr
@ -135,8 +127,8 @@ namespace out {
public:
OutputBufferProxy(HUB& outputHub)
: BufferProviderSetup{Setup{outputHub}}
OutputBufferProxy()
: BufferProviderSetup{Setup{}}
{ }

View file

@ -54,6 +54,7 @@
namespace vault {
namespace out {
using vault::mem::Buff;
using vault::mem::BuffHandle;
using vault::mem::BufferProvider;
using lib::time::TimeValue; /////////////////OOO Rly?
@ -94,12 +95,12 @@ namespace out {
public:
virtual ~Connection();
virtual BuffHandle claimBufferFor(FrameID) =0;
virtual bool isTimely (FrameID, TimeValue) =0;
virtual void transfer (BuffHandle &) =0;
virtual void pushout (BuffHandle &) =0;
virtual void discard (BuffHandle &) =0;
virtual void shutDown () =0;
virtual Buff* claimBufferFor(FrameID) =0;
virtual bool isTimely (FrameID, TimeValue) =0;
virtual void transfer (Buff*) =0;
virtual void pushout (Buff*) =0;
virtual void discard (Buff*) =0;
virtual void shutDown () =0;
};
@ -127,7 +128,7 @@ namespace out {
{
using Connections = lib::ScopedCollection<CON>;
using OpenedSinks = OutputSlot::OpenedSinks;
using BufferProxy = OutputBufferProxy<AllocState>;
using BufferProxy = OutputBufferProxy<Connection>;
Connections connections_;
BufferProxy bufferProxy_;
@ -171,7 +172,7 @@ namespace out {
template<class CTOR>
AllocState(uint numChannels, CTOR&& populator)
: connections_(numChannels, std::forward<CTOR>(populator))
, bufferProxy_{*this}
, bufferProxy_{}
{ }

View file

@ -67,6 +67,7 @@ namespace play {
namespace test {
using fixture::ModelPortRegistry;
using vault::mem::Buff;
using vault::mem::BuffHandle;
using vault::out::DataSink;
using vault::out::FrameID;
@ -79,11 +80,11 @@ namespace test {
class UnimplementedConnection
: public vault::out::OutputSlot::Connection
{
BuffHandle claimBufferFor (FrameID) override;
Buff* claimBufferFor (FrameID) override;
bool isTimely (FrameID, TimeValue) override { return true; }
void transfer (BuffHandle &) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); }
void pushout (BuffHandle &) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); }
void discard (BuffHandle &) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); }
void transfer (Buff*) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); }
void pushout (Buff*) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); }
void discard (Buff*) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); }
void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); }
public:
@ -199,7 +200,7 @@ namespace test {
}
};
inline vault::mem::BuffHandle UnimplementedConnection::claimBufferFor (FrameID)
inline vault::mem::Buff* UnimplementedConnection::claimBufferFor (FrameID)
{
UNIMPLEMENTED ("claimBufferFor(FrameID)");
}

View file

@ -5157,17 +5157,17 @@ 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&lt;DEF&gt;}}} 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.
</pre>
</div>
<div title="OutputSlot" modifier="Ichthyostega" created="201106162339" modified="202603142327" tags="def Concepts Player spec img" changecount="23">
<div title="OutputSlot" modifier="Ichthyostega" created="201106162339" modified="202603220010" tags="def Concepts Player spec img" changecount="26">
<pre>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, using a beamer, sound, Jack, rendering to file) can be added and removed dynamically from various components (Vault, Stage), all using the same resolution and mapping mechanisms (&amp;rarr; OutputManagement)
!Properties of an output slot
Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right soundcard output channels), or an output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of &quot;sound out&quot;). An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right sound card output channels), or an output file or similar capability accepting media content. The setup for an {{{OutputSlot}}} is provided, configured and registered with an OutputManager, using an implementation suitable for the kind of media data to be sent (sound, video) and also suitable for the desired kind if output capability (render to file, display video in a GUI widget, send video to a full screen display, establish a Jack port or use any kind of &quot;sound out&quot;). Thus it is always limited to a single kind of media, and to a single connection unit, but this connection may still comprise several data feeds (e.g. stereoscopic video, multichannel sound). The number of such feeds and the expected data format depends on the _media stream type_ used for configuring or retrieving this OutputSlot. For example, a stereo sound output may expect only a single data feed, where data blocks for both channels are sent in interleaved format.
In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (in special cases, some external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
Once the client retrieves the OutputSlot for some desired output capability, the slot was already _allocated_ for exclusive use for that client. At any time, there may be only a single client using a given output capability this way. It should be noted thus that output slots don't provide any kind of //inherent mixing capability;// any adaptation, mixing, overlaying and sharing needs to be done within the Render Node Network that produces the data to be output. (As an aside, //some// external output frameworks — e.g. the Jack audio connection system — may still provide additional mixing capabilities, yet these are beyond the scope of the Lumiera application)
[&gt;img[Outputslot implementation structures|uml/OutputSlot_components.svg]]
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.
The {{{OutputSlot}}} object is a (copyable) front-end handle, that should be stored by the client and discarded when further output is no longer required. From the OutputSlot, a set of actual ''data sink handles'' can be retrieved (one for each possible data feed). Starting from these handles, a ''two-phase output protocol'' can be executed for each frame of data to send to the output, which includes to obtain a BuffHandle, similar to what the BufferProvider exposes. The suitability of the buffer and the data format is not validated in any way at that level, since the setup works under the assumption that the StreamType and further metadata has been validated or configured suitably at a higher level. However, the OutputSlot exposes a definition of [[timing constraints|Timings]] to describe when data must be provided, and what deadline to observe for each frame. The client is required to comply to these {{{vault::out::Timings}}} both when acquiring the BuffHandle for some frame and when ''emitting'' data -- any use outside these limits will cause that frame to be treated as a glitch. Furthermore, the client is required to invoke {{{BuffHandle::release()}}} as soon as no further access to the buffer is required (and after invoking `emit()`). Failure to do so will cause a blocked buffer and typically leads to abort of the output and play process. However, individual frames that miss the deadline are handled gracefully; the actual implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically whenever a timing failure is detected.
!!!usage and implementation
Clients retrieve a reference to an output slot by asking a suitable OutputManager for an output possibility that supports some specific media and format. Usually, they just &quot;claim&quot; this slot by invoking {{{allocate()}}}, which causes actual output connections to be built and data handover mechanisms to be established, behind the scenes. For each such connection -- corresponding to a single distinct data feed within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The feed may, or may not correspond to a nominal channel in the media format, since some output interfaces require to deliver the data in separate buffers per channel, while others require data to be interleaved. Generally speaking, the concrete ~OutputSlot implementation performs operations rather specific to the kind of output and external interface in question -- thereby calling directly into a specific connection implementation. The »''Output Slot Protocol''« however is designed in a way to abstract from all those technical details.
@ -6265,7 +6265,7 @@ This is the core service provided by the player subsystem. The purpose is to cre
:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
</pre>
</div>
<div title="PlaybackVerticalSlice" creator="Ichthyostega" modifier="Ichthyostega" created="202303272236" modified="202601230245" tags="overview impl discuss draft" changecount="50">
<div title="PlaybackVerticalSlice" creator="Ichthyostega" modifier="Ichthyostega" created="202303272236" modified="202603212353" tags="overview impl discuss draft" changecount="51">
<pre>//Integration effort to promote the development of rendering, playback and video display in the GUI//
This IntegrationSlice was started in {{red{2023}}} as [[Ticket #1221|https://issues.lumiera.org/ticket/1221]] to coordinate the completion and integration of various implementation facilities, planned, drafted and built during the last years; this effort marks the return of development focus to the lower layers (after years of focussed UI development) and will implement the asynchronous and time-bound rendering coordinated by the [[Scheduler]] in the [[Vault|Vault-Layer]]
@ -6312,6 +6312,8 @@ __February.25__: built extended verification mechanisms, including some Node met
__January.26__: During the last nine months, unfortunately I was occupied with generic clean-up, upgrade, infrastructure and research work -- and so there was ''no progress'' regarding the PlaybackVerticalSlice. Just before that recess, an important breakthrough was achieved: a complete node graph was built and invoked end to end in a unit test setting. The further integration up to a RenderJob invocation is still blocked by some yet unresolved [[design mismatch (#1395)|https://issues.lumiera.org/ticket/1395]] related to the //»BufferProvider Protocol«// vs the //»OutputSlot Protocol«//; this is where I'll pick up the development work stalled almost a year ago.
__March.26__: Meanwhile I was able to get at the root of the confusion related to BufferProvider and OutputSlot. This insight allows to rectify and realign the architecture, while largely keeping the APIs and both »Protocols« intact. As part of this design revalidation, I did an in-depth re-assessment of buffer metadata handling and got a much better understanding of the requirements for an actual buffer manager implementation -- and it is clear now that at least such a basic implementation is required to achieve a first engine integration. Furthermore, I was indeed able to build a proxy-BufferProvider that can be used as façade within the OutputSlot Protocol, so that both Protocols can be reconciled. The integration of this new proxy implementation necessitates a deeper refactoring within OutputSlot though, leading to a much better and cleaner design overall.
!Decisions
;Scheduler
:is understood as a high-level Service, not a bare bone implementation mechanism

View file

@ -138551,7 +138551,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
<node CREATED="1774122219137" ID="ID_656953886" MODIFIED="1774122240553" TEXT="&#x27f9; alle weiteren Protokollschritte erfolgen direkt von dort"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1774122365597" ID="ID_292817548" MODIFIED="1774122379717" TEXT="Abw&#xe4;gung: wohin delegieren die Proxy-Callbacks?">
<node COLOR="#435e98" CREATED="1774122365597" ID="ID_292817548" MODIFIED="1774131392789" TEXT="Abw&#xe4;gung: wohin delegieren die Proxy-Callbacks?">
<icon BUILTIN="help"/>
<node CREATED="1774122388349" ID="ID_1459974960" MODIFIED="1774122391728" TEXT="m&#xf6;glich w&#xe4;re...">
<node CREATED="1774122410671" ID="ID_1178347692" MODIFIED="1774124507440" TEXT="an eine Connection (Interface)">
@ -138655,7 +138655,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</body>
</html></richcontent>
</node>
<node CREATED="1774124482636" ID="ID_99417390" MODIFIED="1774124502539" TEXT="Beschlu&#xdf;: Direk-Aufruf auf das Connection-Interface">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#17196b" CREATED="1774124482636" ID="ID_99417390" MODIFIED="1774131406831" TEXT="Beschlu&#xdf;: Direkt-Aufruf auf das Connection-Interface">
<icon BUILTIN="forward"/>
<icon BUILTIN="full-1"/>
</node>
@ -138689,6 +138689,105 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1774126881885" ID="ID_899991315" MODIFIED="1774126887766" TEXT="der Proxy-BufferProvider"/>
</node>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1774132649643" ID="ID_1759796259" MODIFIED="1774132670265" TEXT="Problem &#xfc;bersehen: die Frame-Nummer">
<icon BUILTIN="broken-line"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1774132731585" ID="ID_396286799" MODIFIED="1774136559514">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
das Connection-Interface (Entwurf) verlangt eine Frame-Nr f&#252;r <font face="Monospaced"><b>claimBufferFor</b>(frame)</font>
</p>
</body>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1774132780386" ID="ID_837938632" MODIFIED="1774132808434" TEXT="daf&#xfc;r fehlt mir ein Kommunikations-Kanal &#x27f6; OutputBufferProxy">
<node CREATED="1774132820987" ID="ID_1358245383" MODIFIED="1774132823828" TEXT="LocalTag">
<node CREATED="1774132825036" ID="ID_588976778" MODIFIED="1774132964077" TEXT="ist entweder die Connection">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
das w&#228;re die sch&#246;ne und elegante L&#246;sung die ich mir heute ausgedacht habe (und dabei die Frame-Nr &#252;bersehen)
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1774132833155" ID="ID_484094660" MODIFIED="1774133019220" TEXT="oder die Connection-Nr + verdrahtete Back-Ref">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
das w&#228;re die offensichtliche und d&#228;mliche L&#246;sung: man verdrahtet einen back-Link auf den AllocState, und gibt einen Index in die Connection-Tafel &#252;ber das LocalTag durch.
</p>
</body>
</html></richcontent>
</node>
<node COLOR="#5b280f" CREATED="1774132862617" ID="ID_588130028" MODIFIED="1774133294975" TEXT="Buffer geht nicht (brauche dann Index)">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Deshalb habe ich die zun&#228;chst gebaute L&#246;sung mit dem <font color="#794c4c" face="Monospaced">BufferProxyAdaptor</font>&#160;aufgegeben: denn in den einzelnen Lifecycle-Methoden wei&#223; man dann nicht, zu welcher Connection der Callback geh&#246;rt. Man m&#252;&#223;te dann im AllocState eine reverse-Index-Tabelle aufbauen, mit der man von der Buffer-Adresse zur passenden Connection zur&#252;ckfindet. Prinzipiell w&#228;re das m&#246;glich (da Buffer-Adressen global eindeutig sind und daher niemals gleichzeitig von zwei verschiedenen Connections verwendet werden k&#246;nnen .... allerdings damit h&#246;rts auch schon auf, denn <i>sp&#228;ter k&#246;nnte durchaus eine andere Connection den gleichen Buffer verwenden</i>)
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_cancel"/>
</node>
</node>
<node CREATED="1774133338567" ID="ID_1915597180" MODIFIED="1774133354752" TEXT="v&#xf6;llig egal wie, in jedem Fall ist die Frame-Nummer eine zus&#xe4;tzliche Info"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1774133516471" ID="ID_1070291197" MODIFIED="1774136481626" TEXT="&#x27f9; bleibt nur, einen Kanal zu schaffen">
<icon BUILTIN="yes"/>
<node CREATED="1774133549299" ID="ID_437134456" MODIFIED="1774134318162" TEXT="L&#xf6;sung-1 : zus&#xe4;tzliches Argument durchf&#xe4;deln">
<icon BUILTIN="forward"/>
<node CREATED="1774133599076" ID="ID_1639698129" MODIFIED="1774133622157" TEXT="das w&#xe4;re dann im &#xf6;ffentlichen BufferProviderAPI">
<node CREATED="1774133628078" ID="ID_1884544207" MODIFIED="1774133676414" TEXT="BufferProvider::lockBuffer (BuffDescr, int64_t customArg =0)"/>
<node CREATED="1774133685170" ID="ID_1491964539" MODIFIED="1774133704827" TEXT="da der Aufruf hier direkt aus dem Funktor kommt w&#xe4;re das machbar"/>
<node CREATED="1774133713301" ID="ID_1681738826" MODIFIED="1774133778916" TEXT="m&#xfc;&#xdf;te dann zus&#xe4;tzlich nochmal einen Pointer auf den OutputBufferProxy in die Closure binden"/>
</node>
<node CREATED="1774133855977" ID="ID_247819999" MODIFIED="1774133873835" TEXT="k&#xf6;nnte auch ein spezialAPI auf OutputBufferProxy schaffen">
<node COLOR="#5b280f" CREATED="1774133898723" ID="ID_407416009" MODIFIED="1774133923914" TEXT="das w&#xfc;rde dann zudem noch die interne BufferProvider::lockBuffer aufdoppeln">
<icon BUILTIN="button_cancel"/>
<node CREATED="1774133925648" ID="ID_1503448137" MODIFIED="1774133930448" TEXT="Implementation-reuse"/>
</node>
<node CREATED="1774133931949" ID="ID_1017504193" MODIFIED="1774133950185" TEXT="und obendrein m&#xfc;&#xdf;ten wir auch in dem Fall einen zus&#xe4;tzlichen Pointer mit binden"/>
</node>
</node>
<node CREATED="1774133954237" ID="ID_1833902029" MODIFIED="1774134322458" TEXT="L&#xf6;sung-2 : kontextuell &#xfc;bermitteln">
<icon BUILTIN="button_cancel"/>
<node CREATED="1774133994727" ID="ID_702943419" MODIFIED="1774134019424" TEXT="das Connection-API wird dadurch (etwas mehr) stateful"/>
<node CREATED="1774134039902" ID="ID_1189428583" MODIFIED="1774134064802" TEXT="es mu&#xdf; dann einen &#xbb;current frame&#xab; geben"/>
<node CREATED="1774134118631" ID="ID_1354625599" MODIFIED="1774134133569" TEXT="und dazu eine Konvention, da&#xdf; der Aufruf unmittelbar im gleichen Thread erfolgt"/>
<node CREATED="1774134140492" ID="ID_260619767" MODIFIED="1774134260873" TEXT="die weiteren API-Funktionen sind ohnehin implizit stateful (&#x27f6; Buffer-Adresse)">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Das API <i>ist nicht stateless,</i>&#160;da Buffer belegt und verwaltet werden. Das ist essntieller Zustand. Insofern liefert der claimBufferFor(frame)-Aufruf eine Buffer-Adresse, und die weiteren Aufrufe beziehen sich darauf.
</p>
</body>
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1774134324929" ID="ID_1370520896" MODIFIED="1774134730585" TEXT="Entscheidung ist offensichtlich">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Auf den ersten Blick ist nat&#252;rlich L&#246;sung-2 viel attraktiver, denn das ist schnell reingehackt, und dann funktioniert alles wie bisher. Hinsichtlich Interface-Design w&#228;re das aber eine Bankrott-Erkl&#228;rung; jetzt geb ich mir so viel M&#252;he, da ein klares Design zu schaffen, und dann f&#252;hre ich eine solche faule Konvention ein, die beim Implementatior jede Menge Fragen aufwirft, und praktisch dazu zwingt, die Implementierung des Aufrufers (OutputSlot::AllocState&lt;CON&gt; bzw. OutputBufferProxy) im Detail zu durchdringen (&#10233; Kopplung).
</p>
<p>
Dem gegen&#252;ber ist L&#246;sung-1 zwar nicht sch&#246;n, aber akzeptabel; letztlich schaffen wir uns ein Interface immer zu einem Zweck, und hier verlangt der Zweck eben, eine weitere Info zum Buffer-Manager durchzuleiten (ohne sie direkt in die Metadaten zu speichern). SowaskommtindenbestenFamilienvor. Zudem kann ich das Argument defaulten. Ich bin sogar geneigt, das ganz offiziell auch auf das Buffer-Descripter-API zu heben: aus Gr&#252;nden der Symmetrie, und weil wir uns damit den zus&#228;tzlichen Pointer sparen k&#246;nnen)
</p>
</body>
</html></richcontent>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>
</node>