From 81ee9a2e67f095beab30b50c5456d0841634ddf8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 3 Jun 2023 03:39:33 +0200 Subject: [PATCH] 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) --- src/lib/iter-tree-explorer.hpp | 4 +- src/steam/engine/dispatcher.hpp | 27 +- tests/core/steam/engine/timings-test.cpp | 34 +-- .../core/steam/play/dummy-builder-context.hpp | 265 ++++++++++++++++++ .../steam/play/dummy-play-connection-test.cpp | 2 - .../steam/play/dummy-play-connection.cpp | 0 .../steam/play/dummy-play-connection.hpp | 34 +-- wiki/thinkPad.ichthyo.mm | 67 ++++- 8 files changed, 369 insertions(+), 64 deletions(-) create mode 100644 tests/core/steam/play/dummy-builder-context.hpp rename {src => tests/core}/steam/play/dummy-play-connection.cpp (100%) rename {src => tests/core}/steam/play/dummy-play-connection.hpp (93%) diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index b78ce532c..18e7efc5c 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -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 diff --git a/src/steam/engine/dispatcher.hpp b/src/steam/engine/dispatcher.hpp index 1db71684f..b2083afba 100644 --- a/src/steam/engine/dispatcher.hpp +++ b/src/steam/engine/dispatcher.hpp @@ -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 @@ -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; + struct PipelineBuilder - : lib::SingleValIter ////////////////////////////////////////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(*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}; } diff --git a/tests/core/steam/engine/timings-test.cpp b/tests/core/steam/engine/timings-test.cpp index fa65f565f..549ccbeaf 100644 --- a/tests/core/steam/engine/timings-test.cpp +++ b/tests/core/steam/engine/timings-test.cpp @@ -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 -#include +//#include +//#include 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; -// -// DummyPlaybackSetup dummySetup; -// ModelPorts mockModelPorts = dummySetup.provide_testModelPorts(); -// return *mockModelPorts; // using just the first dummy port -// } FSecs diff --git a/tests/core/steam/play/dummy-builder-context.hpp b/tests/core/steam/play/dummy-builder-context.hpp new file mode 100644 index 000000000..95d7c97d9 --- /dev/null +++ b/tests/core/steam/play/dummy-builder-context.hpp @@ -0,0 +1,265 @@ +/* + DUMMY-BUILDER-CONTEXT.hpp - fake simulated builder result for player tests + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 + + +namespace steam{ +namespace play { +namespace test { + + + /** + * @todo 5/2023 quick-n-dirty placeholder to be able to fabricate fake DataSink handles (`Handle`) + */ + 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; + using TID = asset::ID; + + using ModelPorts = lib::IterSource::iterator; + using DummyOutputLink = std::pair; + + + inline PID + getPipe (string id) + { + return Pipe::query ("id("+id+")"); + } + + inline TID + getTimeline (string id) + { + return asset::Struct::retrieve (Query ("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 modelPorts_; + std::vector 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()); + dataSinks_.emplace_back().activate(std::make_shared()); + } + + + 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 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*/ diff --git a/tests/core/steam/play/dummy-play-connection-test.cpp b/tests/core/steam/play/dummy-play-connection-test.cpp index db21f7661..2d38150c4 100644 --- a/tests/core/steam/play/dummy-play-connection-test.cpp +++ b/tests/core/steam/play/dummy-play-connection-test.cpp @@ -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" diff --git a/src/steam/play/dummy-play-connection.cpp b/tests/core/steam/play/dummy-play-connection.cpp similarity index 100% rename from src/steam/play/dummy-play-connection.cpp rename to tests/core/steam/play/dummy-play-connection.cpp diff --git a/src/steam/play/dummy-play-connection.hpp b/tests/core/steam/play/dummy-play-connection.hpp similarity index 93% rename from src/steam/play/dummy-play-connection.hpp rename to tests/core/steam/play/dummy-play-connection.hpp index 36f557197..7d0bbcae0 100644 --- a/src/steam/play/dummy-play-connection.hpp +++ b/tests/core/steam/play/dummy-play-connection.hpp @@ -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::iterator; - using DummyOutputLink = std::pair; - - /** + /* * @todo 5/2023 quick-n-dirty placeholder to be able to fabricate fake DataSink handles (`Handle`) - */ + * / 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 modelPorts_; std::vector 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. diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 993d97633..9d29b5c18 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -73527,12 +73527,73 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + + + + + +

+ siehe iter-chain-search.hpp +

+ +
+ + + + + + +

+ ...man packt den eigentlichen TreeExplorer-Builder-Aufruf in eine Hilfsunktion und greift den decltype vom Rückgabewert ab; von diesem Typ kann man dann erben (und verwendet die erwähnte Hilfsfunktion im Konstruktor, um den Parent-Typ zu initialisieren)i +

+ +
+
+
+ + + + + + + + + + + + + + + + +

+ In diesem Zusammenhang ist type inference praktisch unvermeidbar, denn die komplexen Typen vom TreeExplorer können anders nicht explizit gemacht werden — aber das Problem ist, die Typen sind rekursiv, denn den neuen, erweiterten Builder-Typ muß ich schon kennen, um ihn in dem TreeEplorer-Builder als Core zu übergeben, aber erst durch diesen Aufruf wird dieser neue erweiterte Builder überhaupt definiert +

+ +
+
+ + + + + + +

+ ...also zunächst einen deaktivierten NumIter als Basisklasse, und an diesen später einen aktivierten NumIter zuweisen...  ziemlich häßlich, aber damit bekomme ich es überhaupt erst mal durch den Compiler... +

+ +
+ +
+
+ + +