From 43c4f7a518bc6c742a35f9b23eef6fba92026b47 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 16 Feb 2026 23:12:22 +0100 Subject: [PATCH] BufferProvider: a proxy implementation mechanism for BufferProvider internals Use a generic helper function in the `BufferProviderSetup` base class to allow layering of a decorator implementation on top of an already instantiated implementation class. After the preceding internal reorganisation, it suffices for the `DiagnosticBufferProvider` to instrument the `BufferStage` interface, in order to capture and record all relevant buffer state transitions --- src/steam/engine/buffer-provider-setup.hpp | 33 ++++++++ .../engine/diagnostic-buffer-provider.cpp | 81 +++++++++++++++++-- .../engine/diagnostic-buffer-provider.hpp | 1 + wiki/thinkPad.ichthyo.mm | 49 ++++++----- 4 files changed, 139 insertions(+), 25 deletions(-) diff --git a/src/steam/engine/buffer-provider-setup.hpp b/src/steam/engine/buffer-provider-setup.hpp index f8a8dfa0f..87fc0aab8 100644 --- a/src/steam/engine/buffer-provider-setup.hpp +++ b/src/steam/engine/buffer-provider-setup.hpp @@ -83,6 +83,39 @@ namespace engine { protected: + /** + * Enable subclasses to layer a decorator on top of + * an already instantiated implementation object + * @tparam IMP concrete type of the implementation decorator; must be specified. + * @tparam API the API to decorate; the decorator IMP must implement this API + * @tparam ARGS (optionally) further constructor arguments for the decorator + * @param rawImp the `uniqe_ptr` with an already instantiated implementation of API + * @note the decorator subclass is assumed to incorporate an unique_ptr&&, holding + * and managing the underlying implementation from that point on + * @warning the new decorator implementation is responsible for the overall sane state, + * starting from that point when it picks up the given unique_ptr. An exception + * emanating after that point will not leak memory, but destroy the original + * implementation and thus leave the whole BufferProvider in undefined state. + */ + template + void + decorate (unique_ptr& rawImp, ARGS&& ...furtherDecoratorArgs) + { + unique_ptr underlying; + try { + REQUIRE (rawImp); + std::swap (rawImp, underlying); + ENSURE (not rawImp); + rawImp = make_unique (move(underlying), forward (furtherDecoratorArgs)...); + ENSURE (rawImp); + } + catch (...) + { + REQUIRE (not rawImp); + REQUIRE (underlying); + std::swap (rawImp, underlying); + } + } }; diff --git a/src/steam/engine/diagnostic-buffer-provider.cpp b/src/steam/engine/diagnostic-buffer-provider.cpp index 966b7920f..ec6f28644 100644 --- a/src/steam/engine/diagnostic-buffer-provider.cpp +++ b/src/steam/engine/diagnostic-buffer-provider.cpp @@ -13,9 +13,9 @@ /** @file diagnostic-buffer-provider.cpp - ** Implementation details of unit test support regarding internals of the render engine. + ** Implementation details for an instrumented BufferProvider to support + ** verifying render engine buffer allocation schemes through unit tests. ** - ** @todo WIP from 2013, half finished, stalled ** @see buffer-provider-protocol-test.cpp */ @@ -30,6 +30,7 @@ #include "lib/util.hpp" #include +#include namespace steam { @@ -39,6 +40,7 @@ namespace engine { using util::unConst; using diagn::Block; using diagn::StateReg; + using std::unique_ptr; using std::ranges::find_if; @@ -92,11 +94,78 @@ namespace engine { } + class DiagnosticBufferProvider::InstrumentedStageProxy + : public BufferProviderSetup::Stage + { + using StageImp = unique_ptr; + using Tracker = DiagnosticBufferProvider::BlockTracker; + + StageImp stage_; + Tracker& tracker_; + + + /* === BufferStage proxy implementation === */ + + ID + lookup (HashVal key) override + { + return stage_->lookup (key); + } + + bool + isAccessible (HashVal stateKey) const override + { + return stage_->isAccessible (stateKey); + } + + ID + defineBufferType (size_t buffSiz, TypeHandler handlerFunctions) + { + return stage_->defineBufferType (buffSiz, move(handlerFunctions)); + } + + ID + mark_locked (ID typeKey, Buff* storage, LocalTag implMark) override + { + return stage_->mark_locked (typeKey, storage, implMark); + } + + ID + mark_emitted (HashVal stateKey) override + { + return stage_->mark_emitted (stateKey); + } + + ID + mark_released (HashVal stateKey) override + { + return stage_->mark_released (stateKey); + } + + ID + abandon (HashVal stateKey, bool invokeDtor) override + { + return stage_->abandon (stateKey, invokeDtor); + } + + void + discard (HashVal stateKey) override + { + stage_->discard (stateKey); + } + + public: + InstrumentedStageProxy (StageImp rawImpl, Tracker& tracker) + : stage_{move(rawImpl)} + , tracker_{tracker} + { } + }; + /* ====== Accounting API ====== */ void StateReg::record (BuffHandle const& handle) { - + reg_.emplace_back (std::make_shared (handle)); } @@ -104,7 +173,9 @@ namespace engine { : NaiveBufferSetup{} , heapStore_{dynamic_cast (*bufferStore_)} //////////////////////////////////////////TICKET 1410 : obsolete after switch to newtracking-API , tracker_{std::make_unique()} - { } + { + decorate (bufferStage_, *tracker_); + } DiagnosticBufferProvider::~DiagnosticBufferProvider() { @@ -117,7 +188,7 @@ namespace engine { , emitted{dbp_.tracker_->emitted} , released{dbp_.tracker_->released} { } - + /* === diagnostic API === */ diff --git a/src/steam/engine/diagnostic-buffer-provider.hpp b/src/steam/engine/diagnostic-buffer-provider.hpp index 79f0d92ef..93d92251e 100644 --- a/src/steam/engine/diagnostic-buffer-provider.hpp +++ b/src/steam/engine/diagnostic-buffer-provider.hpp @@ -85,6 +85,7 @@ namespace engine { : public NaiveBufferSetup { HeapMemBufferStore& heapStore_; //////////////////////////////OOO fällt dann weg nach dem Umbau + class InstrumentedStageProxy; struct BlockTracker; std::unique_ptr tracker_; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 020a62137..444216144 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -112046,12 +112046,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - +

- Frage: kann ProviderBufferProvider einen beliebigen  DiagnosticBuffer instrumentieren? + Frage: kann DiagnosticBufferProvider einen beliebigen  BufferProvider instrumentieren?

@@ -112063,7 +112063,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -112116,9 +112116,16 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - + + + + + + + + + + @@ -112565,7 +112572,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -112840,6 +112847,16 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + @@ -112896,16 +112913,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

Angemessen wäre so was, wenn wir hier über einen generischen Memory-Allokator reden würden, der überall in der Applikation zum Einsatz kommt. Siehe als Beispiel das lib::Several, dort habe ich so eine segmentierte Struktur gebaut, denn dort überwiegt m.E. der Nutzen so eines »insider-APIs«

- -
+
@@ -113135,22 +113149,17 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

BuffHandle hat keine Freunde ... das heißt, wenn man die release(handle) oder emegencyCleanup(handle) auf dem buffer-provider direkt aufruft, dann wird der Pointer im Handle nicht invalidiert! — das Handle ist aber danach trotzdem invalid und kann keine weitere State-transition mehr machen, weil der Entry aus den BufferMetada weggeworfen wurde.

- -
+
- - - +

Deshalb nenne ich es auch ein »Protocol« — wollten wir etwas wirklich wasserdichtes,  dann müßten wir die Struktur ganz anders anlegen, und vermutlich auch deutlich rigider, und mehr interne Zugriffe verwenden.