considerations how to connect exit nodes to external outputs

This commit is contained in:
Fischlurch 2012-01-28 23:12:42 +01:00
parent 288b737718
commit 3768791c76
7 changed files with 109 additions and 49 deletions

View file

@ -45,10 +45,15 @@ namespace session {
/**
* @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
* @see http://lumiera.org/wiki/renderengine.html#Fixture
*/
class Fixture
: boost::noncopyable
{
protected:
/////////////////////////////////////////////////TODO: placeholder code
list<ExplicitPlacement> content_;
boost::scoped_ptr<Segmentation> partitioning_;

View file

@ -42,6 +42,9 @@ namespace session {
* is partitioned such that each segment is <i>structurally constant</i>.
* For each segment there is a RenderGraph (unit of the render engine) which
* is able to render all ExitNodes for this segment.
*
* @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
* @see http://lumiera.org/wiki/renderengine.html#Fixture
*/
class Segment
{
@ -56,6 +59,7 @@ namespace session {
// TODO: actually necessary??
// TODO: ownership??
/////////////////////////////////////////////////TODO: placeholder code
};

View file

@ -44,9 +44,13 @@ namespace session {
* it is the general entry point for accessing the correct part of the engine
* responsible for a given timeline time point.
* @see SegmentationTool actually calculating the Segmentation
*
* @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
* @see http://lumiera.org/wiki/renderengine.html#Fixture
*/
class Segmentation
{
/////////////////////////////////////////////////TODO: placeholder code
/** segments of the engine in ordered sequence. */
list<Segment> segments_;

View file

@ -21,8 +21,50 @@
*/
/** @file dummy-play-connection.hpp
** to \em provide this service, not to access it.
**
** Dummy and test setup of playback and rendering, \em omitting most of the Lumiera engine.
** Building this dummy configuration was driven by the need to test and verify the design
** in the course of building the foundations of the render engine. The design of Lumiera's
** engine is elaborate, and thus -- for a long time -- we have to live with a not-yet
** operational engine. While, at the same time, we need to start integrating components
** to see if and how the design works out. So, as a remedy, we create a fixture of
** "coordinated placeholders". These can be used to stand-in for the real services still
** to be written, allowing to invoke the high-level interfaces soon. And behind the scenes,
** these placeholders are connected, allowing to produce specific test situations and then
** verify the results after the test run.
**
** \par Use cases
** This dummy setup can be used in various circumstances
** - for unit tests we want to produce artificial test media frames: each TestFrame is
** produced with a reproducible pseudo-random sequence and can be verified to the last bit.
** - for integration tests, we want to generate test media data, either to send them to a file,
** or to a real system output
** - the GUI needs a dummy engine for being able to implement at least some operational pieces
** of functionality, instead of just creating windows, menus and icons.
** - on the long run, the application will need some kind of test data generator for the users
** to verify a more complicated wiring setup. Thus, the DummyPlayConnection is there to stay!
**
** Because these are somewhat similar usage scenarios, where this and that part is to be exchanged
** for some, we prefer a <i>policy based design</i> here: The DummyPlayConnection is templated
** to use a \em strategy, filling in the variable parts.
**
** \par provided test services
** By using different strategy template parameters, we create different flavours of the dummy;
** each one counting as a separate setup (not related to each other, that is). The actual instance
** then can just be default created; it should be placed into an scope enduring the whole usage
** cycle. Repeated re-initialisation or re-loading is outside the intended usage scope here.
**
** The <b>core interface</b> allows to retrieve dummy implementations of
** - a session model exposing exit node(s)
** - generator object(s) to live within this session model
** - corresponding generator nodes to serve as implementation of the former
** - a low-level render node network ready to use, relying on those generator nodes
** - OutputSlot implementations to serve as pseudo- or demo output facilities
** - an OutputManager exposing those output facilities.
**
** The <b>test support interface</b> provides a test driver for performing a controlled
** playback or rendering for some time. Thus, a test routine may lock into a blocking wait,
** to investigate results after the planned test sequence was performed.
**
** @see lumiera::DummyPlayer
** @see gui::PlaybackController usage example
*/
@ -65,6 +107,12 @@ namespace play {
/********************************************************************
* 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.
*/
template<class DEF>
class DummyPlayConnection

View file

@ -51,27 +51,6 @@ namespace play {
typedef EngineService::QoS_Definition RenderQuality;
RenderConfigurator::~RenderConfigurator() { } // emit VTable here...
/** Template Method: how to build an active render feed,
* pulling from the given exit point of the model and
* feeding the OutputSlot established appropriately
* to deliver media data of suitable type */
Feed
RenderConfigurator::buildActiveFeed (ModelPort port)
{
OutputSlot& slot = getOutputFor (port);
return Feed (buildCalculationStreams (port,slot));
}
namespace { // Implementation details...
// using std::tr1::bind;
@ -110,8 +89,8 @@ namespace play {
return activateEngine (port, nominalTimings, activeOutputConnection, renderQuality_);
}
protected:
virtual engine::CalcStreams
engine::CalcStreams
activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection, RenderQuality quality)
{
return EngineService::instance().calculate (port, timings, activeOutputConnection, quality);
@ -140,6 +119,22 @@ namespace play {
} // (End) hidden service impl details
RenderConfigurator::~RenderConfigurator() { } // emit VTable here...
/** Template Method: how to build an active render feed,
* pulling from the given exit point of the model and
* feeding the OutputSlot established appropriately
* to deliver media data of suitable type */
Feed
RenderConfigurator::buildActiveFeed (ModelPort port)
{
OutputSlot& slot = getOutputFor (port);
return Feed (buildCalculationStreams (port,slot));
}

View file

@ -42,7 +42,6 @@ namespace test {
//using proc::engine::test::TestFrame;
using lumiera::Play;
typedef Play::Controller Controller;
typedef time::Control<time::Duration> DurationControl;
@ -71,27 +70,28 @@ namespace test {
DummyPlayer dummy;
CHECK (!dummy.isWired());
Controller ctrl = Play::facade().connect ( dummy.provide_testModelPorts()
, dummy.provide_testOutputSlot());
Play::Controller player
= Play::facade().connect ( dummy.provide_testModelPorts()
, dummy.provide_testOutputSlot());
CHECK ( dummy.isWired());
DurationControl playDuration;
ctrl.controlDuration (playDuration);
player.controlDuration (playDuration);
// configure the controller to playback only for a fixed time duration
playDuration (dummy.getPlannedTestDuration());
CHECK (!ctrl.is_playing());
CHECK (!player.is_playing());
ctrl.play(true); // hit the start button
CHECK (ctrl.is_playing());
player.play(true); // hit the start button
CHECK (player.is_playing());
dummy.waitUntilDue(); // test helper: block waiting until planned test should be done
CHECK (!ctrl.is_playing()); // should have returned to pause, since we've set a fixed playback duration
CHECK (!player.is_playing()); // should have returned to pause, since we've set a fixed playback duration
CHECK (dummy.isWired());
ctrl.close();
player.close();
CHECK (!dummy.isWired());
CHECK (dummy.gotCorrectOutput());
}

View file

@ -1967,7 +1967,7 @@ Some further details
* a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
</pre>
</div>
<div title="Fixture" modifier="Ichthyostega" modified="201112171718" created="200706220324" tags="def spec Builder Model" changecount="50">
<div title="Fixture" modifier="Ichthyostega" modified="201201281945" created="200706220324" tags="def spec Builder Model" changecount="53">
<pre>a specially configured view -- joining together high-level and low-level model.
The Fixture acts as //isolation layer// between the two models, and as //backbone to attach the render nodes.//
* all MObjects have their position, length and configuration set up ready for rendering.
@ -1993,12 +1993,12 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
;Segmentation
:The segmentation partitiones the time axis of a single timeline into segments of constant (wiring) configuration
:Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; they are always built and discarded at once.
:Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; individual segments are immutable, they are built and discarded as a whole chunk.
:Segments (and even a different Segmentation) may be //hot swapped// into an ongoing render.
;Exit Nodes
:Each segment holds an ExitNode for each relevant ModelPort of the corresponding timeline.
:Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{&lt;number_of_segments&gt;}}} separate exit nodes, but of course an exit node may be //mute.//
:Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{&lt;number_of_segments&gt;}}} separate exit nodes, but of course an exit node may be //mute//&amp;nbsp; in some segmehts.
</pre>
</div>
<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201112171816" created="201012162304" tags="spec Builder" changecount="13">
@ -3133,9 +3133,9 @@ Model ports are represented by small non-copyable descriptor objects with distin
</pre>
</div>
<div title="ModelPortRegistry" modifier="Ichthyostega" modified="201012112230" created="201012030314" tags="Model impl" changecount="3">
<div title="ModelPortRegistry" modifier="Ichthyostega" modified="201201281946" created="201012030314" tags="Model impl" changecount="4">
<pre>Model ports are conceptual entities, denoting the points where output might possibly be produced &amp;rarr; see [[definition|ModelPort]].
But there is an actual representation, a collection of small descriptor objects managed by the Fixture and organised within the model port table datastructure. Because model ports are discovered during the build process, we need the ability to (re)build this table dynamically, finally swapping in the modified configuration with a transactional switch. Only the builder is allowed to perform such mutations, while for client code model ports are immutable.
But there is an actual representation, a collection of small descriptor objects managed by the Fixture and organised within the model port table datastructure. Because model ports are discovered during the build process, we need the ability to (re)build this table dynamically, finally swapping in the modified configuration with a transactional switch. Only the builder is allowed to perform such mutations, while for client code the model ports are immutable.
!supported operations
* get the model port by ~Pipe-ID
@ -4481,7 +4481,7 @@ The Controller is exposed to the client and acts as frontend handle, while the p
Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalcStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalcStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
</pre>
</div>
<div title="PlayService" modifier="Ichthyostega" modified="201112161614" created="201105221900" tags="Player spec draft" changecount="11">
<div title="PlayService" modifier="Ichthyostega" modified="201201282138" created="201105221900" tags="Player spec draft" changecount="16">
<pre>The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be &quot;rendering and playback coordination subsystem&quot;. It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.
!provided services
@ -4489,6 +4489,10 @@ Right within the play process, there is a separation into two realms, relying on
* managing existing play processes
* convenience short-cuts for //performing//&amp;nbsp; several kinds of high-level model objects
!subject of performance
Every play or render process will perfrom a part of the session. This part can be specified in varios ways, but in the end, every playback or render boils down to //performing some model ports.// While the individual model port as such is just an identifier (actually implemented as ''pipe-ID''), it serves as a common identifier used at various levels and tied into several related contexts. For one, by querying the [[Fixture]], the ModelPort leads to the actual ExitNode -- the stuff actually producing data when being pulled. Besides that, the OutputManager used for establishing the play process is able to resolve a real OutputSlot -- which, as a side effect, also yields the final data format and data implementation type to use for rendering or playback.
!creating a play process
This is the core service provided by the player subsystem. The purpose is to create a collaboration between several entities, creating media output
;data producers
@ -7748,7 +7752,7 @@ In case it's not already clear: we don't have &quot;the&quot; Render Engine, rat
The &amp;raquo;current setup&amp;laquo; of the objects in the session is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use the term &quot;stateless&quot; in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
</pre>
</div>
<div title="Wiring" modifier="Ichthyostega" modified="201201150238" created="201009250223" tags="Concepts Model design draft" changecount="36">
<div title="Wiring" modifier="Ichthyostega" modified="201201282126" created="201009250223" tags="Concepts Model design draft" changecount="45">
<pre>within Lumiera's ~Proc-Layer, on the conceptual level there are two kinds of connections: data streams and control connections. The wiring deals with how to define and control these connections, how they are to be detected by the builder and finally implemented by links in the render engine.
&amp;rarr; see OutputManagement
&amp;rarr; see OutputDesignation
@ -7757,24 +7761,24 @@ The &amp;raquo;current setup&amp;laquo; of the objects in the session is sort of
!Stream connections
A stream connection should result in media data traveling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of &quot;media content&quot; flowing through the system. It can be described unambigously by having an uniform StreamType &amp;mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as //creating a system of stream connections,// which, more specifically can be categorised into two kinds or types of connections -- the rigit and the flexible parts:
* the [[Pipes|Pipe]] are rather rigid ''processing chains'', limited to using one specific StreamType. Pipes are build by attaching processing descriptors (Effect ~MObjects), where the order of attachment is given by the placement.
A stream connection should result in media data travelling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of &quot;media content&quot; flowing through the system. It can be described unambigously by having an uniform StreamType &amp;mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as //creating a system of stream connections,// which, more specifically can be categorised into two kinds or types of connections -- the rigid and the flexible parts:
* the [[Pipes|Pipe]] are rather rigid ''processing chains'', limited to using one specific StreamType. &lt;br/&gt;Pipes are built by attaching processing descriptors (Effect ~MObjects), where the order of attachment is given by the placement.
* there are flexible interconnections or ''routing links'', including the ability to sum or overlay media streams, and the possibility of stream type conversions. They are controlled by the OutputDesignation (&quot;wiring plug&quot;), to be queried from the placement at the source side of the interconnection (i.e. at the exit point of a pipe)
Please note, the high-level model comprises a blue print for constructing the render engine. There is no real data &quot;flowing&quot; through this model, thus any &quot;wiring&quot; may be considered conceptual. Within this context, any wiring specifications just express //the intention of getting things connected in a specific way.// Consider the example of a clip, which might find out (through query of his placement) that he's intended to produce output for some destination or bus or subgroup called &quot;XYZ&quot;
Please note, the ''High-level model'' comprises a blue print for constructing the render engine. There is no real data &quot;flowing&quot; through this model, thus any &quot;wiring&quot; may be considered conceptual. Within this context, any wiring specifications just express //the intention of getting things connected in a specific way.// Consider the example of a clip, which might find out (through query of his placement) that he's intended to produce output for some destination or bus or subgroup called &quot;Ambiance sound&quot;
The builder to the contrary considers matters locally. He's confined to a given set of objects handed in for processing, and during that processing will collect all [[plugs|WiringPlug]] and [[claims|WiringClaim]] encountered. While the former denote the desired //destination//&amp;nbsp; of data emerging from a pipe, the wiring claim expresses the fact that a given object //claims to be some output destination.// Typically, each [[global pipe or bus|GlobalPipe]] raises such a claim. Both plugs and claims are processed on a &quot;just in case they exist&quot; base: When encountering a plug and //resolving//&amp;nbsp; the corresponding claim, the builder drops off a WiringRequest accordingly. Note the additional resolution, which might be necessary due to virtual media and nested sequences (read: the output designation might need to be translated into another designation, using the media/channel mapping created by the virtual entity &amp;rarr; see [[mapping of output designations|OutputMapping]])
The ''Builder'' to the contrary considers matters locally. He's confined to a given set of objects handed in for processing, and during that processing will successively collect all the [[plugs|WiringPlug]] and [[claims|WiringClaim]] encountered. While the former denote the desired //destination//&amp;nbsp; of data emerging from a pipe, the wiring claim expresses the fact that a given object //claims to be some output destination.// Typically, each [[global pipe or bus|GlobalPipe]] raises such a claim. Both plugs and claims are processed on a &quot;just in case they exist&quot; base: When encountering a plug and //resolving//&amp;nbsp; the corresponding claim, the builder drops off a WiringRequest accordingly. Note the additional resolution, which might be necessary due to virtual media and nested sequences (read: the output designation might need to be translated into another designation, using the media/channel mapping created by the virtual entity &amp;rarr; see [[mapping of output designations|OutputMapping]])
The Processing os such a wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
The Processing of such a wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
* the mould is picked from a small number of alternatives, based on the general wiring situation.
* the processing pattern is queried to fit the mould, the stream type and additional placement specifications ({{red{TODO 11/10: work out details}}})
* the stream type system itself contributes in determining possible connections and conversions, introducing further processing patterns
The final result, within the render engine, is a network of processing nodes. Each of this nodes holds a WiringDescriptor, created as a result of the wiring operation detailed above. This descriptor lists the predecessors, and (in somewhat encoded form) the other details necessary for the processing node to respond properly at the engine's calculation requests (read: those details are implementation bound and can be expeted to be made to fit)
The final result, within the ''Render Engine'', is a network of processing nodes. Each of this nodes holds a WiringDescriptor, created as a result of the wiring operation detailed above. This descriptor lists the predecessors, and (in somewhat encoded form) the other details necessary for the processing node to respond properly at the engine's calculation requests (read: those details are implementation bound and can be expeted to be made to fit)
On a more global level, this LowLevelModel within the engine exposes a number of [[exit nodes|ExitNode]], each corresponding to a ModelPort, thus being a possible source to be handled by the OutputManager, which is responsible for mapping and connecting nominal outputs (the model ports) to actual output sinks (external connections and viewer windows). A model port isn't necessarily an absolute endpoint of connected processing nodes &amp;mdash; it may as well reside in the middle of the network, e.g. as a ProbePoint. Besides the core engine network, there is also an [[output network|OutputNetwork]], built and extended on demand to prepare generated data for the purpose of presentation. This ViewerPlayConnection might necesitate scaling or interpolating video for a viewer, adding overlays with control information produced by plugins, or rendering and downmixing multichannel sound. By employing this output network, the same techniques used to control wiring of the main path, can be extended to control this output preparation step. ({{red{WIP 11/20}}} some important details are to be settled here, like how to control semi-automatic adaptation steps. But that is partially true also for the main network: for example, we don't know where to locate and control the faders generated as a consequence of building a summation line)
On a more global level, this LowLevelModel within the engine exposes a number of [[exit nodes|ExitNode]], each corresponding to a ModelPort, thus being a possible source to be handled by the OutputManager, which is responsible for mapping and connecting nominal outputs (the model ports) to actual output sinks (external connections and viewer windows). A model port isn't necessarily an absolute endpoint of connected processing nodes &amp;mdash; it may as well reside in the middle of the network, e.g. as a ProbePoint. Besides the core engine network, there is also an [[output network|OutputNetwork]], built and extended on demand to prepare generated data for the purpose of presentation. This ViewerPlayConnection might necesitate scaling or interpolating video for a viewer, adding overlays with control information produced by plugins, or rendering and downmixing multichannel sound. By employing this output network, the same techniques used to control wiring of the main path, can be extended to control this output preparation step. ({{red{WIP 11/10}}} some important details are to be settled here, like how to control semi-automatic adaptation steps. But that is partially true also for the main network: for example, we don't know where to locate and control the faders generated as a consequence of building a summation line)
!!!Participants and Requirements
* the ~Pipe-ID needs to be something easily usable
* the ~Pipe-ID is an universal key to denote connections, outputs and ports.
* output designation is just a ~Pipe-ID, actually a thin wrapper to make the intention explicit
* claims and plugs are to be implemented as LocatingPin
* as decided elsewhere, we get a [[Bus-MObject|BusMO]] as an attachment point