Dispatcher-Pipeline: builder type rebinding problems

...hard to tackle...
The idea is to wrap the TreeExplorer builder, so that our specific
builder functions can delegated to the (inherited) generic builder functions
and would just need to supply some cleverly bound lambdas. However,
resulting types are recursive, which does not play nice with type inference,
and working around that problem leads to capturing a self reference,
which at time of invocation is already invalidated (due to moving the
whole pipeline into the final storage)
This commit is contained in:
Fischlurch 2023-06-03 03:39:33 +02:00
parent 94fe4a4bec
commit 81ee9a2e67
8 changed files with 369 additions and 64 deletions

View file

@ -1704,13 +1704,13 @@ namespace lib {
/** start building a TreeExplorer
* by suitably wrapping the given iterable source.
* @return a TreeEplorer, which is an Iterator to yield all the source elements,
* but may also be used to build an processing pipeline.
* but may also be used to build up a complex processing pipeline.
* @warning if you capture the result of this call by an auto variable,
* be sure to understand that invoking any further builder operation on
* TreeExplorer will invalidate that variable (by moving it into the
* augmented iterator returned from such builder call).
*
* # usage
* # Usage
*
* This function starts a *Builder* expression. It picks up the given source,
* which can be something "sequence-like" or "iterable", and will automatically

View file

@ -43,7 +43,9 @@
#include "steam/engine/job-ticket.hpp"
#include "steam/engine/job-planning.hpp"
#include "steam/play/output-slot.hpp"
#include "lib/iter-tree-explorer.hpp"
#include "lib/time/timevalue.hpp"
//#include "lib/nocopy.hpp"
#include <functional>
@ -82,7 +84,7 @@ namespace engine {
* which, for the purpose of dispatching a series of jobs just looks
* like a sequence of job descriptors
*
* @todo 10/12 still WIP, but conceptually settled by now
* @todo 6/23 API is remoulded from ground up (»Playback Vertical Slice« integration effort)
*/
class Dispatcher
: public FrameLocator
@ -106,13 +108,28 @@ namespace engine {
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsolete
using FrameNrIter = lib::NumIter<FrameCnt>;
struct PipelineBuilder
: lib::SingleValIter<TimeVar> ////////////////////////////////////////OOO placeholder type; should rather be a TreeExplorer!
: FrameNrIter
{
PipelineBuilder&
Dispatcher* dispatcher;
Timings timings;
ModelPort port;
DataSink sink;
auto
timeRange (Time start, Time after)
{
UNIMPLEMENTED ("setup dispatch time range");
auto frame = [&](Time t){ return timings.getBreakPointAfter(t); };
auto reset = [&](auto i){ static_cast<FrameNrIter&>(*this) = i; };
reset (lib::eachNum (frame(start), frame(after)));
return lib::treeExplore (move(*this))
.transform ([&](FrameCnt frameNr) -> TimeVar //////////////////////////////TICKET #1261 : transform-iterator unable to handle immutable time
{
return timings.getFrameStartAt (frameNr);
});
}
};
@ -126,7 +143,7 @@ namespace engine {
PipelineBuilder forCalcStream(Timings timings, ModelPort port, DataSink sink)
{
UNIMPLEMENTED ("create Pipeline builder");
return PipelineBuilder{FrameNrIter(), this, timings, port, sink};
}

View file

@ -30,40 +30,33 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
//#include "steam/engine/procnode.hpp"
#include "steam/play/dummy-play-connection.hpp"
#include "steam/mobject/model-port.hpp"
#include "steam/engine/dispatcher.hpp"
#include "steam/play/timings.hpp"
#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
//#include "lib/format-cout.hpp"
#include "lib/depend.hpp"
#include "lib/itertools.hpp"
#include "lib/util-coll.hpp"
//#include "lib/util-coll.hpp"
#include "lib/util.hpp"
#include <functional>
#include <vector>
//#include <functional>
//#include <vector>
using test::Test;
using util::isnil;
using util::last;
using std::vector;
using std::function;
//using std::rand;
//using std::vector;
//using std::function;
using std::rand;
namespace steam {
namespace engine{
namespace test {
using lib::time::FSecs;
using lib::time::FrameCnt;
using lib::time::FrameRate;
using lib::time::Duration;
using lib::time::Offset;
using lib::time::TimeValue;
using lib::time::Time;
using mobject::ModelPort;
using play::Timings;
namespace { // Test fixture...
@ -75,17 +68,6 @@ namespace test {
const uint DUMMY_CHANNEL(0); /////////////////////////////////////////////////////////////TICKET #1297 : get rid of the channels (use different ModelPort)
// ModelPort
// getTestPort()
// {
// using play::test::ModelPorts;
// using play::test::PlayTestFrames_Strategy;
// using DummyPlaybackSetup = play::test::DummyPlayConnection<PlayTestFrames_Strategy>;
//
// DummyPlaybackSetup dummySetup;
// ModelPorts mockModelPorts = dummySetup.provide_testModelPorts();
// return *mockModelPorts; // using just the first dummy port
// }
FSecs

View file

@ -0,0 +1,265 @@
/*
DUMMY-BUILDER-CONTEXT.hpp - fake simulated builder result for player tests
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file dummy-builder-context.hpp
** Simulated result of a builder run for test purposes, without actual session and model content.
** Integration tests for the render engine or the player have to deal with a lot of prerequisites,
** since both an external environment and actual content form the Session is required to start any
** processing. Effectively the complete core application is required while in fact the engine is
** built in an abstracted way and does not need to know much about the rest of the application.
** Thus, as a remedy, it is possible to establish a _faked context,_ exposing just those interfaces
** used by the engine for processing. As additional benefit, dedicated test operations can be rigged
** and verification routines can be used in place of actual media output.
**
** This faked builder context provides a selection of consistent media element IDs with sensible
** properties and type markers, plus the attachment points for a simulated model structure, as
** if generated from an actual session model by a real Builder run.
** - a set of ModelPort elements
** - a set of DataSink handles, as if an actual playback connection has been established
** @todo 5/2023 add more elements as needed for test driven integration of Player and engine
** @todo this was invented in 2012 -- but development of the player subsystem stalled thereafter.
**
** @see mock-dispatcher.hpp
** @see JobPlanningSetup_test
*/
#ifndef STEAM_PLAY_DUMMY_BUILDER_CONTEXT_H
#define STEAM_PLAY_DUMMY_BUILDER_CONTEXT_H
#include "steam/mobject/model-port.hpp"
#include "steam/mobject/builder/model-port-registry.hpp"
#include "steam/play/output-slot-connection.hpp"
#include "steam/play/output-manager.hpp"
#include "steam/asset/timeline.hpp"
#include "steam/asset/pipe.hpp"
#include "common/query.hpp"
#include "lib/iter-source.hpp"
#include "lib/nocopy.hpp"
#include "lib/util.hpp"
#include <vector>
namespace steam{
namespace play {
namespace test {
/**
* @todo 5/2023 quick-n-dirty placeholder to be able to fabricate fake DataSink handles (`Handle<Connection>`)
*/
class UnimplementedConnection
: public play::OutputSlot::Connection
{
BuffHandle claimBufferFor(FrameID) override { UNIMPLEMENTED ("claimBufferFor(FrameID)"); }
bool isTimely (FrameID, TimeValue) override { return true; }
void transfer (BuffHandle const&) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); }
void pushout (BuffHandle const&) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); }
void discard (BuffHandle const&) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); }
void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); }
public:
~UnimplementedConnection();
UnimplementedConnection() = default;
};
using asset::Pipe;
using asset::PPipe;
using asset::Struct;
using asset::Timeline;
using asset::PTimeline;
using mobject::ModelPort;
using mobject::builder::ModelPortRegistry;
using util::contains;
using lumiera::Query;
using PID = asset::ID<Pipe>;
using TID = asset::ID<Struct>;
using ModelPorts = lib::IterSource<mobject::ModelPort>::iterator;
using DummyOutputLink = std::pair<mobject::ModelPort, play::DataSink>;
inline PID
getPipe (string id)
{
return Pipe::query ("id("+id+")");
}
inline TID
getTimeline (string id)
{
return asset::Struct::retrieve (Query<Timeline> ("id("+id+")"))->getID();
}
const uint NUMBER_OF_PORTS = 2;
const string namePortA("bus-A");
const string namePortB("bus-B");
/**
* helper for dummy render engine:
* Simulate the result of a build process,
* without actually running the builder.
* Produces some mock pipes, model ports etc.
*/
struct SimulatedBuilderContext
{
ModelPortRegistry registry_;
ModelPortRegistry* existingRegistry_;
std::vector<ModelPort> modelPorts_;
std::vector<DataSink> dataSinks_;
/** setup */
SimulatedBuilderContext()
: registry_()
, existingRegistry_(ModelPortRegistry::setActiveInstance (registry_))
{
performMockBuild();
}
/** tear-down */
~SimulatedBuilderContext()
{
if (existingRegistry_)
ModelPortRegistry::setActiveInstance (*existingRegistry_);
else
ModelPortRegistry::shutdown();
}
void
performMockBuild()
{
PID pipeA = getPipe (namePortA);
PID pipeB = getPipe (namePortB);
TID someTimeline = getTimeline ("dummy_Timeline");
// start out with defining some new model ports......
registry_.definePort (pipeA, someTimeline);
registry_.definePort (pipeB, someTimeline);
registry_.commit();
// now "bus-A" and "bus-B" are known as model ports
modelPorts_.push_back (ModelPort(pipeA));
modelPorts_.push_back (ModelPort(pipeB));
// prepare corresponding placeholder DataSink (a fake active output connection)
dataSinks_.emplace_back().activate(std::make_shared<UnimplementedConnection>());
dataSinks_.emplace_back().activate(std::make_shared<UnimplementedConnection>());
}
ModelPorts
getAllModelPorts()
{
return lib::iter_source::eachEntry (modelPorts_.begin(), modelPorts_.end());
}
DummyOutputLink
getModelPort (uint index)
{
REQUIRE (index < modelPorts_.size());
return {modelPorts_[index]
,dataSinks_[index]
};
}
};
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
/****************************************************************//**
* Framework for dummy playback and rendering.
* A DummyPlayConnection provides a coherent set of placeholders,
* allowing to start a data producing process while leaving out
* various parts of the real engine implementation. The specific
* mode of operation, suitable for various test scenarios, may be
* fine tuned by the strategy object defined as template parameter.
*
* @todo not-yet-implemented as of 2016 -- but the design can be considered valid.
*/
template<class DEF>
class DummyPlayConnection
: util::NonCopyable
{
SimulatedBuilderContext mockBuilder_;
public:
ModelPorts
getAllModelPorts()
{
return mockBuilder_.getAllModelPorts();
}
DummyOutputLink
getModelPort (uint index)
{
return mockBuilder_.getModelPort (index);
}
POutputManager
provide_testOutputSlot()
{
UNIMPLEMENTED ("provide a suitable output sink simulation");
}
/* === Test Support API === */
bool
isWired()
{
UNIMPLEMENTED ("is this dummy in activated state?");
}
Duration
getPlannedTestDuration()
{
UNIMPLEMENTED ("manage the a planned test duration");
}
/** test helper: blocking wait during an output test.
* The waiting time should be in accordance with the
* \link #getPlannedTestduration planned value \endlink,
*/
void
waitUntilDue()
{
UNIMPLEMENTED ("do a blocking wait, while an output test is performed in other threads");
}
bool
gotCorrectOutput()
{
UNIMPLEMENTED ("verify proper operation by inspecting the provided test dummy components");
}
};
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
}}} // namespace steam::play::test
#endif /*STEAM_PLAY_DUMMY_BUILDER_CONTEXT_H*/

View file

@ -28,8 +28,6 @@
#include "lib/test/run.hpp"
#include "steam/play/dummy-play-connection.hpp"
//#include "steam/engine/buffhandle.hpp"
//#include "steam/engine/testframe.hpp"
#include "include/play-facade.hpp"
#include "lib/time/control.hpp"

View file

@ -78,19 +78,9 @@
#define STEAM_PLAY_DUMMY_PLAY_CONNECTION_H
//#include "include/dummy-player-facade.h"
//#include "include/display-facade.h"
//#include "common/instancehandle.hpp"
//#include "lib/singleton-ref.hpp"
#include "steam/play/output-slot-connection.hpp"
#include "steam/play/dummy-builder-context.hpp"
#include "steam/play/output-manager.hpp"
#include "steam/mobject/model-port.hpp"
#include "lib/time/timequant.hpp"
//#include "lib/scoped-collection.hpp"
#include "steam/mobject/builder/model-port-registry.hpp"
#include "steam/asset/timeline.hpp"
#include "steam/asset/pipe.hpp"
#include "common/query.hpp"
#include "lib/iter-source.hpp"
#include "lib/nocopy.hpp"
@ -104,18 +94,12 @@ namespace play {
namespace test {
// using std::string;
// using lumiera::Subsys;
// using lumiera::Display;
// using lumiera::DummyPlayer;
using lib::time::Duration;
using ModelPorts = lib::IterSource<mobject::ModelPort>::iterator;
using DummyOutputLink = std::pair<mobject::ModelPort, play::DataSink>;
/**
/*
* @todo 5/2023 quick-n-dirty placeholder to be able to fabricate fake DataSink handles (`Handle<Connection>`)
*/
* /
class UnimplementedConnection
: public play::OutputSlot::Connection
{
@ -130,7 +114,7 @@ namespace test {
~UnimplementedConnection();
UnimplementedConnection() = default;
};
*/
@ -139,7 +123,7 @@ namespace test {
};
/*
namespace { // simulated builder environment
using asset::Pipe;
@ -176,12 +160,12 @@ namespace test {
const string namePortA("bus-A");
const string namePortB("bus-B");
/**
/ **
* helper for dummy render engine:
* Simulate the result of a build process,
* without actually running the builder.
* Produces some mock pipes, model ports etc.
*/
* /
struct SimulatedBuilderContext
{
ModelPortRegistry registry_;
@ -190,7 +174,6 @@ namespace test {
std::vector<ModelPort> modelPorts_;
std::vector<DataSink> dataSinks_;
/** setup */
SimulatedBuilderContext()
: registry_()
, existingRegistry_(ModelPortRegistry::setActiveInstance (registry_))
@ -198,7 +181,6 @@ namespace test {
performMockBuild();
}
/** tear-down */
~SimulatedBuilderContext()
{
if (existingRegistry_)
@ -246,7 +228,7 @@ namespace test {
}
};
}
*/
/****************************************************************//**
* Framework for dummy playback and rendering.

View file

@ -73527,12 +73527,73 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685676373201" ID="ID_290916668" MODIFIED="1685676382028" TEXT="soll von TreeExplorer erben">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1685676390323" ID="ID_1302265239" MODIFIED="1685676394025" TEXT="geht das?">
<node COLOR="#435e98" CREATED="1685676390323" ID="ID_1302265239" MODIFIED="1685743982852" TEXT="geht das?">
<icon BUILTIN="help"/>
<node CREATED="1685676395607" ID="ID_840485660" MODIFIED="1685676404942" TEXT="erinnere mich an den EventLog-Filter"/>
<node CREATED="1685676405602" ID="ID_1339853853" MODIFIED="1685676416909" TEXT="das l&#xe4;uft auf einen decltype-Trick hinaus">
<node CREATED="1685676395607" ID="ID_840485660" MODIFIED="1685743976291" TEXT="erinnere mich an den EventLog-Filter">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
siehe <font face="Monospaced" size="2" color="#4c409c">iter-chain-search.hpp</font>
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1685676405602" ID="ID_1339853853" MODIFIED="1685743938073" TEXT="das l&#xe4;uft auf einen decltype-Trick hinaus">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...man packt den eigentlichen TreeExplorer-Builder-Aufruf in eine Hilfsunktion und greift den decltype vom R&#252;ckgabewert ab; von diesem Typ kann man dann erben (und verwendet die erw&#228;hnte Hilfsfunktion im Konstruktor, um den Parent-Typ zu initialisieren)i
</p>
</body>
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685744020440" ID="ID_189611589" MODIFIED="1685744043568" TEXT="das eigene Builder-API soll aber auf TreeExplorer aufbauen k&#xf6;nnen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685743991581" ID="ID_865546443" MODIFIED="1685744014683" TEXT="Type-Rebinding einrichten">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1685744055315" ID="ID_115548313" MODIFIED="1685754522949" TEXT="f&#xfc;r Basis-Konstruktion">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1685754130097" ID="ID_286369938" MODIFIED="1685754149459" TEXT="schwierig">
<icon BUILTIN="smily_bad"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1685754164717" ID="ID_1228626909" MODIFIED="1685754376312" TEXT="rekursive Struktur &#x27fa; type inference">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
In diesem Zusammenhang ist <i>type inference </i>praktisch unvermeidbar, denn die komplexen Typen vom TreeExplorer k&#246;nnen anders nicht explizit gemacht werden &#8212; aber das Problem ist, die Typen sind rekursiv, denn den neuen, erweiterten Builder-Typ mu&#223; ich schon kennen, um ihn in dem TreeEplorer-Builder als Core zu &#252;bergeben, aber erst <i>durch diesen Aufruf</i>&#160;wird dieser neue erweiterte Builder &#252;berhaupt definiert
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1685754386041" ID="ID_1627047385" MODIFIED="1685754500941" TEXT="Trick: NumIter ist assignable">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...also zun&#228;chst einen deaktivierten NumIter als Basisklasse, und an diesen sp&#228;ter einen aktivierten NumIter zuweisen...&#160; <b><font color="#ec1313">ziemlich h&#228;&#223;lich</font></b>, aber damit bekomme ich es &#252;berhaupt erst mal durch den Compiler...
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685744060666" ID="ID_676050987" MODIFIED="1685744074319" TEXT="f&#xfc;r Anwendung von TreeExplorer-Builder-Funktionen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>