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.
238 lines
7.7 KiB
C++
238 lines
7.7 KiB
C++
/*
|
||
BufferStore(Test) - verify how the BufferStore API is invoked from BufferProvider
|
||
|
||
Copyright (C)
|
||
2026, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 2 of the License, or (at your
|
||
option) any later version. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
/** @file buffer-store-test.cpp
|
||
** unit test \ref BufferStore_test
|
||
*/
|
||
|
||
|
||
#include "test/run.hpp"
|
||
#include "test/event-log.hpp"
|
||
#include "test/test-helper.hpp"
|
||
#include "vault/mem/buffer-provider-setup.hpp"
|
||
#include "vault/mem/simple-buffer-state-registry.hpp"
|
||
#include "lib/format-obj.hpp"
|
||
|
||
#include <memory>
|
||
#include <string>
|
||
|
||
using test::EventLog;
|
||
using std::string;
|
||
|
||
|
||
namespace util { // add some rigged string-conversions....
|
||
using vault::mem::Buff;
|
||
using vault::mem::LocalTag;
|
||
|
||
template<>
|
||
struct StringConv<Buff*>
|
||
{
|
||
static std::string
|
||
invoke (Buff const* dummyBuff) noexcept
|
||
{
|
||
return "Mem:" + util::showHashLSB ((size_t)dummyBuff);
|
||
}
|
||
};
|
||
template<>
|
||
struct StringConv<LocalTag>
|
||
{
|
||
static std::string
|
||
invoke (LocalTag const& localTag) noexcept
|
||
{
|
||
uint64_t dummyMark(localTag);
|
||
return "Tag:" + util::showHashLSB (dummyMark);
|
||
}
|
||
};
|
||
}
|
||
|
||
namespace vault::mem::test {
|
||
|
||
|
||
namespace { // test fixture...
|
||
/**
|
||
* Dummy implementation of the BufferProvider interface.
|
||
* Instead of a real storage backend, this test setup uses
|
||
* an embedded EventLog to mark all calls received by the
|
||
* BufferStore API-functions. Furthermore, the value
|
||
* transported in the LocalTag is returned as buffer address.
|
||
*/
|
||
class TestProvider
|
||
: public BufferProviderSetup
|
||
{
|
||
|
||
class LoggingBufferStore
|
||
: public BufferProviderSetup::Store
|
||
{
|
||
/** interpret the LocalTag as buffer address */
|
||
Buff*
|
||
asBuffer (LocalTag targetMarker)
|
||
{
|
||
void* buffMem{targetMarker};
|
||
return static_cast<Buff*> (buffMem);
|
||
}
|
||
|
||
|
||
/* =========== BufferStore interface =========== */
|
||
|
||
uint
|
||
prepareBuffers (HashVal typeID, uint cnt,size_t siz) override
|
||
{
|
||
log.call ("TestProvider", "prepareBuffers", typeID,cnt,siz);
|
||
return cnt;
|
||
}
|
||
|
||
BuffAlloc
|
||
provideBuffer (HashVal typeID, size_t siz, LocalTag localTag, int64_t customArg) override
|
||
{
|
||
log.call ("TestProvider", "provideBuffer", typeID,siz,localTag,customArg);
|
||
|
||
Buff* storage = asBuffer (localTag);
|
||
log.event ("allocate").addAttrib("mem", util::toString(storage))
|
||
.addAttrib("tag", util::toString(localTag))
|
||
;
|
||
// A real BufferStore would perform the allocation task here...
|
||
BuffAlloc storageSlot{storage, siz, localTag};
|
||
return storageSlot;
|
||
}
|
||
|
||
void
|
||
mark_emitted (HashVal typeID, BuffAlloc storageSlot) override
|
||
{
|
||
auto& [storage,siz,localTag] = storageSlot;
|
||
log.call ("TestProvider", "mark_emitted", typeID,storage,siz,localTag);
|
||
}
|
||
|
||
void
|
||
detachBuffer (HashVal typeID, BuffAlloc storageSlot) override
|
||
{
|
||
auto& [storage,siz,localTag] = storageSlot;
|
||
log.call ("TestProvider", "detachBuffer", typeID,storage,siz,localTag);
|
||
}
|
||
};
|
||
|
||
|
||
public:
|
||
TestProvider()
|
||
: BufferProviderSetup{*this}
|
||
{
|
||
log.event("Setup");
|
||
}
|
||
|
||
auto buildStage() { return std::make_unique<SimpleBufferStateRegistry> ("BufferStore_test"); }
|
||
auto buildStore() { return std::make_unique<LoggingBufferStore>(); }
|
||
|
||
|
||
static EventLog log;
|
||
|
||
|
||
/** prepare a specifically rigged »buffer type« that passes the given \a mark
|
||
* as a LocalTag to the BufferStore, which will return it as buffer "address" */
|
||
BuffDescr
|
||
specifyBufferType (size_t siz, uint64_t mark)
|
||
{
|
||
auto& typeKey = bufferStage_->defineBufferType (siz, TypeHandler::RAW, LocalTag(mark));
|
||
return buildDescriptor (typeKey);
|
||
}
|
||
};
|
||
|
||
EventLog TestProvider::log("static BufferStore_test");
|
||
|
||
|
||
}//(End) test fixture
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/***********************************************************************//**
|
||
* @test demonstrate how usage of the BufferProvider interface
|
||
* translates into calls on the internal BufferStore API.
|
||
* - use an embedded EventLog to verify sequence of events
|
||
* - use a rigged BufferStore implementation that logs invocations
|
||
* - perform a typical buffer usage cycle
|
||
* @see BufferProviderProtocol_test
|
||
* @see EventLog_test
|
||
*/
|
||
class BufferStore_test : public Test
|
||
{
|
||
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
auto& log = TestProvider::log;
|
||
log.clear ("Start BufferStore_test");
|
||
|
||
const size_t BUFFSIZ = 55;
|
||
const size_t ANOUNCE = 23;
|
||
const int64_t ARG = 88;
|
||
const uint64_t MARK = 42;
|
||
|
||
// this is how the packaged mark will be rendered...
|
||
const string TAG = util::toString (LocalTag{MARK});
|
||
const string MEM = util::toString ((Buff*) MARK);
|
||
|
||
|
||
TestProvider pro;
|
||
|
||
BuffDescr descr = pro.specifyBufferType(BUFFSIZ, MARK);
|
||
HashVal typeID{descr};
|
||
|
||
log.event ("announce buffers");
|
||
descr.announce (ANOUNCE);
|
||
|
||
log.event ("lock a buffer");
|
||
BuffHandle han = descr.lockBuffer (ARG);
|
||
CHECK (han.isAllotted());
|
||
|
||
Buff* mem = han.rawStorage();
|
||
CHECK (MARK == (uint64_t)mem );
|
||
|
||
log.event ("emit buffer");
|
||
han.emit();
|
||
|
||
log.event ("release buffer");
|
||
han.release();
|
||
CHECK (not han.isValid());
|
||
|
||
|
||
cout << "____Event-Log_________________\n"
|
||
<< util::join(log, "\n")
|
||
<< "\n───╼━━━━━━━━━━━╾──────────────"<<endl;
|
||
|
||
CHECK (log.verify ("Start BufferStore_test")
|
||
.beforeEvent("Setup")
|
||
|
||
.beforeEvent("announce buffers")
|
||
.beforeCall ("prepareBuffers").on("TestProvider").arg(typeID, ANOUNCE, BUFFSIZ)
|
||
|
||
.beforeEvent("lock a buffer")
|
||
.beforeCall ("provideBuffer") .on("TestProvider").arg(typeID, BUFFSIZ, TAG, ARG)
|
||
.beforeEvent("allocate") .attrib("mem", MEM)
|
||
.attrib("tag", TAG)
|
||
|
||
.beforeEvent("emit buffer")
|
||
.beforeCall ("mark_emitted") .on("TestProvider").arg(typeID, MEM, BUFFSIZ, TAG)
|
||
|
||
.beforeEvent("release buffer")
|
||
.beforeCall ("detachBuffer") .on("TestProvider").arg(typeID, MEM, BUFFSIZ, TAG)
|
||
);
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (BufferStore_test, "unit common");
|
||
|
||
|
||
}// namespace vault::mem::test
|