diff --git a/doc/devel/uml/fig145157.png b/doc/devel/uml/fig145157.png new file mode 100644 index 000000000..dac8e23cb Binary files /dev/null and b/doc/devel/uml/fig145157.png differ diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 907cb9806..6b6429c21 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -51,6 +51,14 @@ namespace engine { + enum BufferState + { NIL, + FREE, + LOCKED, + EMITTED, + BLOCKED + }; + /** * Handle for a buffer for processing data, abstracting away the actual implementation. diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index d784bad85..e88d486fa 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -66,6 +66,7 @@ namespace play { /** established output channel */ class Connection; + typedef int64_t FrameNr; class DataSink @@ -73,7 +74,7 @@ namespace play { { public: - void emit(Time); + void emit(FrameNr); }; @@ -107,6 +108,13 @@ namespace play { Allocation allocate(); + protected: + friend class DataSink; + + virtual void lock (FrameNr, uint channel) =0; + virtual void transfer (FrameNr, uint channel) =0; + virtual void pushout (FrameNr, uint channel) =0; + private: }; diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index 6336ed674..9857cb0e0 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,6 +1,6 @@ format 58 "Play" // ProcessingLayer::Play - revision 2 + revision 3 modified_by 5 "hiv" // class settings //class diagram settings @@ -352,5 +352,55 @@ ${inlines} relations end end + + sequencediagram 145157 "output data exchange" + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default show_stereotype_properties default + overlapping_bars size A4 + end + + class 178437 "Client" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178565 "DataSink" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178693 "BufferProvider" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end end end diff --git a/uml/lumiera/145157.diagram b/uml/lumiera/145157.diagram new file mode 100644 index 000000000..f75dd4db5 --- /dev/null +++ b/uml/lumiera/145157.diagram @@ -0,0 +1,130 @@ +format 58 + +classinstance 128005 class_ref 178437 // Client + name "" xyz 53 4 2000 life_line_z 2000 +classinstance 128133 class_ref 176133 // OutputSlot + name "" xyz 281 5 2000 life_line_z 2000 +classinstance 128261 class_ref 178565 // DataSink + name "" xyz 140 28 2000 life_line_z 2000 +classinstance 128389 class_ref 178693 // BufferProvider + name "" xyz 463 5 2000 life_line_z 2000 +fragment 131845 "client side" + xyzwh 41 28 1995 156 279 +end +fragment 131973 "output implementation" + xyzwh 281 29 1995 262 277 +end +durationcanvas 128517 classinstance_ref 128005 // :Client + xyzwh 72 83 2010 11 52 +end +durationcanvas 129541 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 41 +end +durationcanvas 129669 classinstance_ref 128261 // :DataSink + xyzwh 162 238 2010 11 25 +end +durationcanvas 129925 classinstance_ref 128133 // :OutputSlot + xyzwh 307 242 2010 11 63 + overlappingdurationcanvas 133637 + xyzwh 313 272 2020 11 28 + end +end +durationcanvas 130309 classinstance_ref 128389 // :BufferProvider + xyzwh 497 250 2010 11 26 +end +durationcanvas 131333 classinstance_ref 128133 // :OutputSlot + xyzwh 307 85 2010 11 34 +end +durationcanvas 132101 classinstance_ref 128261 // :DataSink + xyzwh 162 107 2010 11 25 +end +durationcanvas 132485 classinstance_ref 128005 // :Client + xyzwh 72 167 2010 11 27 +end +durationcanvas 132613 classinstance_ref 128261 // :DataSink + xyzwh 162 169 2010 11 25 +end +durationcanvas 132869 classinstance_ref 128133 // :OutputSlot + xyzwh 307 173 2010 11 25 +end +durationcanvas 133125 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 29 +end +durationcanvas 133381 classinstance_ref 128389 // :BufferProvider + xyzwh 497 180 2010 11 25 +end +durationcanvas 133893 classinstance_ref 128389 // :BufferProvider + xyzwh 497 280 2010 11 25 +end +msg 129797 synchronous + from durationcanvas_ref 129541 + to durationcanvas_ref 129669 + yz 238 2015 explicitmsg "emit()" + show_full_operations_definition default drawing_language default + label_xy 109 230 +msg 130053 synchronous + from durationcanvas_ref 129669 + to durationcanvas_ref 129925 + yz 242 2020 explicitmsg "transfer()" + show_full_operations_definition default drawing_language default + label_xy 217 234 +msg 130437 synchronous + from durationcanvas_ref 129925 + to durationcanvas_ref 130309 + yz 250 2030 explicitmsg "transition to EMITTED" + show_full_operations_definition default drawing_language default + label_xy 374 243 +msg 131461 synchronous + from durationcanvas_ref 128517 + to durationcanvas_ref 131333 + yz 85 2015 explicitmsg "allocate()" + show_full_operations_definition default drawing_language default + label_xy 102 75 +msg 132229 synchronous + from durationcanvas_ref 131333 + to durationcanvas_ref 132101 + yz 108 2020 explicitmsg "create DataSink" + show_full_operations_definition default drawing_language default + label_xy 201 99 +msg 132357 return + from durationcanvas_ref 132101 + to durationcanvas_ref 128517 + yz 116 2025 explicitmsg "DataSink" + show_full_operations_definition default drawing_language default + label_xy 101 121 +msg 132741 synchronous + from durationcanvas_ref 132485 + to durationcanvas_ref 132613 + yz 169 2015 explicitmsg "lockBuffer()" + show_full_operations_definition default drawing_language default + label_xy 97 159 +msg 132997 synchronous + from durationcanvas_ref 132613 + to durationcanvas_ref 132869 + yz 175 2020 explicitmsg "lock()" + show_full_operations_definition default drawing_language default + label_xy 225 165 +reflexivemsg 133253 synchronous + to durationcanvas_ref 133125 + yz 211 2020 explicitmsg "generate Data" + show_full_operations_definition default drawing_language default + label_xy 84 199 +msg 133509 synchronous + from durationcanvas_ref 132869 + to durationcanvas_ref 133381 + yz 180 2025 explicitmsg "transition to LOCKED" + show_full_operations_definition default drawing_language default + label_xy 374 173 +reflexivemsg 133765 synchronous + to durationcanvas_ref 133637 + yz 272 2040 explicitmsg "pushout()" + show_full_operations_definition default drawing_language default + label_xy 325 260 +msg 134021 synchronous + from durationcanvas_ref 133637 + to durationcanvas_ref 133893 + yz 289 2025 explicitmsg "transition to FREE" + show_full_operations_definition default drawing_language default + label_xy 374 282 +preferred_whz 586 416 1 +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 0e80581b8..87c7f7827 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -6,8 +6,10 @@ diagrams 730 488 100 4 0 0 classdiagram_ref 143877 // Player Entities 663 648 100 4 0 0 - active objectdiagram_ref 144005 // Play Process Structure + objectdiagram_ref 144005 // Play Process Structure 562 424 100 4 0 0 + active sequencediagram_ref 145157 // output data exchange + 586 416 100 4 0 0 end show_stereotypes selected diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index e9189265f..6c6cb4c64 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 70 + revision 71 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1826d59ef..042b26d15 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3324,7 +3324,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit. -
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot'' This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (→ OutputManagement) @@ -3393,8 +3393,37 @@ This data exchange protocol operates on a rather low level; there is only limite Thus there are two serious problem situations * allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures. -* failure to release a buffer timely blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. -→ SchedulerRequirements+* failure to release a buffer in a timely fashion blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. +→ SchedulerRequirements +→ OutputSlotImpl +
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+!operation steps
+[>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.
+;lock()
+:claims the specified buffer for exclusive use.
+:Client may now dispose output data
+;transfer()
+:transfers control from the client to the actual output connection for feeding data.
+:Triggered by the client invoking {{{emit()}}}
+;pushout()
+:request the actual implementation to push the data to output.
+:Usually invoked from the {{{transfer()}}} implementation, alternatively from a separate thread.
+
+
+!buffer states
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the client (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+| !state||>| !lock() |>| !transfer() |>| !pushout() |
+| {{{NIL}}}||↯| · |↯| | ↯ | |
+| {{{FREE}}}||✔|↷ locked |✔|· (ignore) | · | · |
+| {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted | · |↷ free |
+| {{{EMITTED}}}||↯|↷ blocked |↯|↷ blocked | · |↷ free |
+| {{{BLOCKED}}}||↯| ✗ |↯| ✗ | ∅ |↷ free |
+where · means no operation, ✔ marks the standard cases (OK response to caller), ↯ will throw and thus kill the calling job, ∅ will treat this frame as //glitch,// ✗ will request playback stop.
+The rationale is for all states out-of-order to transition into the {{{BLOCKED}}}-state eventually, which, when hit by the next operation, will request playback stop.
+
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].