2009-09-04 17:43:53 +02:00
|
|
|
/*
|
2024-07-04 23:54:13 +02:00
|
|
|
NODE-BUILDER.hpp - Setup of render nodes connectivity
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2024-07-05 23:36:41 +02:00
|
|
|
2024, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
2010-12-17 23:28:49 +01:00
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
*/
|
|
|
|
|
|
2024-07-04 23:54:13 +02:00
|
|
|
/** @file node-builder.hpp
|
2024-07-05 23:36:41 +02:00
|
|
|
** Specialised shorthand notation for building the Render Node network.
|
|
|
|
|
** During the Builder run, the render nodes network will be constructed by gradually
|
|
|
|
|
** refining the connectivity structure derived from interpreting the »high-level model«
|
|
|
|
|
** from the current Session. At some point, it is essentially clear what data streams
|
|
|
|
|
** must be produced and what media processing functionality from external libraries
|
|
|
|
|
** will be utilised to achieve this goal. This is when the fluent builder notation
|
|
|
|
|
** defined in this header comes into play, allowing to package the fine grained and
|
|
|
|
|
** in part quite confusing details of parameter wiring and invocation preparation into
|
|
|
|
|
** some goal oriented building blocks, that can be combined and directed with greater
|
|
|
|
|
** clarity by the control structure to govern the build process.
|
2009-09-04 17:43:53 +02:00
|
|
|
**
|
2024-10-23 16:27:09 +02:00
|
|
|
**
|
2024-07-05 23:36:41 +02:00
|
|
|
** # Levels of connectivity building
|
|
|
|
|
**
|
|
|
|
|
** The actual node connectivity is established by a process of gradual refinement,
|
|
|
|
|
** operating over several levels of abstraction. Each of these levels uses its associated
|
|
|
|
|
** builder and descriptor records to collect information, which is then emitted by a
|
|
|
|
|
** _terminal invocation_ to produce the result; the higher levels thereby rely on the
|
|
|
|
|
** lower levels to fill in and elaborate the details.
|
2024-10-23 16:27:09 +02:00
|
|
|
** - *Level-1* is the preparation of an actual frame processing operation; the Level-1-builder
|
2024-07-05 23:36:41 +02:00
|
|
|
** is in fact the implementation class sitting behind a Render Node's _Port._ It is called
|
|
|
|
|
** a _Turnout_ and contains a preconfigured »blue print« for the data structure layout
|
|
|
|
|
** used for the invocation; its purpose is to generate the actual data structure on the
|
|
|
|
|
** stack, holding all the necessary buffers and parameters ready for invoking the external
|
|
|
|
|
** library functions. Since the actual data processing is achieved by a _pull processing,_
|
|
|
|
|
** originating at the top level exit nodes and propagating down towards the data sources,
|
|
|
|
|
** all the data feeds at all levels gradually link together, forming a _TurnoutSystem._
|
2024-10-23 16:27:09 +02:00
|
|
|
** - *Level-2* generates the actual network of Render Nodes, which in turn will have the
|
|
|
|
|
** Turnout instances for Level-1 embedded into their internal ports. Conceptually, a
|
2024-07-05 23:36:41 +02:00
|
|
|
** _Port_ is where data production can be requested, and the processing will then
|
|
|
|
|
** retrieve its prerequisite data from the ports of the _Leads,_ which are the
|
|
|
|
|
** prerequisite nodes situated one level below or one step closer to the source.
|
2024-10-23 16:27:09 +02:00
|
|
|
** - *Level-3* establishes the processing steps and data retrieval links between them;
|
2024-07-05 23:36:41 +02:00
|
|
|
** at this level, thus the outline of possible processing pathways is established.
|
|
|
|
|
** After spelling out the desired connectivity at a high level, the so called »Level-3 build
|
|
|
|
|
** walk« is triggered by invoking the [terminal builder operation](\ref ProcBuilder::build()
|
|
|
|
|
** on the [processing builder](\ref ProcBuilder) corresponding to the topmost node. This
|
|
|
|
|
** build walk will traverse the connectivity graph depth-first, and then start invoking the
|
|
|
|
|
** Level-2 builder operations bottom-up to generate and wire up the corresponding Render Nodes.
|
|
|
|
|
**
|
2024-07-08 01:19:06 +02:00
|
|
|
** ## Using custom allocators
|
|
|
|
|
**
|
|
|
|
|
** Since the low-level-Model is a massive data structure comprising thousands of nodes, each with
|
|
|
|
|
** specialised parametrisation for some media handling library, and a lot of cross-linking pointers,
|
|
|
|
|
** it is important to care for efficient usage of memory with good locality. Furthermore, the higher
|
2024-10-23 16:27:09 +02:00
|
|
|
** levels of the build process will generate additional temporary data structures, refined gradually
|
|
|
|
|
** until the actual render node network can be emitted. Each builder level can thus be outfitted
|
|
|
|
|
** with a custom allocator — typically an instance of lib::AllocationCluster. Notably the higher
|
|
|
|
|
** levels can be attached to a separate AllocationCluster instance, which will be discarded once
|
|
|
|
|
** the build process is complete, while Level-2 (and below) uses the allocator for the actual
|
|
|
|
|
** target data structure, which has to be retained while the render graph is used; more
|
|
|
|
|
** specifically until a complete segment of the timeline is superseded and has been re-built.
|
2024-07-08 01:19:06 +02:00
|
|
|
** @remark syntactically, the custom allocator specification is given after opening a top-level
|
|
|
|
|
** builder, by means of the builder function `.withAllocator<ALO> (args...)`
|
|
|
|
|
**
|
2024-10-23 16:27:09 +02:00
|
|
|
** @todo WIP-WIP-WIP 10/2024 Node-Invocation is reworked from ground up -- some parts can not be
|
2024-07-05 23:36:41 +02:00
|
|
|
** spelled out completely yet, since we have to build this tightly interlocked system of
|
|
|
|
|
** code moving bottom up, and then filling in further details later working top-down.
|
2024-06-21 16:14:24 +02:00
|
|
|
**
|
2018-11-15 23:59:23 +01:00
|
|
|
** @see steam::engine::NodeFactory
|
2009-09-04 17:43:53 +02:00
|
|
|
** @see nodewiring.hpp
|
|
|
|
|
** @see node-basic-test.cpp
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2024-07-04 23:54:13 +02:00
|
|
|
#ifndef ENGINE_NODE_BUILDER_H
|
|
|
|
|
#define ENGINE_NODE_BUILDER_H
|
2009-09-04 17:43:53 +02:00
|
|
|
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
#include "lib/nocopy.hpp"
|
2024-07-23 03:52:44 +02:00
|
|
|
#include "steam/engine/weaving-pattern-builder.hpp"
|
2024-06-29 04:23:55 +02:00
|
|
|
#include "steam/engine/proc-node.hpp"
|
2024-07-08 19:24:03 +02:00
|
|
|
#include "steam/engine/turnout.hpp"
|
2024-07-06 21:31:03 +02:00
|
|
|
#include "lib/several-builder.hpp"
|
2024-10-25 18:13:55 +02:00
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
#include "lib/iter-index.hpp"
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
#include "lib/test/test-helper.hpp"/////////////////////TODO TOD-oh
|
2009-09-04 17:43:53 +02:00
|
|
|
|
2024-07-02 23:33:23 +02:00
|
|
|
#include <utility>
|
2024-07-06 21:31:03 +02:00
|
|
|
#include <vector>
|
2009-09-04 17:43:53 +02:00
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
namespace steam {
|
2009-09-04 17:43:53 +02:00
|
|
|
namespace engine {
|
2024-10-25 18:13:55 +02:00
|
|
|
namespace err = lumiera::error;
|
2009-09-04 17:43:53 +02:00
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
using util::_Fmt;
|
2024-07-07 23:56:10 +02:00
|
|
|
using std::forward;
|
2024-10-25 18:13:55 +02:00
|
|
|
using std::move;
|
2024-07-02 23:33:23 +02:00
|
|
|
|
2024-07-08 01:19:06 +02:00
|
|
|
|
2024-07-23 03:52:44 +02:00
|
|
|
namespace { // default policy configuration to use heap allocator
|
2024-07-07 23:56:10 +02:00
|
|
|
|
|
|
|
|
struct UseHeapAlloc
|
|
|
|
|
{
|
|
|
|
|
template<class I, class E=I>
|
2024-07-22 22:37:36 +02:00
|
|
|
using Policy = lib::allo::HeapOwn<I,E>;
|
2024-07-07 23:56:10 +02:00
|
|
|
};
|
2024-07-08 01:19:06 +02:00
|
|
|
//
|
2024-07-23 03:52:44 +02:00
|
|
|
}//(End) policy
|
2024-07-08 00:44:46 +02:00
|
|
|
|
2009-09-04 17:43:53 +02:00
|
|
|
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
|
2024-07-08 01:19:06 +02:00
|
|
|
/**
|
2024-07-23 03:52:44 +02:00
|
|
|
* A builder to collect working data.
|
2024-07-08 01:19:06 +02:00
|
|
|
* Implemented through a suitable configuration of lib::SeveralBuilder,
|
2024-07-23 03:52:44 +02:00
|
|
|
* with a policy configuration parameter to define the allocator to use.
|
2024-07-08 01:19:06 +02:00
|
|
|
*/
|
|
|
|
|
template<class POL, class I, class E=I>
|
2024-07-22 22:37:36 +02:00
|
|
|
using DataBuilder = lib::SeveralBuilder<I,E, POL::template Policy>;
|
2024-07-08 01:19:06 +02:00
|
|
|
|
2024-10-23 16:27:09 +02:00
|
|
|
|
|
|
|
|
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
template<class POL, class DAT>
|
|
|
|
|
class NodeBuilder;
|
2024-07-08 01:19:06 +02:00
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT>
|
2024-10-11 02:45:51 +02:00
|
|
|
class PortBuilderRoot;
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-06 21:31:03 +02:00
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT = PatternDataAnchor>
|
2024-07-06 21:31:03 +02:00
|
|
|
class NodeBuilder
|
2024-06-24 23:49:55 +02:00
|
|
|
: util::MoveOnly
|
2024-07-06 21:31:03 +02:00
|
|
|
{
|
2024-07-07 23:56:10 +02:00
|
|
|
using PortData = DataBuilder<POL, Port>;
|
2024-07-08 01:19:06 +02:00
|
|
|
using LeadRefs = DataBuilder<POL, ProcNodeRef>;
|
2024-07-07 23:56:10 +02:00
|
|
|
|
2024-07-08 19:24:03 +02:00
|
|
|
protected:
|
2024-07-08 01:19:06 +02:00
|
|
|
LeadRefs leads_;
|
2024-10-21 03:40:19 +02:00
|
|
|
DAT patternData_;
|
|
|
|
|
|
2024-07-06 21:31:03 +02:00
|
|
|
public:
|
2024-10-22 05:59:00 +02:00
|
|
|
template<typename...INIT>
|
2024-07-07 23:56:10 +02:00
|
|
|
NodeBuilder (INIT&& ...alloInit)
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
: leads_{forward<INIT> (alloInit)...}
|
2024-10-22 05:59:00 +02:00
|
|
|
{ }
|
2024-07-06 21:31:03 +02:00
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class BUILD, uint siz, class D0>
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
NodeBuilder (NodeBuilder<POL,D0>&& pred, SizMark<siz>, BUILD&& entryBuilder)
|
|
|
|
|
: leads_{move (pred.leads_)}
|
2024-10-21 03:40:19 +02:00
|
|
|
, patternData_{move (pred.patternData_), forward<BUILD> (entryBuilder)}
|
|
|
|
|
{ }
|
|
|
|
|
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
template<class P, class D0>
|
|
|
|
|
friend class NodeBuilder;
|
|
|
|
|
|
2024-07-08 01:19:06 +02:00
|
|
|
|
2024-07-06 21:31:03 +02:00
|
|
|
NodeBuilder
|
|
|
|
|
addLead (ProcNode const& lead)
|
|
|
|
|
{
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
leads_.append (lead);
|
2024-07-06 21:31:03 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-07-08 19:24:03 +02:00
|
|
|
/** recursively enter detailed setup of a single processing port */
|
2024-10-21 03:40:19 +02:00
|
|
|
PortBuilderRoot<POL,DAT> preparePort();
|
2024-07-06 21:31:03 +02:00
|
|
|
|
2024-07-08 00:44:46 +02:00
|
|
|
|
2024-07-08 01:19:06 +02:00
|
|
|
/**
|
|
|
|
|
* cross-builder function to specify usage of a dedicated *node allocator*
|
|
|
|
|
* @tparam ALO (optional) spec for the allocator to use
|
|
|
|
|
* @tparam INIT (optional) initialisation arguments for the allocator
|
|
|
|
|
* @remarks this is a front-end to the extension point for allocator specification
|
|
|
|
|
* exposed through lib::SeveralBuilder::withAllocator(). The actual meaning
|
|
|
|
|
* of the given parameters and the choice of the actual allocator happens
|
|
|
|
|
* through resolution of partial template specialisations of the extension
|
|
|
|
|
* point lib::allo::SetupSeveral. Some notable examples
|
|
|
|
|
* - withAllocator<ALO>() attaches to a _monostate_ allocator type.
|
|
|
|
|
* - `withAllocator<ALO> (ALO<X> allo)` uses a C++ standard allocator
|
|
|
|
|
* instance `allo`, dedicated to produce objects of type `X`
|
|
|
|
|
* - `withAllocator (AllocationCluster&)` attaches to a specific
|
|
|
|
|
* AllocationCluster; this is the most relevant usage pattern
|
|
|
|
|
*/
|
2024-07-08 00:44:46 +02:00
|
|
|
template<template<typename> class ALO =std::void_t, typename...INIT>
|
|
|
|
|
auto
|
|
|
|
|
withAllocator (INIT&& ...alloInit)
|
|
|
|
|
{
|
2024-07-23 03:52:44 +02:00
|
|
|
using AllocatorPolicy = lib::allo::SetupSeveral<ALO,INIT...>;
|
2024-07-08 01:19:06 +02:00
|
|
|
return NodeBuilder<AllocatorPolicy>{forward<INIT>(alloInit)...};
|
2024-07-08 00:44:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-07-08 01:19:06 +02:00
|
|
|
/************************************************************//**
|
|
|
|
|
* Terminal: complete the ProcNode Connectivity defined thus far.
|
2024-07-06 21:31:03 +02:00
|
|
|
*/
|
|
|
|
|
Connectivity
|
|
|
|
|
build()
|
|
|
|
|
{
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
PortData ports;
|
|
|
|
|
patternData_.collectEntries(ports);
|
|
|
|
|
return Connectivity{ports.build()
|
2024-07-08 01:19:06 +02:00
|
|
|
,leads_.build()
|
|
|
|
|
,NodeID{}}; //////////////////////////////////////OOO what's the purpose of the NodeID??
|
2024-07-06 21:31:03 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
/** Deduction Guide: help the compiler with deducing follow-up NodeBuilder parameters */
|
|
|
|
|
template<class POL, class D0, uint siz, class BUILD>
|
|
|
|
|
NodeBuilder (NodeBuilder<POL,D0>&&, SizMark<siz>, BUILD&&) -> NodeBuilder<POL, PatternData<D0,BUILD,siz>>;
|
|
|
|
|
|
2024-10-11 02:45:51 +02:00
|
|
|
|
|
|
|
|
|
2024-10-22 05:59:00 +02:00
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT>
|
2024-10-11 02:45:51 +02:00
|
|
|
class PortBuilderRoot
|
2024-10-21 03:40:19 +02:00
|
|
|
: protected NodeBuilder<POL,DAT>
|
2024-06-24 23:49:55 +02:00
|
|
|
{
|
2024-10-13 03:49:01 +02:00
|
|
|
public:
|
2024-10-21 03:40:19 +02:00
|
|
|
NodeBuilder<POL,DAT>
|
2024-10-11 02:45:51 +02:00
|
|
|
completePort()
|
2024-07-02 23:33:23 +02:00
|
|
|
{
|
2024-10-11 02:45:51 +02:00
|
|
|
static_assert(not sizeof(POL),
|
|
|
|
|
"can not build a port without specifying a processing function");
|
2024-07-02 23:33:23 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 02:45:51 +02:00
|
|
|
/** setup standard wiring to adapt the given processing function.
|
|
|
|
|
* @return a PortBuilder specialised to wrap the given \a FUN */
|
|
|
|
|
template<typename FUN>
|
2024-10-22 05:59:00 +02:00
|
|
|
auto invoke (FUN fun);
|
2024-10-11 02:45:51 +02:00
|
|
|
|
|
|
|
|
/** specify an `InvocationAdapter` to use explicitly. */
|
2024-10-10 21:35:02 +02:00
|
|
|
template<class ADA, typename...ARGS>
|
2024-10-11 02:45:51 +02:00
|
|
|
auto adaptInvocation(ARGS&& ...args);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PortBuilderRoot(NodeBuilder<POL>&& anchor)
|
|
|
|
|
: NodeBuilder<POL>{move(anchor)}
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
friend PortBuilderRoot NodeBuilder<POL>::preparePort();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @remark while _logically_ this builder-function _descends_ into the
|
|
|
|
|
* definition of a port, for the implementation we _wrap_ the existing
|
|
|
|
|
* NodeBuilder and layer a PortBuilder subclass „on top“ — thereby shadowing
|
|
|
|
|
* the enclosed original builder temporarily; the terminal builder operation
|
|
|
|
|
* PortBuilder::completePort() will unwrap and return the original NodeBuilder.
|
|
|
|
|
*/
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT>
|
|
|
|
|
inline PortBuilderRoot<POL, DAT>
|
|
|
|
|
NodeBuilder<POL,DAT>::preparePort ()
|
2024-10-11 02:45:51 +02:00
|
|
|
{
|
2024-10-21 03:40:19 +02:00
|
|
|
return PortBuilderRoot<POL,DAT>{move(*this)};
|
2024-10-11 02:45:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT, class WAB>
|
2024-10-11 02:45:51 +02:00
|
|
|
class PortBuilder
|
2024-10-21 03:40:19 +02:00
|
|
|
: public PortBuilderRoot<POL,DAT>
|
2024-10-11 02:45:51 +02:00
|
|
|
{
|
2024-10-21 03:40:19 +02:00
|
|
|
using _Par = PortBuilderRoot<POL,DAT>;
|
2024-10-11 02:45:51 +02:00
|
|
|
|
|
|
|
|
WAB weavingBuilder_;
|
2024-10-24 23:51:45 +02:00
|
|
|
uint defaultPort_;
|
2024-10-11 02:45:51 +02:00
|
|
|
|
|
|
|
|
public:
|
2024-07-02 23:33:23 +02:00
|
|
|
|
|
|
|
|
template<class ILA, typename...ARGS>
|
2024-07-05 13:41:01 +02:00
|
|
|
PortBuilder
|
2024-07-02 23:33:23 +02:00
|
|
|
createBuffers (ARGS&& ...args)
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("define builder for all buffers to use");
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-22 05:59:00 +02:00
|
|
|
/** define the output slot to use as result
|
|
|
|
|
* @remark default is to use the first one */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
asResultSlot (uint r)
|
|
|
|
|
{
|
2024-10-22 05:59:00 +02:00
|
|
|
weavingBuilder_.selectResultSlot(r);
|
2024-10-10 21:35:02 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
/** connect the next input slot to existing lead-node given by index */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
connectLead (uint idx)
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
return connectLeadPort (idx, this->defaultPort_);
|
2024-10-10 21:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
/** connect the next input slot to either existing or new lead-node" */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
conectLead (ProcNode& leadNode)
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
return connectLeadPort (leadNode, this->defaultPort_);
|
2024-10-10 21:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
/** connect next input to lead-node, using a specific port-number */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
connectLeadPort (uint idx, uint port)
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
if (idx >= _Par::leads_.size())
|
|
|
|
|
throw err::Logic{_Fmt{"Builder refers to lead-node #%d, yet only %d are currently defined."}
|
|
|
|
|
% idx % _Par::leads_.size()
|
|
|
|
|
,LERR_(INDEX_BOUNDS)
|
|
|
|
|
};
|
|
|
|
|
weavingBuilder_.attachToLeadPort (_Par::leads_[idx], port);
|
2024-10-10 21:35:02 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
/** connect next input to existing or new lead-node, with given port-number */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
connectLeadPort (ProcNode& leadNode, uint port)
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
uint knownEntry{0};
|
|
|
|
|
for (auto& lead : lib::IterIndex{_Par::leads_})
|
|
|
|
|
if (util::isSameObject (leadNode, lead))
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
++knownEntry;
|
|
|
|
|
if (knownEntry == _Par::leads_.size())
|
|
|
|
|
_Par::addLead (leadNode);
|
|
|
|
|
|
|
|
|
|
ENSURE (knownEntry < _Par::leads_.size());
|
|
|
|
|
weavingBuilder_.attachToLeadPort (knownEntry, port);
|
2024-10-10 21:35:02 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-25 18:13:55 +02:00
|
|
|
/** use given port-index as default for all following connections */
|
2024-10-10 21:35:02 +02:00
|
|
|
PortBuilder
|
|
|
|
|
useLeadPort (uint defaultPort)
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
this->defaultPort_ = defaultPort;
|
2024-10-10 21:35:02 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 03:33:05 +02:00
|
|
|
/*************************************************************//**
|
2024-07-05 13:41:01 +02:00
|
|
|
* Terminal: complete the Port wiring and return to the node level.
|
|
|
|
|
*/
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
auto
|
2024-07-05 13:41:01 +02:00
|
|
|
completePort()
|
|
|
|
|
{
|
2024-10-25 18:13:55 +02:00
|
|
|
weavingBuilder_.connectRemainingInputs (_Par::leads_, this->defaultPort_);
|
2024-10-14 04:07:47 +02:00
|
|
|
weavingBuilder_.fillRemainingBufferTypes();
|
2024-10-22 05:59:00 +02:00
|
|
|
return NodeBuilder{static_cast<NodeBuilder<POL,DAT>&&> (*this) // slice away PortBulder subclass data
|
Invocation: switch `WeavingBuilder` to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)
By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.
However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
|
|
|
,weavingBuilder_.sizMark
|
|
|
|
|
,weavingBuilder_.build()};
|
2024-10-22 05:59:00 +02:00
|
|
|
} // chain to builder with extended patternData
|
2024-07-08 19:24:03 +02:00
|
|
|
|
|
|
|
|
private:
|
2024-10-13 03:49:01 +02:00
|
|
|
template<typename FUN>
|
|
|
|
|
PortBuilder(_Par&& base, FUN&& fun)
|
2024-10-11 02:45:51 +02:00
|
|
|
: _Par{move(base)}
|
2024-10-23 16:27:09 +02:00
|
|
|
, weavingBuilder_{forward<FUN> (fun), _Par::leads_.policyConnect()}
|
2024-10-25 18:13:55 +02:00
|
|
|
, defaultPort_{_Par::patternData_.size()}
|
2024-07-08 19:24:03 +02:00
|
|
|
{ }
|
|
|
|
|
|
2024-10-21 03:40:19 +02:00
|
|
|
friend class PortBuilderRoot<POL,DAT>;
|
2024-07-05 13:41:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2024-10-24 23:51:45 +02:00
|
|
|
/**
|
|
|
|
|
* @param fun invocation of the actual _data processing operation._
|
|
|
|
|
* @remarks
|
|
|
|
|
* - a _»weaving pattern«_ is applied for the actual implementation, which amounts
|
|
|
|
|
* to a specific style how to route data input and output and how to actually integrate
|
|
|
|
|
* with the underlying media handling library, which exposes the processing functionality.
|
|
|
|
|
* - the standard case of this connectivity is to associate input and output connections
|
|
|
|
|
* directly with the »parameter slots« of the processing function; a function suitable
|
|
|
|
|
* for this pattern takes two arguments (input, output) — each of which is a std::array
|
|
|
|
|
* of buffer pointers, corresponding to the »parameter slots«
|
|
|
|
|
* - what is bound as \a FUN here thus typically is either an adapter function provided by
|
|
|
|
|
* the media-library plug-in, or it is a lambda directly invoking implementation functions
|
|
|
|
|
* of the underlying library, using a buffer type (size) suitable for this library and for
|
|
|
|
|
* the actual media frame data to be processed.
|
|
|
|
|
* - the `fun` is deliberately _taken by-value_ and then moved into a »prototype copy« within
|
|
|
|
|
* the generated `Turnout`, from which an actual copy is drawn anew for each node invocation.
|
|
|
|
|
* - notably this implies that the implementation code of a lambda will be _inlined_ into the
|
|
|
|
|
* actual invocation call, while possibly _creating a copy_ of value-captured closure data;
|
|
|
|
|
* this arrangement aims at exposing the actual invocation for the optimiser.
|
|
|
|
|
*/
|
2024-10-21 03:40:19 +02:00
|
|
|
template<class POL, class DAT>
|
2024-10-11 02:45:51 +02:00
|
|
|
template<typename FUN>
|
|
|
|
|
auto
|
2024-10-22 05:59:00 +02:00
|
|
|
PortBuilderRoot<POL,DAT>::invoke (FUN fun)
|
2024-10-11 02:45:51 +02:00
|
|
|
{
|
|
|
|
|
using WeavingBuilder_FUN = WeavingBuilder<POL, manifoldSiz<FUN>(), FUN>;
|
2024-10-22 05:59:00 +02:00
|
|
|
return PortBuilder<POL,DAT, WeavingBuilder_FUN>{move(*this), move(fun)};
|
2024-10-11 02:45:51 +02:00
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
template<class POL>
|
|
|
|
|
template<class ADA, typename...ARGS>
|
|
|
|
|
auto
|
|
|
|
|
PortBuilderRoot<POL>::adaptInvocation(ARGS&& ...args)
|
|
|
|
|
{
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
*/
|
2024-07-08 19:24:03 +02:00
|
|
|
|
2024-07-05 23:36:41 +02:00
|
|
|
/**
|
|
|
|
|
* Entrance point for building actual Render Node Connectivity (Level-2)
|
2024-07-08 01:19:06 +02:00
|
|
|
* @note when using a custom allocator, the first follow-up builder function
|
|
|
|
|
* to apply should be `withAllocator<ALO>(args...)`, prior to adding
|
|
|
|
|
* any further specifications and data elements.
|
2024-07-05 23:36:41 +02:00
|
|
|
*/
|
|
|
|
|
inline auto
|
|
|
|
|
prepareNode()
|
|
|
|
|
{
|
2024-07-07 23:56:10 +02:00
|
|
|
return NodeBuilder<UseHeapAlloc>{};
|
2024-07-05 23:36:41 +02:00
|
|
|
}
|
2024-07-08 01:19:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-06-24 23:49:55 +02:00
|
|
|
|
2024-07-05 13:41:01 +02:00
|
|
|
class ProcBuilder
|
|
|
|
|
: util::MoveOnly
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
void //////////////////////////////////////////////////////////OOO return type
|
|
|
|
|
requiredSources ()
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("enumerate all source feeds required");
|
|
|
|
|
// return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void //////////////////////////////////////////////////////////OOO return type
|
|
|
|
|
retrieve (void* streamType)
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("recursively define a predecessor feed");
|
|
|
|
|
// return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************//**
|
|
|
|
|
* Terminal: trigger the Level-3 build walk to produce a ProcNode network.
|
|
|
|
|
*/
|
|
|
|
|
void //////////////////////////////////////////////////////////OOO return type
|
|
|
|
|
build()
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED("Level-3 build-walk");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LinkBuilder
|
|
|
|
|
: util::MoveOnly
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
void //////////////////////////////////////////////////////////OOO return type
|
|
|
|
|
from (void* procAsset)
|
|
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("recursively enter definition of processor node to produce this feed link");
|
|
|
|
|
// return move(*this);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2024-07-05 23:36:41 +02:00
|
|
|
|
2024-06-24 23:49:55 +02:00
|
|
|
/**
|
2024-07-05 23:36:41 +02:00
|
|
|
* Entrance point for defining data flows and processing steps.
|
2024-06-24 23:49:55 +02:00
|
|
|
*/
|
2024-07-05 23:36:41 +02:00
|
|
|
inline auto
|
|
|
|
|
retrieve(void* streamType)
|
2024-06-24 23:49:55 +02:00
|
|
|
{
|
2024-07-05 23:36:41 +02:00
|
|
|
UNIMPLEMENTED("start a connectivity definition at Level-3");
|
2024-07-07 23:56:10 +02:00
|
|
|
return LinkBuilder{}; ///////////////////////////////////////////////////////////////////OOO this is placeholder code; should at least open a ticket
|
2024-06-24 23:49:55 +02:00
|
|
|
}
|
2009-09-04 17:43:53 +02:00
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
}} // namespace steam::engine
|
2024-07-04 23:54:13 +02:00
|
|
|
#endif /*ENGINE_NODE_BUILDER_H*/
|