From 56ceca398b15f4d588aae2fe944442ed38594c54 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 29 Nov 2010 06:06:52 +0100 Subject: [PATCH 01/13] The first substantial step towards a builder backbone Defined the structure of the fixture and the outline of the process leading to creating this data structure. --- doc/devel/draw/Builder.Fixture-1.svg | 4091 +++++++++++++++++ .../draw/Builder.SegmentationSteps-1.svg | 3333 ++++++++++++++ doc/devel/uml/fig129413.png | Bin 15598 -> 15242 bytes uml/lumiera/128901 | 16 +- uml/lumiera/129413.diagram | 66 +- uml/lumiera/5.session | 10 +- uml/lumiera/lumiera.prj | 2 +- wiki/draw/Fixture1.png | Bin 0 -> 82722 bytes wiki/draw/SegmentationSteps1.png | Bin 0 -> 73601 bytes wiki/renderengine.html | 57 +- 10 files changed, 7521 insertions(+), 54 deletions(-) create mode 100644 doc/devel/draw/Builder.Fixture-1.svg create mode 100644 doc/devel/draw/Builder.SegmentationSteps-1.svg create mode 100644 wiki/draw/Fixture1.png create mode 100644 wiki/draw/SegmentationSteps1.png diff --git a/doc/devel/draw/Builder.Fixture-1.svg b/doc/devel/draw/Builder.Fixture-1.svg new file mode 100644 index 000000000..c7bb4e9d2 --- /dev/null +++ b/doc/devel/draw/Builder.Fixture-1.svg @@ -0,0 +1,4091 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + Structure of the Fixture + + + Ichthyostega + + + Lumiera: how the Fixture data structure is organised + 2010 + + + + + + + + + + + + + + + + List of Model Ports + + + + + exitnodes + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Segmentation-1 + Segmtn-2 + Eplicitplacem + diff --git a/doc/devel/draw/Builder.SegmentationSteps-1.svg b/doc/devel/draw/Builder.SegmentationSteps-1.svg new file mode 100644 index 000000000..9694920cf --- /dev/null +++ b/doc/devel/draw/Builder.SegmentationSteps-1.svg @@ -0,0 +1,3333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + Steps towards building a Segmentation + + + Ichthyostega + + + explanation of Builder structures: How the Segmentation of the Lumiera low-level-model (render nodes graph) is derived from the high-level-model + 2010 + + + + + + + + + + + + + + + + + + Binding + + + + + + + + + + + Sequence-α + + + + + + Timeline-1 + + + Fixture + + + + + + + + + + + + + + + + + + global Pipes + + + + + + + + + + + + + x + + + + + + + + + + + + + + Clip-A + + + + + + + Meta-Clip + + + + + + + + + + Sequence-β + Segmentation + + + + + + + Clip-B + + + + + + + + Binding + + + Timeline-2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clip-A + + + + + + + Clip-B + + + + + + + + + + + + Clip-B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Binding + + + + x + + + + x + + + + x + + + + x + + explicit Placement + exit node + ModelPort-1 + + + + + + + + x + + + ModelPort-2 + + + + + + x + + + + + + diff --git a/doc/devel/uml/fig129413.png b/doc/devel/uml/fig129413.png index df8722ad204d59d46078e4734bbade67ba738c37..581c0bf4636c4c6dd513a19a1d087a283f2ff49e 100644 GIT binary patch literal 15242 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%0ATs?pO_vR+0()lp%`4NBeYXNUE^w;YSXo$Y?{$dA zBGToIU|C;h@5{l>d#tTQW%L*Ne$459-v6uFrEX6mIA8+VrP_-fn$oc+LocuJVUgC> z_$Z2mN*bg@F=)-& z>`Y}sO|CW?3FWIFltu8o4bYL)UilHWTI<-eG zWjT0H=Hv(BK6Z{keA`w@!&AVcD-rQ~gA)dI_sR-u-U!4ju>w+bh0X3+h&Wf>0D1L2g8}Q;AT#8ZO!3X ziCP|EM=I^fk>6ppx2v-9vsV*=o23EI-$&f1-C@4(O}hLD3F&20f!kZ7I+FuMPrHv3 zPrSw^dEu5GYt}-uao?p3m6QN=98VeT*5_%LuSK`OZp($F&4@kcq2{R$qp5 z%+wg5+(Bsfh0D}Ct*=k=r_Em_GsAof!kJjCwcW@?!)1A;eMnYrG|}mYT{{>IGK0=L z1DHoM9jK;oBo!C?eHj2bT8tN#$$SN%5dLvWZ<7ZYKnw^$2guvI?yxvy=v5bEV;qEW zqfH}9glh1TQG(qEVG1(P3Q)LcF+f*b9bbNLPvTsIxlWs^d%X*&3J_F%1+}-FeF^?? zoMKe)<4c{iNbi;NNxG$Kog?(-n}{q0ui)v?`BX*Qp5t;E_&kCBS7#WAH`T)J2!tRE zMGi3kzVaybqUF@N+h=-si^nbY}!8PtJ^tsyq>jrJKf&C}uykmp=E5GFCB1MlZ4CcLz zi+aJJD>y+)L*cnzU9$InO=cj>mzRhDIPl8$Er2}DvZo5b>#QiwpeR{ID&Pm(+j8Dp z?(JJLL_Ztnap^K?^>7w}8O_`6gB!Eo$jg;!Xe*H9 zMUY~Mv5?`IPeTHjoC`8hC9Lc$3Ldc(uyivm`wo|&sDpM1FCGk=Fz_Rr0xNW(FHE>z zXb=r((V`ln*&NIa?DNDxF0cuvj(BM!ZH2!V7x0r`9p4wQSG3lb2C1Emk`fdsBbiu2 z#>UfYks5we7iJAa0u?#qF+YBi;VxNOd{f<^C@WdMmgNjcRTPjD%^KZ6vif}a)oEzqC4hWWx_~k;y6Ihzr4VK z5tes+fD)#1$QEf06&_0Zap_?U-<|qG^1^&&U^7S~qXI^b@4Vc&_ z!@LIR5`F4a(gULe?Op)?`THUU-KNCFf2$Z?D6gYsV}p1ba zYJfy85Bet_o5Y?CPOmGZ4Tn^cwcPtS*QGch%L+VNT*G_*cQlJ-39oLC&lhsmYp zq%ZxQI;q-SJk z<+{G#HsY%+<21372T}vMYK0zidrL=@h`y_-si~Y?NBe5j3;l@2J9BBR*G^mG5}5`I ziwi4Nh6}ysX3~1%HRDQz=mct{(5WdyIBK^>rX3UntQXqjc*{U6ZMJOI(ACJR&LJOu z;rmqyiCsr@HZ;Dnk=hQ})|Fj^ap(B2k0cHjx)(^UT)B@82jZXeU_nQAe<_X# zAFv}tg_@+&D)2dQvAG(C40OCw==vpNqun4DR3!d>P8s!3R=Lvonu|}w^nU+>No-^B z8=5v~l>O>Hd82iypmV=Fx1gbDoMA!+K&jtv>riiB|9*S(O9tjwpg2Gunvx?Q^Li72 z;^Zn>4(9iB)(g?(()!)ziZJWHdS0z^TwR$mATd~VvpNoa3F^cwf=dnOX8VtR9{dOe z*C%ctD78ycIDzq_R9lfWo|gy>^;LK6CPHRA@%5Etc@-OLmEcUcIhZs6UB#fyarLlJ zIHd#2z^ld{&ZOMqRtw&YsvE=A=m5g8wLPgaa@XJIoMmZ#9muJttkwZXtG#%PGh6l5 z0(U=#Zhub%&XY_qhIQ0*EYGd0hskBy3U{e56Pr(Qg0WPq&;+ZLh{Qa9&*P0MA)!uz z)n?m$M;>oARQqk>3nt~!D@Lb<<8L}V_@XtaFAy^vPNone0k6Cl9&>dOq3-ga9rg;2 zf%;9Ux8fz!1Qrm2z=E^K=MO%EVC)X}TOEHFC`k+{^Ac-*gLZ;}`QalO->(iFqa*QnNAAN8v{!7l8(fzRLZK+v_V z2ML!x2un%DS~l^MLSlByx+AAJRL?2RLM$Ng13!~!#SpVwk8;tVgQ&A!;F={N&fI8| zszigFV|Oj&vl1^xF-+M^+CtugOLX$6nf1mm4yXEW{ri7%k^Q-q`e(i4-@C<8S@?hW eQ{P|fbw<0lcvV2XV(JXs2$GVNmw=1wz5ZWXc@>8M diff --git a/uml/lumiera/128901 b/uml/lumiera/128901 index 5a7cdb03b..300f66d4c 100644 --- a/uml/lumiera/128901 +++ b/uml/lumiera/128901 @@ -1,6 +1,6 @@ format 58 "Builder" // ProcessingLayer::MObject::Builder - revision 20 + revision 21 modified_by 5 "hiv" // class settings //class diagram settings @@ -74,7 +74,7 @@ format 58 iterative activityaction 128773 "define segment" opaque_action - pin 128133 "inFixture" explicit_type "" + pin 128133 "inContent" explicit_type "" unordered in end @@ -107,11 +107,11 @@ format 58 end flow 130181 "" - on pin_ref 128133 // inFixture + on pin_ref 128133 // inContent end flow 131717 "" - on pin_ref 128133 // inFixture + on pin_ref 128133 // inContent end end @@ -168,6 +168,14 @@ format 58 activitynode 129157 activity_final "" end + + activityobject 129157 "Timeline contents" + explicit_type "" + unordered + flow 133125 "" + on pin_ref 128133 // inContent + end + end end classdiagram 129285 "Builder Tool (Visitor)" diff --git a/uml/lumiera/129413.diagram b/uml/lumiera/129413.diagram index c8fd505f1..655fa1d29 100644 --- a/uml/lumiera/129413.diagram +++ b/uml/lumiera/129413.diagram @@ -2,76 +2,72 @@ format 58 activitycanvas 130437 activity_ref 128005 // building the Engine show_infonote default drawing_language default show_stereotype_properties default - xyzwh 147 27 2000 581 532 + xyzwh 120 26 2000 468 525 params parametercanvas 130565 parameter_ref 128645 // build Request - xyzwh 541 12 2002 113 31 + xyzwh 401 11 2002 113 31 end end end activityactioncanvas 130693 activityaction_ref 128645 // activity action configure Tools show_infonote default drawing_language default show_stereotype_properties default show_opaque_action_definition default - xyzwh 529 71 2005 136 41 + xyzwh 389 63 2005 136 41 end expansionregioncanvas 130821 expansionregion_ref 128133 // establish partitioning - xyzwh 359 208 2005 205 96 + xyzwh 219 200 2005 205 96 nodes expansionnodecanvas 131077 expansionnode_ref 128005 // segment Tool - xyzwh 458 203 2007 33 11 label_xy 456 183 + xyzwh 318 195 2007 33 11 label_xy 316 175 end expansionnodecanvas 132613 expansionnode_ref 128133 // segments - xyzwh 460 298 2007 33 11 label_xy 459 312 + xyzwh 320 290 2007 33 11 label_xy 319 304 end end end activityactioncanvas 130949 activityaction_ref 128773 // activity action define segment show_infonote default drawing_language default show_stereotype_properties default show_opaque_action_definition default - xyzwh 409 236 2010 135 42 + xyzwh 269 228 2010 135 42 pins - pincanvas 131205 pin_ref 128133 // inFixture - xyzwh 399 249 2012 11 11 label_xy 315 241 + pincanvas 131205 pin_ref 128133 // inContent + xyzwh 259 241 2012 11 11 label_xy 170 250 end end end -activityobjectcanvas 131333 activityobject_ref 128005 // activity object Fixture - show_infonote default drawing_language default show_stereotype_properties default - xyzwh 177 239 2005 49 31 -end activitynodecanvas 133509 activitynode_ref 129029 // fork - horizontal xyz 585 147 2005 + horizontal xyz 445 139 2005 end expansionregioncanvas 133893 expansionregion_ref 128261 // build Processors - xyzwh 359 349 2005 272 152 + xyzwh 219 341 2005 272 152 nodes expansionnodecanvas 134021 expansionnode_ref 128261 // build Tool - xyzwh 581 344 2007 33 11 label_xy 573 324 + xyzwh 441 336 2007 33 11 label_xy 433 316 end expansionnodecanvas 134149 expansionnode_ref 128389 // segments - xyzwh 460 344 2007 33 11 label_xy 460 359 + xyzwh 320 336 2007 33 11 label_xy 320 351 end expansionnodecanvas 136581 expansionnode_ref 128517 // complete Render Engine - xyzwh 460 495 2007 33 11 label_xy 496 500 + xyzwh 320 487 2007 33 11 label_xy 356 492 end end end activityactioncanvas 134277 activityaction_ref 128901 // activity action create ProcNode show_infonote default drawing_language default show_stereotype_properties default show_opaque_action_definition default - xyzwh 419 389 2010 114 42 + xyzwh 279 381 2010 114 42 end activityactioncanvas 134405 activityaction_ref 129029 // activity action connect show_infonote default drawing_language default show_stereotype_properties default show_opaque_action_definition default - xyzwh 420 442 2015 113 42 + xyzwh 280 434 2015 113 42 end activitynodecanvas 134533 activitynode_ref 129157 // activity_final - xyz 543 527 2005 + xyz 403 519 2005 end -simplerelationcanvas 131461 simplerelation_ref 128389 - from ref 131333 z 1999 to point 400 253 - line 131589 z 1999 to ref 130437 +activityobjectcanvas 137093 activityobject_ref 129157 // activity object Timeline contents + show_infonote default drawing_language default show_stereotype_properties default + xyzwh 147 157 2005 99 31 end flowcanvas 132101 flow_ref 130309 // @@ -92,8 +88,8 @@ end flowcanvas 133765 flow_ref 130949 // geometry VHV - from ref 133509 z 2004 to point 595 180 - line 134789 z 2004 to point 472 180 + from ref 133509 z 2004 to point 455 172 + line 134789 z 2004 to point 332 172 line 134917 z 2004 to ref 131077 show_infonote default drawing_language default show_stereotype_properties default write_horizontally default end @@ -105,7 +101,7 @@ end flowcanvas 135429 flow_ref 131205 // geometry HVr - from ref 134021 z 2006 to point 595 407 + from ref 134021 z 2006 to point 455 399 line 135557 z 2006 to ref 134277 show_infonote default drawing_language default show_stereotype_properties default write_horizontally default end @@ -119,11 +115,6 @@ flowcanvas 136197 flow_ref 131461 // from ref 134277 z 2009 to ref 134405 show_infonote default drawing_language default show_stereotype_properties default write_horizontally default end -flowcanvas 136453 flow_ref 131717 // - - from ref 131333 z 2004 to ref 131205 - show_infonote default drawing_language default show_stereotype_properties default write_horizontally default -end flowcanvas 136709 flow_ref 131845 // from ref 134405 z 2006 to ref 136581 @@ -131,9 +122,16 @@ flowcanvas 136709 flow_ref 131845 // end flowcanvas 136837 flow_ref 131973 // - from ref 136581 z 2004 to point 487 524 + from ref 136581 z 2004 to point 347 516 line 136965 z 2004 to ref 134533 show_infonote default drawing_language default show_stereotype_properties default write_horizontally default end -preferred_whz 768 616 1 +flowcanvas 137221 flow_ref 133125 // + geometry VH + + from ref 137093 z 2004 to point 194 244 + line 137349 z 2004 to ref 131205 + show_infonote default drawing_language default show_stereotype_properties default write_horizontally default +end +preferred_whz 627 621 1 end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 87b65616c..5e7b8f6a0 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -1,11 +1,13 @@ -window_sizes 1324 1020 270 1044 872 71 +window_sizes 1615 1020 270 1335 872 71 diagrams classdiagram_ref 136453 // Session backbone 631 352 100 4 0 0 - active objectdiagram_ref 138885 // ModelAssetRelations + objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 classdiagram_ref 128133 // Session structure - 835 697 100 4 0 0 + 835 697 100 4 300 0 + active activitydiagram_ref 129413 // build flow + 627 621 100 4 0 0 end show_stereotypes selected @@ -31,7 +33,7 @@ open class_ref 152453 // PlacementRef classrelation_ref 178437 // class_ref 153733 // QueryFocusStack - classview_ref 128261 // Builder Workings + expansionregion_ref 128133 // establish partitioning usecaseview_ref 128261 // config examples classview_ref 128133 // Engine Workings class_ref 164485 // Request diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 35bcc31a2..ae9d839be 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 64 + revision 65 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/draw/Fixture1.png b/wiki/draw/Fixture1.png new file mode 100644 index 0000000000000000000000000000000000000000..d594217319a1b3fe0adb977054c2e63db723fdb6 GIT binary patch literal 82722 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%0DDe(pQ}`MZ|@ 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 literal 0 HcmV?d00001 diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 2ab329a32..f2dd7e790 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1077,6 +1077,26 @@ Please note the shortcomings and logical contradictions in the solution currentl * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.// +
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
+{{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
+
+;Resolving the DAG[>img[Steps towards creating a Segmentation|draw/SegmentationSteps1.png]]
+Because of the possibility of binding a Sequence multiple times, and maybe even nested as virtual clip, the [[high-level model|HighLevelModel]] actually constitutes a DAG, not a tree. This leds to quite some tricky problems, which we try to resolve by //rectifying the DAG into N virtual trees.// (&rarr; BindingScopeProblem)
+
+Relying on this transformation, each Timeline spans a sub-tree virtually separated from all other timelines; the BuildProcess is driven by [[visiting|VisitorUse]] all the //tangible// objects within this subtree. In the example shown to the right, Sequence-β is both bound as VirtualClip into Sequence-α, as well as bound independently as top-level sequence into Timeline-2. Thus it will be visited twice, but the QueryFocus mechanism ensures that each visitation »sees« the proper context.
+
+;Explicit Placements
+Each tangible object placement (relevant for rendering), which is encountered during that visitation, gets //resolved// into an [[explicit placement|ExplicitPlacement]]. If we see [[Placement]] as a positioning within a multi dimensional configuration space, then the resolution into an explicit placement is like the creation of an ''orthogonal base'': Within the explicit placement, each LocatingPin corresponds exactly to one degree of freedom and can be considered independently from all other locating pins. This resolution step removes any fancy dynamic behaviour and all scoping and indirect references. Indeed, an explicit placement is a mere //value object;// it isn't part of the session core (PlacementIndex), isn't typed and can't be referred indirectly.
+
+;Segmentation of Time axis
+This simple and explicit positioning thus allows to arrange all objects as time intervals on a single axis. Any change and especially any overlap is likely to create a different wiring configuration. Thus, for each such configuration change, we fork off a new //segment// and //copy over// all partially touched placements. The resulting seamless sequence of non-overlapping time intervals provides the backbone of the datastructure called [[Fixture]].
+
+;Building the Network
+From this backbone, the actual [[building mechanism|BuilderMechanics]] proceeds as a ongoing visitation and resolution, resulting in the gowth of a network of [[render nodes|ProcNode]] starting out from the source reading nodes and proceeding up through the local pipes, the transitions and the global pipes. When this build process is exhausted, besides the actual network, the result is a //residuum of nodes not connected any further.// Any of these [[exit nodes|ExitNode]] can be associated to a ~Pipe-ID in the high-level model. Within each segment, there should be one exit node per pipe-ID at max. These are the [[model ports|ModelPort]] resulting from the build process, keyed by their corresponding ~Pipe-ID.
+&rarr; see [[Structure of the Fixture|Fixture]]
+
+
All decisions on //how // the RenderProcess has to be carried out are concentrated in this rather complicated Builder Subsystem. The benefit of this approach is, besides decoupling of subsystems, to keep the actual performance-intensive video processing code as simple and transparent as possible. The price, in terms of increased complexity &mdash; to pay in the Builder &mdash; can be handled by making the Build Process generic to a large degree. Using a Design By Contract approach we can decompose the various decisions into small decision modules without having to trace the actual workings of the Build Process as a whole.
 
@@ -1708,8 +1728,8 @@ To make the intended use of the classes more clear, consider the following two e
 
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
 
-
-
A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. In addition to the properties of a (unspecific) Placement, the ExplicitPlacement specifies a absolute time and track position for locating the Subject
+
+
A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. But contrary to the (standard) placements, which may exhibit all kinds of fancy dynamic and scope dependent behaviour, within an explicit placement all properties are resolved and materialised. While the (standard) placement may contain an arbitrary list of LocatingPin objects, the resolution into an explicit placement performs a kind of »orthogonalisation«: each remaining LocatingPin defines exactly one degree of freedom independent of all others. Most notably, the explicit placement always specifies a absolute time and [[output designation|OutputDesignation]] for for locating the Subject.
 
@@ -1766,19 +1786,34 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
-
-
a specially configured sequence list
+
+
a specially configured view -- joining together high-level and low-level model
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
-* every MObject is associated with an ExplicitPlacement, which declares a fixed position (Time, [[Pipe-ID|OutputDesignation]])
-* these ~ExplicitPlacements are contained in a ordered List, sometimes denoted as the //effective timeline.//
+* every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
+* these ~ExplicitPlacements are contained immediately within the Fixture, ordered by time
 * besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPort]]
 
-As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. Creating the Fixture can be seen as a preprocessing step to simplify the build process. For this reason, the process of [[(re)building the fixture|PlanningBuildFixture]] has been designed together with the [[Builder]]
+As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]].
 
 !{{red{WIP}}} Structure of the fixture
-The fixture is like a grid, where one dimension is given by the [[model ports|ModelPort]], and the other dimension extends in time. The time axis is grouped in segments of constant structure.
-A problem yet to be solved is how the fixture relates to the mulitude of top-level timelines, without generating a too fine grained segmentation.
+[<img[Structure of the Fixture|draw/Fixture1.png]]
+
+The fixture is like a grid, where one dimension is given by the [[model ports|ModelPort]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
+
+;Model Ports
+:The model ports share a single uniform and global name space: actually they're keyed by ~Pipe-ID
+:Model ports are derived as a result of the build process, as the //residuum// of all nodes not connected any further
+:Each port belongs to a specific Timeline and is associated with the [[Segmentation]] of that timeline.
+
+;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.
+:Segments 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 {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
@@ -2784,8 +2819,8 @@ These are used as token for dealing with other objects and have no identity of t
-
-
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel and denote a specific ExitNode in the render nodes network.
+
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denote a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
 A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output desitnation and some other entity actually uses this designation as a target, either directly or indirecly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 

From d1c64dd1c05c39d88350eec93fc7aed63cccac0e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 3 Dec 2010 02:28:29 +0100
Subject: [PATCH 02/13] small cleanup, replace try-catch by VERIFY_ERROR macro

---
 tests/lib/scoped-holder-test.cpp | 75 +++++++++++---------------------
 1 file changed, 26 insertions(+), 49 deletions(-)

diff --git a/tests/lib/scoped-holder-test.cpp b/tests/lib/scoped-holder-test.cpp
index 918e7416b..baa350562 100644
--- a/tests/lib/scoped-holder-test.cpp
+++ b/tests/lib/scoped-holder-test.cpp
@@ -23,7 +23,9 @@
 
 
 #include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
 #include "lib/util.hpp"
+#include "lib/error.hpp"
 
 #include "lib/scoped-holder.hpp"
 #include "testdummy.hpp"
@@ -38,6 +40,7 @@ namespace test{
   
   using ::Test;
   using util::isnil;
+  using lumiera::error::LUMIERA_ERROR_LOGIC;
   
   using std::map;
   using std::cout;
@@ -105,8 +108,8 @@ namespace test{
             
             TRACE (test, "holder at %p", &holder);
             TRACE (test, "object at %p", holder.get() );
-            TRACE (test, "size(object) = %u", sizeof(*holder));
-            TRACE (test, "size(holder) = %u", sizeof(holder));
+            TRACE (test, "size(object) = %lu", sizeof(*holder));
+            TRACE (test, "size(holder) = %lu", sizeof(holder));
           }
           ASSERT (0==checksum);
         }
@@ -157,60 +160,34 @@ namespace test{
             ASSERT (holder);
             long currSum = checksum;
             void* adr = holder.get();
-            try
-              {
-                holder2 = holder;
-                NOTREACHED ();
-              }
-            catch (lumiera::error::Logic&)
-              {
-                ASSERT (holder);
-                ASSERT (!holder2);
-                ASSERT (holder.get()==adr);
-                ASSERT (checksum==currSum);
-              }
             
-            try
-              {
-                holder = holder2;
-                NOTREACHED ();
-              }
-            catch (lumiera::error::Logic&)
-              {
-                ASSERT (holder);
-                ASSERT (!holder2);
-                ASSERT (holder.get()==adr);
-                ASSERT (checksum==currSum);
-              }
+            VERIFY_ERROR(LOGIC, holder2 = holder );
+            ASSERT (holder);
+            ASSERT (!holder2);
+            ASSERT (holder.get()==adr);
+            ASSERT (checksum==currSum);
+            
+            VERIFY_ERROR(LOGIC, holder = holder2 );
+            ASSERT (holder);
+            ASSERT (!holder2);
+            ASSERT (holder.get()==adr);
+            ASSERT (checksum==currSum);
             
             create_contained_object (holder2);
             ASSERT (holder2);
             ASSERT (checksum != currSum);
             currSum = checksum;
-            try
-              {
-                holder = holder2;
-                NOTREACHED ();
-              }
-            catch (lumiera::error::Logic&)
-              {
-                ASSERT (holder);
-                ASSERT (holder2);
-                ASSERT (holder.get()==adr);
-                ASSERT (checksum==currSum);
-              }
+
+            VERIFY_ERROR(LOGIC, holder = holder2 );
+            ASSERT (holder);
+            ASSERT (holder2);
+            ASSERT (holder.get()==adr);
+            ASSERT (checksum==currSum);
             
-            try
-              {
-                HO holder3 (holder2);
-                NOTREACHED ();
-              }
-            catch (lumiera::error::Logic&)
-              {
-                ASSERT (holder);
-                ASSERT (holder2);
-                ASSERT (checksum==currSum);
-              }
+            VERIFY_ERROR(LOGIC, HO holder3 (holder2) );
+            ASSERT (holder);
+            ASSERT (holder2);
+            ASSERT (checksum==currSum);
           }
           ASSERT (0==checksum);
         }

From 145ad6c3a51d918ebe6a101d673d7e00591d8941 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 3 Dec 2010 03:13:57 +0100
Subject: [PATCH 03/13] more (trivial) cleanup and renaming

---
 src/lib/allocationcluster.hpp                              | 2 +-
 ...scopedholdertransfer.hpp => scoped-holder-transfer.hpp} | 6 +++---
 src/lib/scoped-holder.hpp                                  | 2 +-
 src/lib/scoped-ptrvect.hpp                                 | 2 +-
 tests/lib/Makefile.am                                      | 4 ++--
 ...dertransfertest.cpp => scoped-holder-transfer-test.cpp} | 5 ++---
 tests/lib/testdummy.hpp                                    | 7 +++++++
 .../{vectortransfertest.cpp => vector-transfer-test.cpp}   | 2 +-
 8 files changed, 18 insertions(+), 12 deletions(-)
 rename src/lib/{scopedholdertransfer.hpp => scoped-holder-transfer.hpp} (97%)
 rename tests/lib/{scopedholdertransfertest.cpp => scoped-holder-transfer-test.cpp} (98%)
 rename tests/lib/{vectortransfertest.cpp => vector-transfer-test.cpp} (99%)

diff --git a/src/lib/allocationcluster.hpp b/src/lib/allocationcluster.hpp
index 304405d58..1b51e9d5c 100644
--- a/src/lib/allocationcluster.hpp
+++ b/src/lib/allocationcluster.hpp
@@ -54,7 +54,7 @@
 #include "lib/error.hpp"
 #include "lib/sync-classlock.hpp"
 #include "lib/scoped-holder.hpp"
-#include "lib/scopedholdertransfer.hpp"
+#include "lib/scoped-holder-transfer.hpp"
 
 
 
diff --git a/src/lib/scopedholdertransfer.hpp b/src/lib/scoped-holder-transfer.hpp
similarity index 97%
rename from src/lib/scopedholdertransfer.hpp
rename to src/lib/scoped-holder-transfer.hpp
index 3d600e61a..e7e78c945 100644
--- a/src/lib/scopedholdertransfer.hpp
+++ b/src/lib/scoped-holder-transfer.hpp
@@ -1,5 +1,5 @@
 /*
-  SCOPEDHOLDERVECTOR.hpp  -  using ScopedHolder within a STL vector 
+  SCOPED-HOLDER-TRANSFER.hpp  -  using ScopedHolder within a STL vector 
  
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -22,8 +22,8 @@
 
 
 
-#ifndef LIB_SCOPEDHOLDERVECTOR_H
-#define LIB_SCOPEDHOLDERVECTOR_H
+#ifndef LIB_SCOPEDHOLDER_TRANSFER_H
+#define LIB_SCOPEDHOLDER_TRANSFER_H
 
 #include "lib/error.hpp"
 #include 
diff --git a/src/lib/scoped-holder.hpp b/src/lib/scoped-holder.hpp
index 4e2c82627..83191970f 100644
--- a/src/lib/scoped-holder.hpp
+++ b/src/lib/scoped-holder.hpp
@@ -47,7 +47,7 @@
  ** friend function.  
  ** 
  ** @see scoped-holder-test.cpp
- ** @see scopedholdertransfer.hpp use in std::vector
+ ** @see scoped-holder-transfer.hpp use in std::vector
  ** @see AllocationCluster usage example
  ** @see scoped-ptrvect.hpp simple pointer-holding collection
  */
diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp
index d21e2333a..fa7515c6b 100644
--- a/src/lib/scoped-ptrvect.hpp
+++ b/src/lib/scoped-ptrvect.hpp
@@ -171,7 +171,7 @@ namespace lib {
       
       
     private:
-      /** @internal element access, including null check */
+      /** @internal element access, including range and null check */
       T*
       get (size_type i)
         {
diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am
index f8661006b..f6930f20b 100644
--- a/tests/lib/Makefile.am
+++ b/tests/lib/Makefile.am
@@ -84,7 +84,7 @@ test_lib_SOURCES =							\
 	$(testlib_srcdir)/sanitised-identifier-test.cpp			\
 	$(testlib_srcdir)/scoped-holder-test.cpp			\
 	$(testlib_srcdir)/scoped-ptrvect-test.cpp			\
-	$(testlib_srcdir)/scopedholdertransfertest.cpp			\
+	$(testlib_srcdir)/scoped-holder-transfer-test.cpp		\
 	$(testlib_srcdir)/singleton-subclass-test.cpp			\
 	$(testlib_srcdir)/singleton-test.cpp				\
 	$(testlib_srcdir)/singletontestmocktest.cpp			\
@@ -106,7 +106,7 @@ test_lib_SOURCES =							\
 	$(testlib_srcdir)/typed-allocation-manager-test.cpp		\
 	$(testlib_srcdir)/typed-counter-test.cpp			\
 	$(testlib_srcdir)/util-foreach-test.cpp				\
-	$(testlib_srcdir)/vectortransfertest.cpp			\
+	$(testlib_srcdir)/vector-transfer-test.cpp			\
 	$(testlib_srcdir)/visitingtoolconcept.cpp			\
 	$(testlib_srcdir)/visitingtoolextendedtest.cpp			\
 	$(testlib_srcdir)/visitingtooltest.cpp
diff --git a/tests/lib/scopedholdertransfertest.cpp b/tests/lib/scoped-holder-transfer-test.cpp
similarity index 98%
rename from tests/lib/scopedholdertransfertest.cpp
rename to tests/lib/scoped-holder-transfer-test.cpp
index c47cd2a76..ace319736 100644
--- a/tests/lib/scopedholdertransfertest.cpp
+++ b/tests/lib/scoped-holder-transfer-test.cpp
@@ -26,7 +26,7 @@
 #include "lib/util.hpp"
 
 #include "lib/scoped-holder.hpp"
-#include "lib/scopedholdertransfer.hpp"
+#include "lib/scoped-holder-transfer.hpp"
 #include "testdummy.hpp"
 
 #include 
@@ -68,8 +68,7 @@ namespace lib {
             if (throw_in_transfer)
               throw to.getVal();
             
-            to.setVal (from.getVal());
-            from.setVal(0);
+            swap (from,to);
           }
           
         };
diff --git a/tests/lib/testdummy.hpp b/tests/lib/testdummy.hpp
index afa810f79..605a92fc7 100644
--- a/tests/lib/testdummy.hpp
+++ b/tests/lib/testdummy.hpp
@@ -23,6 +23,7 @@
 
 
 #include 
+#include 
 
 
 namespace lib {
@@ -63,6 +64,12 @@ namespace test{
             val_ = newVal;
           }
         
+        friend void
+        swap (Dummy& dum1, Dummy& dum2)  ///< checksum neutral
+        {
+          std::swap(dum1.val_, dum2.val_);
+        }
+        
       private:
         void
         init()
diff --git a/tests/lib/vectortransfertest.cpp b/tests/lib/vector-transfer-test.cpp
similarity index 99%
rename from tests/lib/vectortransfertest.cpp
rename to tests/lib/vector-transfer-test.cpp
index f165f2ece..c011f56c4 100644
--- a/tests/lib/vectortransfertest.cpp
+++ b/tests/lib/vector-transfer-test.cpp
@@ -24,7 +24,7 @@
 
 #include "lib/test/run.hpp"
 
-#include "lib/scopedholdertransfer.hpp"
+#include "lib/scoped-holder-transfer.hpp"
 #include "testdummy.hpp"
 
 #include 

From ee56ab36a068889803da8f647c9acb8ec24404b7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 3 Dec 2010 05:13:52 +0100
Subject: [PATCH 04/13] Definition of ModelPort, model port reference and
 -table (#718)

---
 src/proc/mobject/model-port.cpp | 69 +++++++++++++++++++++++++
 src/proc/mobject/model-port.hpp | 91 +++++++++++++++++++++++++++++++++
 wiki/renderengine.html          | 37 ++++++++++++--
 3 files changed, 192 insertions(+), 5 deletions(-)
 create mode 100644 src/proc/mobject/model-port.cpp
 create mode 100644 src/proc/mobject/model-port.hpp

diff --git a/src/proc/mobject/model-port.cpp b/src/proc/mobject/model-port.cpp
new file mode 100644
index 000000000..332fe9d31
--- /dev/null
+++ b/src/proc/mobject/model-port.cpp
@@ -0,0 +1,69 @@
+/*
+  ModelPort  -  point to pull output data from the model
+ 
+  Copyright (C)         Lumiera.org
+    2010,               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 model-port.cpp 
+ ** Implementation details of model port descriptors and references.
+ ** Especially, the handling of the ModelPortTable datastructure is
+ ** kept an opaque implementation detail and confined entirely within
+ ** this translation unit.
+ ** 
+ ** TODO: comment necessary?
+ ** 
+ ** @see OutputDesignation
+ ** @see OutputMapping
+ ** @see Timeline
+ **
+ */
+
+
+#include "lib/error.hpp"
+//#include "lib/symbol.hpp"//
+#include "proc/mobject/model-port.hpp"
+
+//#include 
+//#include 
+#include 
+
+//using lumiera::query::QueryHandler;
+//using lumiera::query::removeTerm;
+//using lumiera::query::extractID;
+//using lumiera::ConfigRules;
+
+//using lumiera::Symbol;
+
+namespace mobject {
+  
+  
+  /**
+   * TODO type comment
+   */
+  class ModelPortTable
+    : boost::noncopyable
+    {
+      
+    public:
+    };
+  
+  
+  
+} // namespace mobject
diff --git a/src/proc/mobject/model-port.hpp b/src/proc/mobject/model-port.hpp
new file mode 100644
index 000000000..634d1e0dc
--- /dev/null
+++ b/src/proc/mobject/model-port.hpp
@@ -0,0 +1,91 @@
+/*
+  MODEL-PORT.hpp  -  point to pull output data from the model
+ 
+  Copyright (C)         Lumiera.org
+    2010,               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 model-port.hpp
+ ** Organising the output data calculation possibilities.
+ ** Model ports are conceptual entities, denoting the points where output might
+ ** possibly be produced. There is an actual representation, a collection of small
+ ** descriptor objects managed by the Fixture and organised within the ModelPortTable
+ ** datastructure. Thus, while the actual ModelPort descriptor entities are located
+ ** within and managed by the Fixture -- model port as a concept spans the high-level
+ ** and low-level view. A model port can be associated both to a pipe within a timeline
+ ** in the HighLevelModel, as well as to denote a set of corresponding exit nodes within
+ ** the segments of the render nodes network. Model ports are keyed by Pipe-ID and thus
+ ** are unique within the application.
+ ** 
+ ** A model port is rather derived than configured; it emerges during the build process
+ ** when a pipe claims an OutputDesignation and some other entity actually uses this
+ ** designation as a target, either directly or indirectly. This match of provision
+ ** and usage is detected by the Builder and produces an entry in the fixture's
+ ** ModelPortTable. Because of the 1:1 association with a pipe, each model port
+ ** has an associated StreamType.
+ ** 
+ ** Because model ports are discovered this way, dynamically during the build process,
+ ** at some point there is a transactional switch to promote the new configuration
+ ** to be come the valid current model port configuration. After that switch, model ports
+ ** are immutable.
+ ** 
+ ** Model ports are to be accessed, enumerated and grouped in various ways, because each
+ ** port belongs to a specific timeline and is used for producing data of a single
+ ** StreamType solely. But all those referrals, searching and grouping happens only
+ ** after the build process has discovered all model ports currently available.
+ ** Thus we provide a MPortRef smart-pointer to ease handling of those access
+ ** operations. The actual model port descriptors are owned and managed by
+ ** the fixture; they are bulk allocated in a similar manner than the
+ ** ProcNode and WiringDescriptor objects.
+ ** 
+ ** TODO fill in more details?
+ ** TODO where to put the ModelPortTable
+ ** 
+ ** @see OutputDesignation
+ ** @see OutputMapping
+ ** @see Timeline
+ */
+
+
+#ifndef PROC_MOBJECT_MODEL_PORT_H
+#define PROC_MOBJECT_MODEL_PORT_H
+
+#include "proc/asset/pipe.hpp"
+//#include "lib/opaque-holder.hpp"
+//#include "lib/meta/typelist-util.hpp"
+
+//extern "C" {
+//#include "lib/luid.h"
+//}
+
+namespace mobject {
+  
+  /**
+   * TODO type comment
+   */
+  class ModelPort
+    : boost::noncopyable
+    {
+      
+    public:
+    };
+  
+  
+  
+} // namespace mobject
+#endif
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index f2dd7e790..9a0da012c 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1786,7 +1786,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
 
-
+
a specially configured view -- joining together high-level and low-level model
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
@@ -1799,7 +1799,7 @@ As the builder and thus render engine //only consults the fixture,// while all e
 !{{red{WIP}}} Structure of the fixture
 [<img[Structure of the Fixture|draw/Fixture1.png]]
 
-The fixture is like a grid, where one dimension is given by the [[model ports|ModelPort]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
+The fixture is like a grid, where one dimension is given by the [[model ports|ModelPortTable]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
 
 ;Model Ports
 :The model ports share a single uniform and global name space: actually they're keyed by ~Pipe-ID
@@ -2819,12 +2819,39 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denote a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
-A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output desitnation and some other entity actually uses this designation as a target, either directly or indirecly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
+A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
-Model ports are the effective, resulting ouputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+
+!formal specification
+Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being denoted by a ~Pipe-ID.
+
+Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are bound to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
+
+
+
+
+
Model ports are conceptual entities, denoting the points where output might possibly be produced &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.
+
+!supported operations
+* get the model port by ~Pipe-ID
+* a collection of model ports per timeline
+* sub-grouping by media kind, possibly even by StreamType
+* possibility to enumerate model ports in distinct //order,// defined within the timeline
+* with the additional possibility to filter by media kind or suitable stream type.
+* a way to express the fact of //not having a model port.//
+
+!!!mutating and rebuilding
+Model ports are added once and never changed. The corresponding timeline and pipe is known at setup time, but the overall number of model ports is determined only as a result of completing the build process. At that point, the newly built configuration is swapped in transactionally to become the current configuration.
+
+!Implementation considerations
+The transactional switch creates a clear partitioning in the lifespan of the model port table. //Before// that point, entries are just added, but not accessed in any way. //After// that point, no further mutation occurs, but lookup is frequent and happens in a variety of different configurations and transient orderings.
+
+This observation leads to the idea of using //model port references// to provide all kinds of access, searching and reordering. These encapsulate the actual access by silently assuming reference to "the" global current model port configuration. Thus, the actual model port descriptors could be bulk allocated in a similar manner as the processing nodes and wiring descriptors. Access to stale model ports could be detected by the port references, allowing also for a {{{bool}}} checkable "has no port" information.
 
From 8802beb753a7b2191dd9f8cc9f0055dc3e037208 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 9 Dec 2010 22:43:32 +0100 Subject: [PATCH 05/13] rereading (and rewrapping) the builder/engine code... started this a long time ago, then got sidelined.... --- src/proc/engine/renderengine.cpp | 10 +++++----- src/proc/engine/renderengine.hpp | 21 ++++++++++++++------- src/proc/engine/rendergraph.cpp | 12 ++++++------ src/proc/engine/rendergraph.hpp | 22 ++++++++++++---------- src/proc/mobject/builder/buildertool.hpp | 22 +++++++++++++++------- src/proc/mobject/builderfacade.cpp | 10 +++++----- src/proc/mobject/builderfacade.hpp | 12 +++++++----- 7 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/proc/engine/renderengine.cpp b/src/proc/engine/renderengine.cpp index 25eb6257d..73af98a72 100644 --- a/src/proc/engine/renderengine.cpp +++ b/src/proc/engine/renderengine.cpp @@ -24,10 +24,10 @@ #include "proc/engine/renderengine.hpp" namespace engine { - - + + /** */ - - - + + + } // namespace engine diff --git a/src/proc/engine/renderengine.hpp b/src/proc/engine/renderengine.hpp index 1c361c739..b64530ed4 100644 --- a/src/proc/engine/renderengine.hpp +++ b/src/proc/engine/renderengine.hpp @@ -32,19 +32,26 @@ using std::list; -namespace engine - { - - +namespace engine { + + + /** + * @todo this is planned to become the frontend + * to the render node network, which can be considered + * at the lower end of the middle layer; the actual + * render operations are mostly implemented by the backend + * ////////TODO WIP as of 12/2010 + */ class RenderEngine : public RenderGraph { public: ///// TODO: find out about the public operations - // note: the play controller lives in the backend - + // note: the play controller lives in the proc-layer, + // but is a subsystem separate of the sesison. + private: list renderSegments; - + }; } // namespace engine diff --git a/src/proc/engine/rendergraph.cpp b/src/proc/engine/rendergraph.cpp index c2ab6e332..0900dde06 100644 --- a/src/proc/engine/rendergraph.cpp +++ b/src/proc/engine/rendergraph.cpp @@ -26,17 +26,17 @@ #include "proc/state.hpp" namespace lumiera { - + /** storage for the unique node-ID counter */ ulong NodeID::currID (0); } namespace engine { - + /** */ - - - - + + + + } // namespace engine diff --git a/src/proc/engine/rendergraph.hpp b/src/proc/engine/rendergraph.hpp index 6fdb28217..c994737f5 100644 --- a/src/proc/engine/rendergraph.hpp +++ b/src/proc/engine/rendergraph.hpp @@ -29,25 +29,27 @@ -namespace engine - { - +namespace engine { + class ExitNode; - + + /** + * @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010 + */ class RenderGraph { protected: ExitNode * output; - + /** begin of the timerange covered by this RenderGraph */ lumiera::Time start; - + /**end (exclusive) of the timerange */ lumiera::Time end; - + }; - - - + + + } // namespace engine #endif diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index 93086ac4c..2d919a70a 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -62,7 +62,7 @@ namespace mobject { - + class Buildable; namespace builder { @@ -105,7 +105,7 @@ namespace mobject { * as we simply store a pointer within the BuilderTool instance. */ class BuilderTool - : public lumiera::visitor::Tool + : public lumiera::visitor::Tool { lumiera::WrapperPtr currentWrapper_; @@ -172,7 +172,9 @@ namespace mobject { using lumiera::typelist::Types; // convenience for the users of "Applicable" - } // namespace mobject::builder + }// namespace mobject::builder + + @@ -185,8 +187,15 @@ namespace mobject { - namespace builder { // to be found by ADL + namespace builder { + /** to be picked up by ADL: redirect tool invocation for double dispatch. + * The purpose of this function is to apply a visitor, while the actual target + * is managed by a generic wrapper (smart-ptr). This template function serves + * to generate forwarding functions, which pass on the \c apply() call to the + * actual embedded target, while passing on the fully wrapped object for later + * referral and usage too. + */ template inline Buildable::ReturnType apply (BuilderTool& tool, WRA& wrappedTargetObj) @@ -195,8 +204,7 @@ namespace mobject { wrappedTargetObj->apply (tool); // dispatch to suitable treat() function tool.forgetWrapper(); } - - } // namespace mobject::builder -} // namespace mobject + +}} // namespace mobject::builder #endif diff --git a/src/proc/mobject/builderfacade.cpp b/src/proc/mobject/builderfacade.cpp index 175aa2b24..99eb87ed9 100644 --- a/src/proc/mobject/builderfacade.cpp +++ b/src/proc/mobject/builderfacade.cpp @@ -30,8 +30,8 @@ namespace mobject { using ::NOBUG_FLAG(memory); NOBUG_CPP_DEFINE_FLAG_PARENT(buildermem, memory); } - - + + /** * Main Operation of the Builder: * create a render engine for a given part of the timeline @@ -41,7 +41,7 @@ namespace mobject { { //////////////////////TODO } - - - + + + } // namespace mobject diff --git a/src/proc/mobject/builderfacade.hpp b/src/proc/mobject/builderfacade.hpp index 98197ec13..86520d483 100644 --- a/src/proc/mobject/builderfacade.hpp +++ b/src/proc/mobject/builderfacade.hpp @@ -30,8 +30,8 @@ namespace mobject { - - + + /** * Provides unified access to the builder functionality. * While individual components of the builder subsystem may be called @@ -44,12 +44,14 @@ namespace mobject { /** * Main Operation of the Builder: * create a render engine for a given part of the timeline + * @deprecated thats a placeholder! the real invocation is now + * in the course of being worked out ////////////TODO 12/2010 */ engine::RenderEngine & buildEngine () ; // TODO: allocation, GC?????? }; - - - + + + } // namespace mobject #endif From 28279613858fbdd4287567e79765978980b11804 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 10 Dec 2010 01:27:17 +0100 Subject: [PATCH 06/13] WIP: test driven brainstorming about model port registry --- .../model-port-registry.cpp} | 8 +- .../mobject/builder/model-port-registry.hpp | 72 ++++++ .../builder/model-port-registry-test.cpp | 212 ++++++++++++++++++ wiki/renderengine.html | 14 +- 4 files changed, 297 insertions(+), 9 deletions(-) rename src/proc/mobject/{model-port.cpp => builder/model-port-registry.cpp} (89%) create mode 100644 src/proc/mobject/builder/model-port-registry.hpp create mode 100644 tests/components/proc/mobject/builder/model-port-registry-test.cpp diff --git a/src/proc/mobject/model-port.cpp b/src/proc/mobject/builder/model-port-registry.cpp similarity index 89% rename from src/proc/mobject/model-port.cpp rename to src/proc/mobject/builder/model-port-registry.cpp index 332fe9d31..87a26492e 100644 --- a/src/proc/mobject/model-port.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -1,5 +1,5 @@ /* - ModelPort - point to pull output data from the model + ModelPortRegistry - creating and organising and accessing model ports Copyright (C) Lumiera.org 2010, Hermann Vosseler @@ -21,7 +21,7 @@ * *****************************************************/ -/** @file model-port.cpp +/** @file model-port-registry.cpp ** Implementation details of model port descriptors and references. ** Especially, the handling of the ModelPortTable datastructure is ** kept an opaque implementation detail and confined entirely within @@ -39,6 +39,7 @@ #include "lib/error.hpp" //#include "lib/symbol.hpp"// #include "proc/mobject/model-port.hpp" +#include "proc/mobject/builder/model-port-registry.hpp" //#include //#include @@ -52,6 +53,7 @@ //using lumiera::Symbol; namespace mobject { +namespace builder { /** @@ -66,4 +68,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp new file mode 100644 index 000000000..2ef87f126 --- /dev/null +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -0,0 +1,72 @@ +/* + MODEL-PORT-REGISTRY.hpp - creating and organising and accessing model ports + + Copyright (C) Lumiera.org + 2010, 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 model-port-registry.hpp + ** Mutation and management facility for model ports within the builder. + ** Model ports denote the points where output might possibly be produced. + ** While client code accesses model ports only as immutable descriptors handled + ** through an (opaque) reference, the builder is in charge of detecting and organising + ** any (new) model ports arising as the result of the build process. Changes to the set + ** of current model ports are to be activated with an atomic transactional switch. + ** + ** builder::ModelPortRegistry thus acts as management interface and factory for model ports. + ** A given instance of this registry can be promoted to be "the" model port registry reflecting + ** the current active model ports. Within the Lumiera application, the builder subsystem cares + ** for setting up such a registry, while all other parts of the system just access the current + ** model ports through the mobject::ModelPort frontend. + ** + ** TODO fill in more details? + ** + ** @see ModelPort + ** @see OutputDesignation + ** @see ModelPortRegistry_test + */ + + +#ifndef PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H +#define PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H + +#include "proc/asset/pipe.hpp" +//#include "lib/opaque-holder.hpp" +//#include "lib/meta/typelist-util.hpp" + +//extern "C" { +//#include "lib/luid.h" +//} + +namespace mobject { +namespace builder { + + /** + * TODO type comment + */ + class ModelPortRegistry + : boost::noncopyable + { + + public: + }; + + + +}} // namespace mobject::builder +#endif diff --git a/tests/components/proc/mobject/builder/model-port-registry-test.cpp b/tests/components/proc/mobject/builder/model-port-registry-test.cpp new file mode 100644 index 000000000..47f425476 --- /dev/null +++ b/tests/components/proc/mobject/builder/model-port-registry-test.cpp @@ -0,0 +1,212 @@ +/* + ModelPortRegistry(Test) - verify handling of model ports + + Copyright (C) Lumiera.org + 2010, 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. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "proc/mobject/builder/model-port-registry.hpp" +#include "proc/asset/pipe.hpp" +#include "lib/util.hpp" + +//#include +//#include +#include + +//using boost::format; +//using boost::scoped_ptr; +using util::isSameObject; +using util::isnil; +using std::string; + + +namespace mobject { +namespace builder { +namespace test { + + using asset::Pipe; + using asset::PPipe; + + //typedef asset::ID PID; + namespace { // test environment + + struct TestContext + { + ModelPortRegistry registry_; + ModelPortRegistry& previous_; + + /** setup */ + TestContext() + : registry_() + , previous_(ModelPortRegistry::setActiveInstance (registry_)) + { } + + /** tear-down */ + ~TestContext() + { + ModelPortRegistry::setActiveInstance (previous_); + } + }; + + } + + + /********************************************************************************* + * @test create a standalone model port registry to verify the behaviour of + * model ports, accessed through reference handles. This test provides + * an example setup detached from the real usage situation within the builder. + * The ModelPortRegistry management interface is used to create and track a + * set of model ports, to be made visible by an atomic, transactional switch. + * The access for client code through the ModelPort frontend is then verified. + * + * @see mobject::ModelPort + * @see mobject::builder::ModelPortRegistry + */ + class ModelPortRegistry_test : public Test + { + + virtual void + run (Arg) + { + TestContext ctx; + + fabricating_ModelPorts (ctx); + accessing_ModelPorts(); + transactionalSwitch (ctx); + } + + + void + fabricating_ModelPorts (ModelPortRegistry& registry) + { + ModelPortDescriptor& p1 = registry.definePort (pipeA, someTimeline); + ModelPortDescriptor& p2 = registry.definePort (pipeB, someTimeline); + + CHECK (p1); + CHECK (p2); + + VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) ); + CHECK (p2); + + CHECK (p1.getID() == pipeA); + CHECK (p2.getID() == pipeB); + CHECK (p1.getPipe() == pipeA); + CHECK (p2.getPipe() == pipeB); + CHECK (p1.getTimeline() == someTimeline); + CHECK (p2.getTimeline() == someTimeline); + + registry.commit(); + } + + + void + accessing_ModelPorts () + { + ModelPort mp1(pipeA); + ModelPort mp2(pipeB); + + VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort(pipeWC)); + + ModelPort mp1x(pipeA); + ModelPort mpNull; + + CHECK (mp1); + CHECK (mp2); + CHECK (mp1x); + CHECK (!mpNull); + + CHECK ( ModelPort::exists (pipeA)); + CHECK ( ModelPort::exists (pipeB)); + CHECK (!ModelPort::exists (pipeWC)); + + CHECK (mp1 == mp1x); + CHECK (!isSameObject (mp1, mp1x)); + CHECK (mp1 != mp2); + CHECK (mp2 != mp1); + CHECK (mp1 != mpNull); + CHECK (mp2 != mpNull); + + CHECK (mp1.pipe() == pipeA); + CHECK (mp2.pipe() == pipeB); + CHECK (mp1x.pipe() == pipeA); + VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); + + CHECK (mp1.streamType() == pipeA.getStreamType()); + } + + + void + transactionalSwitch (ModelPortRegistry& registry) + { + CHECK ( ModelPort::exists (pipeB)); + CHECK (!ModelPort::exists (pipeWC)); + + CHECK (ModelPort::exists (pipeA)); + CHECK (registry.contains (pipeA)); + registry.remove (pipeA); + CHECK (ModelPort::exists (pipeA)); + CHECK (!registry.contains (pipeA)); + + ModelPortDescriptor& p1 = registry.definePort (pipeA, anotherTimeline); + CHECK (registry.contains (pipeA)); + CHECK (p1.getTimeline() == anotherTimeline); + CHECK (ModelPort(pipeA).timeline() != anotherTimeline); + + registry.remove (pipeB); + registry.definePort (pipeWC,anotherTimeline); + CHECK (!registry.contains (pipeB)); + CHECK ( registry.contains (pipeWC)); + CHECK ( ModelPort::exists (pipeB)); + CHECK (!ModelPort::exists (pipeWC)); + + ModelPort portA(pipeA); + ModelPort portB(pipeB); + VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort(pipeWC)); + CHECK (portA); + CHECK (portB); + CHECK (portA.pipe() == pipeA); + CHECK (portB.pipe() == pipeB); + CHECK (portA.timeline() != anotherTimeline); + + registry.commit(); + CHECK ( ModelPort::exists (pipeA)); + CHECK (!ModelPort::exists (pipeB)); + CHECK ( ModelPort::exists (pipeWC)); + CHECK ( portA); + CHECK (!portB); + CHECK (portA.timeline() == anotherTimeline); + CHECK (portA.pipe() == pipeA); + VERIFY_ERROR (UNCONNECTED_MODEL_PORT, portB.pipe()); + + ModelPort pwc(pipeWC); + CHECK (pwc); + CHECK (pwc.pipe() == pipeWC); + CHECK (pwc.timeline() == anotherTimeline); + } + }; + + + /** Register this test class... */ + LAUNCHER (ModelPortRegistry_test, "unit session builder"); + + + +}}} // namespace mobject::builder::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 9a0da012c..09e2a5006 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1786,20 +1786,20 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
-
+
a specially configured view -- joining together high-level and low-level model
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
 * every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
 * these ~ExplicitPlacements are contained immediately within the Fixture, ordered by time
-* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPort]]
+* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]]
 
 As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]].
 
 !{{red{WIP}}} Structure of the fixture
 [<img[Structure of the Fixture|draw/Fixture1.png]]
 
-The fixture is like a grid, where one dimension is given by the [[model ports|ModelPortTable]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
+The fixture is like a grid, where one dimension is given by the [[model ports|ModelPortRegistry]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
 
 ;Model Ports
 :The model ports share a single uniform and global name space: actually they're keyed by ~Pipe-ID
@@ -2819,8 +2819,8 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
-
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denote a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
+
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
 A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
@@ -2833,7 +2833,7 @@ Model ports are represented by small non-copyable descriptor objects with distin
 
 
-
+
Model ports are conceptual entities, denoting the points where output might possibly be produced &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.
 
@@ -2852,6 +2852,8 @@ Model ports are added once and never changed. The corresponding timeline and pip
 The transactional switch creates a clear partitioning in the lifespan of the model port table. //Before// that point, entries are just added, but not accessed in any way. //After// that point, no further mutation occurs, but lookup is frequent and happens in a variety of different configurations and transient orderings.
 
 This observation leads to the idea of using //model port references// to provide all kinds of access, searching and reordering. These encapsulate the actual access by silently assuming reference to "the" global current model port configuration. Thus, the actual model port descriptors could be bulk allocated in a similar manner as the processing nodes and wiring descriptors. Access to stale model ports could be detected by the port references, allowing also for a {{{bool}}} checkable "has no port" information.
+
+A model port registry, maintained by the builder, is responsible for storing the discovered model ports within a model port table, which is then swapped in after completing the build process. The {{{builder::ModelPortRegistry}}} acts as management interface, while client code accesses just the {{{ModelPort}}} frontend. A link to the actual registry instance is hooked into that frontend when bringing up the builder subsystem.
 
From 9c86deb18cd5f51f95a146309191d434f1f7c967 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 10 Dec 2010 15:29:43 +0100 Subject: [PATCH 07/13] augment asset ID: automatic conversion, typed NIL ID constants --- src/proc/asset.hpp | 102 ++++++++++++++++++++++------------------ src/proc/asset/pipe.hpp | 14 ++++-- 2 files changed, 66 insertions(+), 50 deletions(-) diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index def4a02ee..80e5f2aa7 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -108,6 +108,8 @@ namespace asset { ID (HashVal id) : hash(id) {} ID (const KIND& asset) : hash(asset.getID()) {} operator HashVal() const { return hash; } + + static ID INVALID; }; class DB; @@ -303,53 +305,59 @@ namespace asset { - /* ====== ordering of Assets and Asset-Pointers ====== */ - - /** ordering of Assets is based on the ordering - * of Ident tuples, which are supposed to be unique. - * By using our customised lumiera::P as smart ptr, - * comparison on P ptrs will be automatically - * forwarded to the Asset comparison operators. - * @note version info is irrelevant */ - inline int - Asset::Ident::compare (Asset::Ident const& oi) const - { - int res; - if (0 != (res=category.compare (oi.category))) return res; - if (0 != (res=org.compare (oi.org))) return res; - return name.compare (oi.name); - } - - - /** promote subtype-ptr to PAsset, e.g. for comparing */ - template - inline const PcAsset - pAsset (shared_ptr const& subPtr) - { - return static_pointer_cast (subPtr); - } - - - /** type trait for detecting a shared-ptr-to-asset */ - template - struct is_pAsset : boost::false_type {}; - - template - struct is_pAsset > - : boost::is_base_of {}; - - - /** convenient for debugging */ - inline string str (PcAsset const& a) - { - if (a) - return string (*a.get()); - else - return "Asset(NULL)"; - } - - - + /* ====== ordering of Assets and Asset-Pointers ====== */ + + /** ordering of Assets is based on the ordering + * of Ident tuples, which are supposed to be unique. + * By using our customised lumiera::P as smart ptr, + * comparison on P ptrs will be automatically + * forwarded to the Asset comparison operators. + * @note version info is irrelevant */ + inline int + Asset::Ident::compare (Asset::Ident const& oi) const + { + int res; + if (0 != (res=category.compare (oi.category))) return res; + if (0 != (res=org.compare (oi.org))) return res; + return name.compare (oi.name); + } + + + /** promote subtype-ptr to PAsset, e.g. for comparing */ + template + inline const PcAsset + pAsset (shared_ptr const& subPtr) + { + return static_pointer_cast (subPtr); + } + + + /** type trait for detecting a shared-ptr-to-asset */ + template + struct is_pAsset : boost::false_type {}; + + template + struct is_pAsset > + : boost::is_base_of {}; + + + /** marker constant denoting a NIL asset */ + template + ID ID::INVALID = ID(0); + + + /** convenient for debugging */ + inline string str (PcAsset const& a) + { + if (a) + return string (*a.get()); + else + return "Asset(NULL)"; + } + + + + } // namespace asset diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index 922dec444..35c64664d 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -45,7 +45,13 @@ namespace asset { { public: ID (HashVal id); - ID (const Pipe&); + ID (Pipe const&); + ID (PPipe const&); + + /** allows a Pipe-ID to stand in for a full Pipe Asset + * @throw error::Invalid when there is no corresponding Pipe + */ + operator PPipe() const; }; @@ -95,8 +101,10 @@ namespace asset { // catch up with postponed definition of ID ctors... // - inline ID::ID(HashVal id) : ID (id) {}; - inline ID::ID(Pipe const& pipe) : ID (pipe.getID()) {}; + inline ID::ID(HashVal id) : ID (id) {}; + inline ID::ID(Pipe const& pipe) : ID (pipe.getID()) {}; + inline ID::ID(PPipe const& pipe) : ID (pipe->getID()) {}; + inline ID::operator PPipe() const { return Pipe::lookup(*this); } From 7043db90eeffaf7b4bc602ca1c97d58996d08219 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 10 Dec 2010 17:39:39 +0100 Subject: [PATCH 08/13] introduce an explicit StreamType::ID --- src/lib/query.hpp | 4 +-- src/lib/streamtype.hpp | 9 ++++--- src/proc/asset/entry-id.hpp | 2 +- src/proc/asset/pipe.cpp | 2 +- src/proc/asset/pipe.hpp | 25 +++++++++++++++---- src/proc/asset/struct.cpp | 2 +- src/proc/control/stypemanager.cpp | 8 ++++++ src/proc/control/stypemanager.hpp | 2 ++ tests/components/proc/asset/basicpipetest.cpp | 12 ++++----- .../proc/mobject/output-mapping-test.cpp | 2 +- .../mobject/session/defsmanagerimpltest.cpp | 7 ++++-- .../proc/mobject/session/defsmanagertest.cpp | 6 +++-- 12 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/lib/query.hpp b/src/lib/query.hpp index 283292b24..2e3186cc6 100644 --- a/src/lib/query.hpp +++ b/src/lib/query.hpp @@ -28,7 +28,7 @@ #include #include -#include +#include /////////////////////////////////////////TICKET #166 Oh RLY ... need to do away with this #include "lib/symbol.hpp" @@ -64,7 +64,7 @@ namespace lumiera { { public: explicit Query (string const& predicate="") : string(predicate) {} - explicit Query (format& pattern) : string(str(pattern)) {} +// explicit Query (format& pattern) : string(str(pattern)) {} //////////////TICKET #166 outch... that needs to disappear const string asKey() const { diff --git a/src/lib/streamtype.hpp b/src/lib/streamtype.hpp index 6dc5747fc..cf45ad59c 100644 --- a/src/lib/streamtype.hpp +++ b/src/lib/streamtype.hpp @@ -37,6 +37,7 @@ #include "lib/symbol.hpp" #include "lib/query.hpp" +#include "proc/asset/entry-id.hpp" #include @@ -47,7 +48,7 @@ namespace lumiera { /** - * + * TODO write type comment */ struct StreamType : boost::noncopyable { @@ -72,6 +73,8 @@ namespace lumiera { class ImplFacade; class ImplConstraint; + typedef asset::EntryID ID; + Prototype const& prototype; ImplFacade * implType; /////////////TODO: really by ptr??? @@ -149,9 +152,9 @@ namespace lumiera { * and use it to create a new framebuffer */ virtual DataBuffer* createFrame () const =0; - /** similarily create a impl type which complies to this constraint + /** Similarly create a impl type which complies to this constraint * as well as to the additional constraints (e.g. frame size). - * Create a new framebuffer of the resutling type */ + * Create a new frame buffer of the resulting type */ virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const =0; //TODO: do we need functions to represent and describe this constraint? diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index 3f736892e..b16739a02 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -241,7 +241,7 @@ namespace asset { } friend ostream& operator<< (ostream& os, EntryID const& id) { return os << string(id); } - friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); } + friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); } }; diff --git a/src/proc/asset/pipe.cpp b/src/proc/asset/pipe.cpp index 2188a8138..8e823313a 100644 --- a/src/proc/asset/pipe.cpp +++ b/src/proc/asset/pipe.cpp @@ -38,7 +38,7 @@ namespace asset { * default wiring. */ Pipe::Pipe ( const Asset::Ident& idi - , string const& streamID + , string const& streamID ////////////////////////////////////////TICKET #648 , PProcPatt& wiring , string shortName , string longName diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index 35c64664d..aed616264 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -26,6 +26,7 @@ #include "proc/asset/struct.hpp" #include "proc/asset/procpatt.hpp" +#include "lib/streamtype.hpp" #include @@ -34,6 +35,7 @@ namespace asset { using lumiera::P; + using lumiera::StreamType; using std::string; class Pipe; @@ -49,9 +51,12 @@ namespace asset { ID (PPipe const&); /** allows a Pipe-ID to stand in for a full Pipe Asset - * @throw error::Invalid when there is no corresponding Pipe - */ + * @throw error::Invalid when there is no corresponding Pipe */ operator PPipe() const; + + /** allows to fetch the StreamType directly just from a Pipe-ID + * @throw error::Invalid when there is no corresponding Pipe */ + StreamType::ID streamType() const; }; @@ -65,7 +70,7 @@ namespace asset { : public Struct { PProcPatt wiringTemplate_; - const string streamID_; ///< @todo just a placeholder for now 10/10 + StreamType::ID streamID_; ////////////////////////////////////////TICKET #648 public: string shortDesc; @@ -84,7 +89,7 @@ namespace asset { public: string const& getPipeID() const { return ident.name; } - string const& getStreamID() const { return streamID_; } + StreamType::ID getStreamID() const { return streamID_; } ////////////////////////////////////////TICKET #648 PProcPatt const& getProcPatt() const { return wiringTemplate_; } @@ -104,8 +109,18 @@ namespace asset { inline ID::ID(HashVal id) : ID (id) {}; inline ID::ID(Pipe const& pipe) : ID (pipe.getID()) {}; inline ID::ID(PPipe const& pipe) : ID (pipe->getID()) {}; - inline ID::operator PPipe() const { return Pipe::lookup(*this); } + inline + ID::operator PPipe() const + { + return Pipe::lookup(*this); + } + + inline StreamType::ID + ID::streamType() const + { + return Pipe::lookup(*this)->getStreamID(); + } diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp index 9145c2ad8..70f893149 100644 --- a/src/proc/asset/struct.cpp +++ b/src/proc/asset/struct.cpp @@ -148,7 +148,7 @@ namespace asset { normaliseID (pipeID); normaliseID (streamID); static format descriptor("pipe(%s), stream(%s)."); - Pipe* pP = impl_->fabricate (Query (descriptor % pipeID % streamID)); + Pipe* pP = impl_->fabricate (Query (str(descriptor % pipeID % streamID))); return AssetManager::instance().wrap (*pP); } diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp index 10131f130..4ba2fda19 100644 --- a/src/proc/control/stypemanager.cpp +++ b/src/proc/control/stypemanager.cpp @@ -79,6 +79,14 @@ namespace control { } + /** */ + StreamType const& + STypeManager::getType (StreamType::ID stID) + { + UNIMPLEMENTED ("get type just by symbolic ID (query defaults manager)"); + } + + StreamType const& STypeManager::getType (StreamType::Prototype const& protoType) { diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index 70003eef4..cd6a311df 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -53,6 +53,8 @@ namespace control { * just a symbolic ID. Effectively this queries a default */ StreamType const& getType (Symbol sTypeID) ; + StreamType const& getType (StreamType::ID stID) ; + /** build or retrieve a complete StreamType implementing the given Prototype */ StreamType const& getType (StreamType::Prototype const& protoType) ; diff --git a/tests/components/proc/asset/basicpipetest.cpp b/tests/components/proc/asset/basicpipetest.cpp index 70f644ed1..e3491cba4 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -42,13 +42,13 @@ using std::string; using std::cout; -namespace asset - { - namespace test - { +namespace asset { +namespace test { + using mobject::Session; using lumiera::Query; using lumiera::query::normaliseID; + using lumiera::StreamType; @@ -88,7 +88,7 @@ namespace asset ASSERT (thePipe); ASSERT (thePipe->getProcPatt()); ASSERT (thePipe->getPipeID() == pID_sane); - ASSERT (thePipe->getStreamID() == sID); + ASSERT (thePipe->getStreamID() == StreamType::ID(sID)); ASSERT (thePipe->shortDesc == pID_sane); Asset::Ident idi = thePipe->ident; @@ -147,7 +147,7 @@ namespace asset string sID = pipe1->getStreamID(); // sort of a "default stream type" PPipe pipe3 = Pipe::query ("stream("+sID+")"); ASSERT (pipe3); - ASSERT (pipe3->getStreamID() == sID); + ASSERT (pipe3->getStreamID() == StreamType::ID(sID)); ASSERT (pipe3->getProcPatt() == Session::current->defaults (Query("stream("+sID+")"))); } diff --git a/tests/components/proc/mobject/output-mapping-test.cpp b/tests/components/proc/mobject/output-mapping-test.cpp index db6f36ee1..5f4a5be1a 100644 --- a/tests/components/proc/mobject/output-mapping-test.cpp +++ b/tests/components/proc/mobject/output-mapping-test.cpp @@ -76,7 +76,7 @@ namespace test { { PPipe srcP = Pipe::lookup (sourcePipeID); format queryPattern ("id(master_%1%), stream(%1%), ord(%2%)"); - return Query (queryPattern % srcP->getStreamID() % seqNr); + return Query (str(queryPattern % srcP->getStreamID() % seqNr)); } }; diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp index 954730804..44a2184cd 100644 --- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp +++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp @@ -31,6 +31,7 @@ #include "common/configrules.hpp" #include "proc/assetmanager.hpp" #include "proc/mobject/session.hpp" +#include "lib/streamtype.hpp" #include @@ -49,6 +50,7 @@ namespace asset { using lumiera::ConfigRules; using lumiera::query::QueryHandler; + using lumiera::StreamType; @@ -94,6 +96,7 @@ namespace asset { define_and_search () { string sID = newID ("stream"); + StreamType::ID stID (sID); // create Pipes explicitly // (without utilising default queries) @@ -101,7 +104,7 @@ namespace asset { PPipe pipe2 = Struct::retrieve.newPipe (newID("pipe"), sID ); ASSERT (pipe1 != pipe2); - ASSERT (sID == pipe2->getStreamID()); + ASSERT (stID == pipe2->getStreamID()); ASSERT (!find (pipe1->getPipeID()), "accidental clash of random test-IDs"); ASSERT (!find (pipe2->getPipeID()), "accidental clash of random test-IDs"); @@ -116,7 +119,7 @@ lumiera::query::setFakeBypass("stream("+sID+")"); ////////////////////////////// ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default"); ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default"); - ASSERT (sID != pipe1->getStreamID(), "accidental clash"); + ASSERT (stID != pipe1->getStreamID(), "accidental clash"); ASSERT (!Session::current->defaults.define (pipe1, Query ("stream("+sID+")"))); // can't be registered with this query, due to failure caused by wrong stream-ID } diff --git a/tests/components/proc/mobject/session/defsmanagertest.cpp b/tests/components/proc/mobject/session/defsmanagertest.cpp index 608dce6db..8b67522c9 100644 --- a/tests/components/proc/mobject/session/defsmanagertest.cpp +++ b/tests/components/proc/mobject/session/defsmanagertest.cpp @@ -32,6 +32,7 @@ #include "proc/asset/pipe.hpp" #include "proc/assetmanager.hpp" #include "proc/mobject/session.hpp" +#include "lib/streamtype.hpp" #include @@ -49,6 +50,7 @@ namespace test { using mobject::Session; using lumiera::Query; using lumiera::query::normaliseID; + using lumiera::StreamType; /** shortcut: run just a query @@ -117,7 +119,7 @@ namespace test { retrieveConstrainedDefault (string pID, string sID) { PPipe pipe1 = Pipe::query (""); // "the default pipe" - ASSERT (sID != pipe1->getStreamID(), + ASSERT ( pipe1->getStreamID() != StreamType::ID(sID), "stream-ID \"%s\" not suitable for test, because " "the default-pipe \"%s\" happens to have the same " "stream-ID. We need it to be different", @@ -126,7 +128,7 @@ namespace test { string query_for_sID ("stream("+sID+")"); PPipe pipe2 = Pipe::query (query_for_sID); - ASSERT (sID == pipe2->getStreamID()); + ASSERT (pipe2->getStreamID() == StreamType::ID(sID)); ASSERT (pipe2 != pipe1); ASSERT (pipe2 == Pipe::query (query_for_sID)); // reproducible } From 0464ca965c3ee85157d8236146c6bc4fcda974e4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 10 Dec 2010 18:12:56 +0100 Subject: [PATCH 09/13] fill in ModelPort and ModelPortRegistry definition stubs making test pass the compiler --- .../mobject/builder/model-port-registry.cpp | 181 ++++++++++++++++-- .../mobject/builder/model-port-registry.hpp | 96 +++++++++- src/proc/mobject/model-port.hpp | 93 +++++++-- tests/44builder.tests | 5 + tests/components/Makefile.am | 1 + .../builder/model-port-registry-test.cpp | 107 ++++++++--- 6 files changed, 423 insertions(+), 60 deletions(-) diff --git a/src/proc/mobject/builder/model-port-registry.cpp b/src/proc/mobject/builder/model-port-registry.cpp index 87a26492e..2346c4b9f 100644 --- a/src/proc/mobject/builder/model-port-registry.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -53,19 +53,176 @@ //using lumiera::Symbol; namespace mobject { -namespace builder { - - - /** - * TODO type comment - */ - class ModelPortTable - : boost::noncopyable - { + namespace builder { + + + /** + * TODO type comment + */ + class ModelPortTable + : boost::noncopyable + { + + public: + }; + - public: - }; + typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + + inline MPDescriptor + accessDescriptor() + { + + } + + ModelPortRegistry& + ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) + { + UNIMPLEMENTED ("handling of active model port registry"); + } + + + /** */ + ModelPortRegistry& + ModelPortRegistry::globalInstance() + { + UNIMPLEMENTED ("access the globally valid registry instance"); + } + + + + /** */ + bool + ModelPortRegistry::contains (ID pID) const + { + UNIMPLEMENTED ("diagnostics querying the state of the pending transaction"); + } + + + /** @return true if the given pipe-ID actually denotes an + * existing, connected and usable model port. + * @note reflects the state of the publicly visible + * model port registry, \em not any model ports + * being registered within a currently pending + * transaction (ongoing build process). */ + bool + ModelPortRegistry::isRegistered (ID key) const + { + if (!key) return false; + + UNIMPLEMENTED ("query the publicly valid contents"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::operator[] (ID key) const + { + UNIMPLEMENTED ("access registered model port"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::accessDescriptor (ID key) + { + UNIMPLEMENTED ("access the current global registry and fetch model port"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::definePort (ID pipeA, ID element_exposing_this_port) + { + UNIMPLEMENTED ("create and register a new model port entry, within the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::remove (PID key) + { + UNIMPLEMENTED ("remove a model port entry from the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::clear() + { + UNIMPLEMENTED ("schedule removal of all registry contents into the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::commit() + { + UNIMPLEMENTED ("transactional switch for new/modified model ports"); + } + + + /** */ + void + ModelPortRegistry::rollback() + { + UNIMPLEMENTED ("discard current transaction"); + } + + + + LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port"); + + } // namespace builder -}} // namespace mobject + + LUMIERA_ERROR_DEFINE (INVALID_MODEL_PORT, "Referral to unknown model port"); + LUMIERA_ERROR_DEFINE (UNCONNECTED_MODEL_PORT, "Attempt to operate on an existing but unconnected model port"); + + + ModelPort::ModelPort (ID refID) + : id_(refID) + { + builder::ModelPortRegistry::accessDescriptor (refID); + } + + + /** */ + bool + ModelPort::exists (ID key) + { + return builder::ModelPortRegistry::globalInstance().isRegistered (key); + } + + + /** */ + ID + ModelPort::pipe() const + { + ENSURE (this->id_ == builder::ModelPortRegistry::accessDescriptor(this->id_).id); + + return builder::ModelPortRegistry::accessDescriptor(this->id_).id; + } + + + /** */ + ID + ModelPort::holder() const + { + return builder::ModelPortRegistry::accessDescriptor(this->id_).holder; + } + + + /** */ + StreamType::ID + ModelPort::streamType() const + { + return this->id_.streamType(); + } + + + + +} // namespace mobject diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index 2ef87f126..53ddc6810 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -34,8 +34,6 @@ ** for setting up such a registry, while all other parts of the system just access the current ** model ports through the mobject::ModelPort frontend. ** - ** TODO fill in more details? - ** ** @see ModelPort ** @see OutputDesignation ** @see ModelPortRegistry_test @@ -45,7 +43,11 @@ #ifndef PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H #define PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H +#include "lib/error.hpp" #include "proc/asset/pipe.hpp" +#include "proc/asset/struct.hpp" +#include "proc/mobject/model-port.hpp" + //#include "lib/opaque-holder.hpp" //#include "lib/meta/typelist-util.hpp" @@ -56,17 +58,105 @@ namespace mobject { namespace builder { + using asset::ID; + using asset::Pipe; +//using asset::PPipe; + using asset::Struct; + + LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port + + /** - * TODO type comment + * Management facility for tracking model ports. + * ModelPort handles are exposed as frontend for usage + * by client code. Model ports are discovered by the builder + * when re-creating the low-level model; during such an ongoing + * build process, newly discovered ports are accumulated within + * a transaction, which then gets committed atomically when the + * new model is complete and ready for use. */ class ModelPortRegistry : boost::noncopyable { + typedef ID PID; + typedef ID StID; + public: + /** @internal record to describe a model port */ + struct ModelPortDescriptor; + + + static ModelPortRegistry& + setActiveInstance (ModelPortRegistry& newRegistry); + + static ModelPortRegistry& + globalInstance(); + + static ModelPortDescriptor const& + accessDescriptor (PID); + + + ModelPortDescriptor const& + definePort (PID pipe, StID element_exposing_this_port); + + bool contains (PID) const; + bool isRegistered (PID) const; + + ModelPortDescriptor const& + operator[] (PID) const; + + + void remove (PID); + void clear(); + + + /** activate pending model port changes. + * Any accumulated changes and newly defined model ports + * are promoted to become part of the current active configuration + * with a single atomic (transactional) switch. + */ + void commit(); + + /** discard pending changes. + * Silently drop model port definition changes since the last commit. + */ + void rollback(); }; + /** ModelPortDescriptor records are used as actual storage + * within the model port registration table; they are never + * exposed to client code directly. + */ + struct ModelPortRegistry::ModelPortDescriptor + { + const PID id; + const StID holder; + + bool + isValid() const + { + return bool(id); + } + + + ModelPortDescriptor() + : id(PID::INVALID) + , holder(StID::INVALID) + { } + + // default copy operations permitted + + protected: + ModelPortDescriptor (PID pipe, StID element_exposing_this_port) + : id(pipe) + , holder(element_exposing_this_port) + { } + }; + + + }} // namespace mobject::builder #endif diff --git a/src/proc/mobject/model-port.hpp b/src/proc/mobject/model-port.hpp index 634d1e0dc..cf98bc149 100644 --- a/src/proc/mobject/model-port.hpp +++ b/src/proc/mobject/model-port.hpp @@ -30,7 +30,7 @@ ** and low-level view. A model port can be associated both to a pipe within a timeline ** in the HighLevelModel, as well as to denote a set of corresponding exit nodes within ** the segments of the render nodes network. Model ports are keyed by Pipe-ID and thus - ** are unique within the application. + ** are bound to be unique within the application. ** ** A model port is rather derived than configured; it emerges during the build process ** when a pipe claims an OutputDesignation and some other entity actually uses this @@ -41,21 +41,22 @@ ** ** Because model ports are discovered this way, dynamically during the build process, ** at some point there is a transactional switch to promote the new configuration - ** to be come the valid current model port configuration. After that switch, model ports + ** to become the valid current model port configuration. After that switch, model ports ** are immutable. ** ** Model ports are to be accessed, enumerated and grouped in various ways, because each ** port belongs to a specific timeline and is used for producing data of a single ** StreamType solely. But all those referrals, searching and grouping happens only ** after the build process has discovered all model ports currently available. - ** Thus we provide a MPortRef smart-pointer to ease handling of those access - ** operations. The actual model port descriptors are owned and managed by - ** the fixture; they are bulk allocated in a similar manner than the + ** Thus actually the ModelPort elements handed out to client code are just + ** smart-handles, accessing a global ModelPortRegistry behind the scenes. + ** Validity of these handles will be checked on each access. The actual + ** model port descriptors are owned and managed by the fixture; + ** they are bulk allocated in a similar manner than the ** ProcNode and WiringDescriptor objects. ** - ** TODO fill in more details? - ** TODO where to put the ModelPortTable - ** + ** @see ModelPortRegistry_test abstract usage example + ** @see ModelPortRegistry management interface ** @see OutputDesignation ** @see OutputMapping ** @see Timeline @@ -66,23 +67,83 @@ #define PROC_MOBJECT_MODEL_PORT_H #include "proc/asset/pipe.hpp" -//#include "lib/opaque-holder.hpp" -//#include "lib/meta/typelist-util.hpp" - -//extern "C" { -//#include "lib/luid.h" -//} +#include "lib/bool-checkable.hpp" +#include "lib/streamtype.hpp" namespace mobject { + LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port + LUMIERA_ERROR_DECLARE (UNCONNECTED_MODEL_PORT); ///< Attempt to operate on an existing but unconnected model port + + + using asset::ID; + using lumiera::StreamType; + + /** - * TODO type comment + * Handle denoting a port within the model, + * where actually output data can be pulled. + * ModelPort is a frontend to be used by clients. + * These ModelPort handle objects may be copied and stored + * at will, but their validity will be verified on each access. + * Actually, the Builder will discover any model ports and + * maintain a ModelPortRegistry behind the scenes. + * + * Each model port corresponds to a (global) pipe within a + * specific Timeline; consequently each such port is also + * bound to produce data of a specific StreamType (as defined by + * the corresponding pipe). A model port may be in \em unconnected + * state, which can be checked by \c bool conversion. While the + * ModelPort handles are value objects, the identity of the + * underlying model port (descriptor) is given by the + * corresponding pipe-ID, thus effectively resulting + * in a global namespace for model ports. + * + * @see builder::ModelPortRegistry management interface + * @see ModelPortRegistry_test abstract usage example */ class ModelPort - : boost::noncopyable + : public lib::BoolCheckable { + ID id_; public: + ModelPort() ///< \em unconnected model port + : id_(ID::INVALID) + { } + + ModelPort (ID refID); ///< @note conversion from pipe-ID + + // using default copy operations + + + static bool exists (ID); + + ID pipe() const; + ID holder() const; + StreamType::ID streamType() const; + + bool + isValid() const + { + return exists (this->id_); + } + + + friend bool + operator== (ModelPort const& mp1, ModelPort const& mp2) + { + return mp1.id_ == mp2.id_; + } + + friend bool + operator!= (ModelPort const& mp1, ModelPort const& mp2) + { + return mp1.id_ != mp2.id_; + } + + private: + }; diff --git a/tests/44builder.tests b/tests/44builder.tests index 6424f453f..5df8ef0a4 100644 --- a/tests/44builder.tests +++ b/tests/44builder.tests @@ -15,6 +15,11 @@ PLANNED "BuildSegment_test" BuildSegment_test < //#include #include -//using boost::format; -//using boost::scoped_ptr; -using util::isSameObject; -using util::isnil; -using std::string; - namespace mobject { namespace builder { namespace test { + //using boost::format; + //using boost::scoped_ptr; + using util::isSameObject; + using util::isnil; + using std::string; + using asset::Pipe; using asset::PPipe; + using asset::Struct; + using asset::Timeline; + using asset::PTimeline; + using lumiera::Query; + + typedef asset::ID PID; + typedef asset::ID TID; + - //typedef asset::ID PID; namespace { // test environment + inline PID + getPipe (string id) + { + return Pipe::query("id("+id+")"); + } + + inline TID + getTimeline (string id) + { + return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); + } + + typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + struct TestContext { ModelPortRegistry registry_; @@ -88,30 +111,35 @@ namespace test { { TestContext ctx; - fabricating_ModelPorts (ctx); + fabricating_ModelPorts (ctx.registry_); accessing_ModelPorts(); - transactionalSwitch (ctx); + transactionalSwitch (ctx.registry_); } void fabricating_ModelPorts (ModelPortRegistry& registry) { - ModelPortDescriptor& p1 = registry.definePort (pipeA, someTimeline); - ModelPortDescriptor& p2 = registry.definePort (pipeB, someTimeline); + /* == some Assets to play with == */ + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + TID someTimeline = getTimeline ("some_test_Timeline"); - CHECK (p1); - CHECK (p2); + // start out with defining some new model ports...... + MPDescriptor p1 = registry.definePort (pipeA, someTimeline); + MPDescriptor p2 = registry.definePort (pipeB, someTimeline); + + CHECK (registry.contains (pipeA)); + CHECK (registry.contains (pipeB)); VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) ); - CHECK (p2); + CHECK (registry.contains (pipeB)); - CHECK (p1.getID() == pipeA); - CHECK (p2.getID() == pipeB); - CHECK (p1.getPipe() == pipeA); - CHECK (p2.getPipe() == pipeB); - CHECK (p1.getTimeline() == someTimeline); - CHECK (p2.getTimeline() == someTimeline); + CHECK (p1.id == pipeA); + CHECK (p2.id == pipeB); + CHECK (p1.holder == someTimeline); + CHECK (p2.holder == someTimeline); registry.commit(); } @@ -120,6 +148,10 @@ namespace test { void accessing_ModelPorts () { + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + ModelPort mp1(pipeA); ModelPort mp2(pipeB); @@ -149,26 +181,32 @@ namespace test { CHECK (mp1x.pipe() == pipeA); VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); - CHECK (mp1.streamType() == pipeA.getStreamType()); + CHECK (mp1.streamType() == pipeA.streamType()); } void transactionalSwitch (ModelPortRegistry& registry) { + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); CHECK (ModelPort::exists (pipeA)); CHECK (registry.contains (pipeA)); registry.remove (pipeA); - CHECK (ModelPort::exists (pipeA)); + CHECK ( ModelPort::exists (pipeA)); CHECK (!registry.contains (pipeA)); - - ModelPortDescriptor& p1 = registry.definePort (pipeA, anotherTimeline); + + // now create a new and differing definition of port A + TID anotherTimeline = getTimeline ("another_test_Timeline"); + MPDescriptor p1 = registry.definePort (pipeA, anotherTimeline); CHECK (registry.contains (pipeA)); - CHECK (p1.getTimeline() == anotherTimeline); - CHECK (ModelPort(pipeA).timeline() != anotherTimeline); + CHECK (p1.holder == anotherTimeline); + CHECK (ModelPort(pipeA).holder() != anotherTimeline); registry.remove (pipeB); registry.definePort (pipeWC,anotherTimeline); @@ -184,7 +222,7 @@ namespace test { CHECK (portB); CHECK (portA.pipe() == pipeA); CHECK (portB.pipe() == pipeB); - CHECK (portA.timeline() != anotherTimeline); + CHECK (portA.holder() != anotherTimeline); registry.commit(); CHECK ( ModelPort::exists (pipeA)); @@ -192,14 +230,25 @@ namespace test { CHECK ( ModelPort::exists (pipeWC)); CHECK ( portA); CHECK (!portB); - CHECK (portA.timeline() == anotherTimeline); + CHECK (portA.holder() == anotherTimeline); CHECK (portA.pipe() == pipeA); VERIFY_ERROR (UNCONNECTED_MODEL_PORT, portB.pipe()); ModelPort pwc(pipeWC); CHECK (pwc); CHECK (pwc.pipe() == pipeWC); - CHECK (pwc.timeline() == anotherTimeline); + CHECK (pwc.holder() == anotherTimeline); + + registry.remove (pipeA); + registry.clear(); + CHECK (!registry.contains (pipeA)); + CHECK (!registry.contains (pipeB)); + CHECK (!registry.contains (pipeWC)); + + registry.rollback(); + CHECK ( registry.contains (pipeA)); + CHECK ( registry.contains (pipeB)); + CHECK ( registry.contains (pipeWC)); } }; From be6f555e047402bfee79c3861839f9810ddae044 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 11 Dec 2010 01:52:02 +0100 Subject: [PATCH 10/13] Builder: registry for model ports coded up passes compiler, but not yet unit test.... Needed to change asset::ID to encapsulate the embedded hash value --- src/include/logging.h | 10 +- src/proc/asset.hpp | 8 +- src/proc/mobject/builder/common.hpp | 17 +- .../mobject/builder/model-port-registry.cpp | 188 +++++++++++++----- .../mobject/builder/model-port-registry.hpp | 62 ++++-- src/proc/mobject/builderfacade.cpp | 8 +- src/proc/mobject/builderfacade.hpp | 1 + .../builder/model-port-registry-test.cpp | 17 +- 8 files changed, 209 insertions(+), 102 deletions(-) diff --git a/src/include/logging.h b/src/include/logging.h index d2420b0f8..d5123f48f 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -132,6 +132,12 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc, progress); NOBUG_CPP_DEFINE_FLAG_PARENT ( command, proc); /** progress log for session datastructure */ NOBUG_CPP_DEFINE_FLAG_PARENT ( session, proc); +/** progress log for the builder and build process */ +NOBUG_CPP_DEFINE_FLAG_PARENT ( builder, proc); +/** progress log for running the engine */ +NOBUG_CPP_DEFINE_FLAG_PARENT ( engine, proc); +/** progress log for play- and render subsystem */ +NOBUG_CPP_DEFINE_FLAG_PARENT ( play, proc); /** progress log for the gui */ NOBUG_CPP_DEFINE_FLAG_PARENT ( gui, progress); /** progress log for the support lib */ @@ -158,13 +164,13 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( config, logging); /** base flag for software testing */ NOBUG_CPP_DEFINE_FLAG_PARENT ( test, logging); -/** base flag for syncronization logging */ +/** base flag for synchronisation logging */ NOBUG_CPP_DEFINE_FLAG_PARENT ( sync, logging); // do we need subsections here? backend_mutex_sync proc_mutex_sync etc? NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_sync, sync); //locking/unlocking mutexes NOBUG_CPP_DEFINE_FLAG_PARENT ( cond_sync, sync); //waiting and signalling condition vars /** base flag for memory related logging */ NOBUG_CPP_DEFINE_FLAG_PARENT ( memory, logging); -/** memory busines of the proc layer */ +/** proc layer memory handling */ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_mem, memory); NOBUG_CPP_DEFINE_FLAG_PARENT ( mobject_mem, proc_mem); NOBUG_CPP_DEFINE_FLAG_PARENT ( builder_mem, proc_mem); diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index 80e5f2aa7..ef56fc632 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -103,11 +103,11 @@ namespace asset { template class ID { + HashVal hash_; public: - const HashVal hash; - ID (HashVal id) : hash(id) {} - ID (const KIND& asset) : hash(asset.getID()) {} - operator HashVal() const { return hash; } + ID (HashVal id) : hash_(id) {} + ID (const KIND& asset) : hash_(asset.getID()) {} + operator HashVal() const { return hash_; } static ID INVALID; }; diff --git a/src/proc/mobject/builder/common.hpp b/src/proc/mobject/builder/common.hpp index e0503499b..ed3554c4c 100644 --- a/src/proc/mobject/builder/common.hpp +++ b/src/proc/mobject/builder/common.hpp @@ -24,25 +24,14 @@ #ifndef MOBJECT_BUILDER_COMMON_H #define MOBJECT_BUILDER_COMMON_H +#include "lib/error.hpp" #include "include/logging.h" namespace mobject { - namespace builder { +namespace builder { - // TODO NOBUG_DECLARE_FLAG (builder_mem); - - } // namespace builder - -} // namespace mobject +}} // namespace mobject::builder #endif - -/* -// Local Variables: -// mode: C -// c-file-style: "gnu" -// indent-tabs-mode: nil -// End: -*/ diff --git a/src/proc/mobject/builder/model-port-registry.cpp b/src/proc/mobject/builder/model-port-registry.cpp index 2346c4b9f..86a444a70 100644 --- a/src/proc/mobject/builder/model-port-registry.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -37,7 +37,10 @@ #include "lib/error.hpp" +#include "include/logging.h" +#include "lib/sync-classlock.hpp" //#include "lib/symbol.hpp"// +#include "proc/mobject/builderfacade.hpp" #include "proc/mobject/model-port.hpp" #include "proc/mobject/builder/model-port-registry.hpp" @@ -55,47 +58,72 @@ namespace mobject { namespace builder { + namespace error = lumiera::error; - /** - * TODO type comment - */ - class ModelPortTable - : boost::noncopyable - { - - public: - }; - typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + typedef lib::ClassLock LockRegistry; - inline MPDescriptor - accessDescriptor() - { - - } - ModelPortRegistry& - ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) + /** storage for the link to the + global Registry instance currently in charge */ + lib::OptionalRef ModelPortRegistry::theGlobalRegistry; + + + + /** globally deactivate access to model ports */ + void + ModelPortRegistry::shutdown () { - UNIMPLEMENTED ("handling of active model port registry"); + INFO (builder, "disabling ModelPort registry...."); + LockRegistry global_lock; + theGlobalRegistry.clear(); } - /** */ + /** switch the implicit link to \em the global ModelPort registry + * to point to the given implementation instance. Typically used within + * the Builder subsystem lifecycle methods, or for unit tests to use + * a test instance of the registry temporarily + * @return the registry instance previously in use or \c NULL + */ + ModelPortRegistry* + ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) + { + INFO (builder, "activating new ModelPort registry."); + LockRegistry global_lock; + ModelPortRegistry *previous = theGlobalRegistry.isValid()? + &(theGlobalRegistry()) : 0; + theGlobalRegistry.link_to (newRegistry); + return previous; + } + + + /** access the globally valid registry instance. + * @throw error::State if this global registry is + * already closed or not yet initialised. */ ModelPortRegistry& ModelPortRegistry::globalInstance() { - UNIMPLEMENTED ("access the globally valid registry instance"); + LockRegistry global_lock; + if (theGlobalRegistry.isValid()) + return theGlobalRegistry(); + + throw error::State ("global model port registry is not accessible" + , LUMIERA_ERROR_BUILDER_LIFECYCLE); } - /** */ + /** does the transaction currently being built + * already contain a model port registration for the given ID? + * @note this does \em not query registration state of the + * global registry; use #isRegistered for that...*/ bool - ModelPortRegistry::contains (ID pID) const + ModelPortRegistry::contains (ID key) const { - UNIMPLEMENTED ("diagnostics querying the state of the pending transaction"); + return bool(key) + && util::contains (transaction_, key); } @@ -103,70 +131,117 @@ namespace mobject { * existing, connected and usable model port. * @note reflects the state of the publicly visible * model port registry, \em not any model ports - * being registered within a currently pending - * transaction (ongoing build process). */ + * being registered within a pending transaction + * (ongoing build process). */ bool ModelPortRegistry::isRegistered (ID key) const { - if (!key) return false; - - UNIMPLEMENTED ("query the publicly valid contents"); + return bool(key) + && util::contains (currentReg_, key); } - /** */ + /** basic access operation: access the descriptor + * of a currently valid model port. + * @note no locking (but #accessDescriptor does lock!) + * @throw error::Logic if accessing a non registered port + * @throw error::State if accessing an invalid / disconnected port + */ MPDescriptor - ModelPortRegistry::operator[] (ID key) const + ModelPortRegistry::get (ID key) const { - UNIMPLEMENTED ("access registered model port"); + if (!key) + throw error::State ("This model port is disconnected or NIL" + , LUMIERA_ERROR_UNCONNECTED_MODEL_PORT); + if (!isRegistered (key)) + throw error::Logic ("Model port was never registered, or got unregistered meanwhile." + ,LUMIERA_ERROR_INVALID_MODEL_PORT); + + MPTable::const_iterator pos = currentReg_.find (key); + ASSERT (pos != currentReg_.end()); + ASSERT (pos->second.isValid()); + return pos->second; } - /** */ + /** access \em the globally valid model port for the given pipe. + * This (static) function locks and accesses the global model port registry + * to fetch the descriptor record. Typically invoked by client code + * through the ModelPort frontend + * @throw error::State when registry is down or the model port is disconnected + * @throw error::Logic when the given key wasn't registered for a model port */ MPDescriptor ModelPortRegistry::accessDescriptor (ID key) { - UNIMPLEMENTED ("access the current global registry and fetch model port"); + LockRegistry global_lock; + return theGlobalRegistry().get(key); } - /** */ + /* === Mutations === */ + + /** create and register a new model port entry, + * within the pending transaction */ MPDescriptor - ModelPortRegistry::definePort (ID pipeA, ID element_exposing_this_port) + ModelPortRegistry::definePort (ID pipe, ID element_exposing_this_port) { - UNIMPLEMENTED ("create and register a new model port entry, within the pending transaction"); + LockRegistry global_lock; + if (contains (pipe)) + throw error::Logic ("attempt to register a model port with a pipe-ID, " + "which has already been used to register a " + "model port within this transaction (build process)." + , LUMIERA_ERROR_DUPLICATE_MODEL_PORT); + return (transaction_[pipe] = ModelPortDescriptor(pipe, element_exposing_this_port)); } - /** */ + /** remove a model port entry from the pending transaction */ void ModelPortRegistry::remove (PID key) { - UNIMPLEMENTED ("remove a model port entry from the pending transaction"); + LockRegistry global_lock; + transaction_.erase (key); } - /** */ + /** schedule removal of all registry contents. + * When the currently pending transaction is committed, + * all registered model ports will be removed */ void ModelPortRegistry::clear() { - UNIMPLEMENTED ("schedule removal of all registry contents into the pending transaction"); + LockRegistry global_lock; + transaction_.clear(); } - /** */ + /** transactional switch for new/modified model ports. + * Promote the registered model ports from the currently + * pending transaction to become the globally valid model ports + * @note automatically starts a new transaction, initialised + * with the now published mappings. + */ void ModelPortRegistry::commit() { - UNIMPLEMENTED ("transactional switch for new/modified model ports"); + LockRegistry global_lock; + MPTable newTransaction(transaction_); + TRACE (builder, "committing new ModelPort list...."); + swap (currentReg_, transaction_); + swap (transaction_, newTransaction); } - /** */ + /** discard current transaction. + * The global port registration thus + * remains unaltered. */ void ModelPortRegistry::rollback() { - UNIMPLEMENTED ("discard current transaction"); + LockRegistry global_lock; + TRACE (builder, "discarding changes to ModelPort list (rollback)...."); + MPTable newTransaction(transaction_); + swap (transaction_, newTransaction); } @@ -189,7 +264,8 @@ namespace mobject { } - /** */ + /** check if the global model port registration + * contains a mapping for the given pipe-ID*/ bool ModelPort::exists (ID key) { @@ -197,25 +273,35 @@ namespace mobject { } - /** */ + /** access the Pipe (ID) of the global model port registered + * with the ID underlying this model port. + * @throw error::Logic if no model port is registered for this Pipe-ID + */ ID ModelPort::pipe() const { - ENSURE (this->id_ == builder::ModelPortRegistry::accessDescriptor(this->id_).id); + ENSURE (this->id_ == builder::ModelPortRegistry::accessDescriptor(this->id_).id()); - return builder::ModelPortRegistry::accessDescriptor(this->id_).id; + return builder::ModelPortRegistry::accessDescriptor(this->id_).id(); } - /** */ + /** access the timeline (or similar structural element) holding + * a global pipe which corresponds to this model port + * @throw error::Logic if no model port is registered for this Pipe-ID + */ ID ModelPort::holder() const { - return builder::ModelPortRegistry::accessDescriptor(this->id_).holder; + return builder::ModelPortRegistry::accessDescriptor(this->id_).holder(); } - /** */ + /** convenience shortcut to access the stream type + * associated with the pipe-ID corresponding to this model port. + * @note no check if this model port actually is valid + * @throw error::Invalid in case of unknown/unregistered Pipe-ID + */ StreamType::ID ModelPort::streamType() const { diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index 53ddc6810..e0c79e77f 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -34,6 +34,12 @@ ** for setting up such a registry, while all other parts of the system just access the current ** model ports through the mobject::ModelPort frontend. ** + ** @note the locking is rather coarse grained; basically we're using just one + ** single global lock for all ModelPortRegistry instances and all access/mutations, + ** as well as for accessing the globally valid Registry through the ModelPort frontend. + ** Assumed that usually there is just one Registry maintained by the builder, this is + ** likely to be sufficient. + ** ** @see ModelPort ** @see OutputDesignation ** @see ModelPortRegistry_test @@ -44,10 +50,13 @@ #define PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H #include "lib/error.hpp" +#include "lib/optional-ref.hpp" #include "proc/asset/pipe.hpp" #include "proc/asset/struct.hpp" #include "proc/mobject/model-port.hpp" +#include + //#include "lib/opaque-holder.hpp" //#include "lib/meta/typelist-util.hpp" @@ -87,7 +96,9 @@ namespace builder { struct ModelPortDescriptor; - static ModelPortRegistry& + static void shutdown (); + + static ModelPortRegistry* setActiveInstance (ModelPortRegistry& newRegistry); static ModelPortRegistry& @@ -104,7 +115,7 @@ namespace builder { bool isRegistered (PID) const; ModelPortDescriptor const& - operator[] (PID) const; + get (PID) const; void remove (PID); @@ -122,38 +133,51 @@ namespace builder { * Silently drop model port definition changes since the last commit. */ void rollback(); + + private: + static lib::OptionalRef theGlobalRegistry; + + typedef std::map MPTable; + + MPTable currentReg_; + MPTable transaction_; }; /** ModelPortDescriptor records are used as actual storage - * within the model port registration table; they are never - * exposed to client code directly. + * within the model port registration table; they are immutable + * value objects and never exposed to client code directly. */ - struct ModelPortRegistry::ModelPortDescriptor + class ModelPortRegistry::ModelPortDescriptor { - const PID id; - const StID holder; + PID id_; + StID holder_; - bool - isValid() const - { - return bool(id); - } + protected: + ModelPortDescriptor (PID pipe, StID element_exposing_this_port) + : id_(pipe) + , holder_(element_exposing_this_port) + { } + friend class ModelPortRegistry; + public: ModelPortDescriptor() - : id(PID::INVALID) - , holder(StID::INVALID) + : id_(PID::INVALID) + , holder_(StID::INVALID) { } // default copy operations permitted - protected: - ModelPortDescriptor (PID pipe, StID element_exposing_this_port) - : id(pipe) - , holder(element_exposing_this_port) - { } + bool + isValid() const + { + return bool(id_); + } + + const PID id() const { return id_; } + const StID holder() const { return holder_; } }; diff --git a/src/proc/mobject/builderfacade.cpp b/src/proc/mobject/builderfacade.cpp index 99eb87ed9..cbf73bbbd 100644 --- a/src/proc/mobject/builderfacade.cpp +++ b/src/proc/mobject/builderfacade.cpp @@ -26,11 +26,6 @@ namespace mobject { - namespace builder { - using ::NOBUG_FLAG(memory); - NOBUG_CPP_DEFINE_FLAG_PARENT(buildermem, memory); - } - /** * Main Operation of the Builder: @@ -42,6 +37,9 @@ namespace mobject { //////////////////////TODO } + LUMIERA_ERROR_DEFINE (BUILDER_LIFECYCLE, "Builder activated while in non operational state"); + + } // namespace mobject diff --git a/src/proc/mobject/builderfacade.hpp b/src/proc/mobject/builderfacade.hpp index 86520d483..31ab936ab 100644 --- a/src/proc/mobject/builderfacade.hpp +++ b/src/proc/mobject/builderfacade.hpp @@ -31,6 +31,7 @@ namespace mobject { + LUMIERA_ERROR_DECLARE (BUILDER_LIFECYCLE); ///< Builder activated while in non operational state /** * Provides unified access to the builder functionality. diff --git a/tests/components/proc/mobject/builder/model-port-registry-test.cpp b/tests/components/proc/mobject/builder/model-port-registry-test.cpp index 2c57df4b9..3e948c6c9 100644 --- a/tests/components/proc/mobject/builder/model-port-registry-test.cpp +++ b/tests/components/proc/mobject/builder/model-port-registry-test.cpp @@ -74,7 +74,7 @@ namespace test { struct TestContext { ModelPortRegistry registry_; - ModelPortRegistry& previous_; + ModelPortRegistry* previous_; /** setup */ TestContext() @@ -85,7 +85,10 @@ namespace test { /** tear-down */ ~TestContext() { - ModelPortRegistry::setActiveInstance (previous_); + if (previous_) + ModelPortRegistry::setActiveInstance (*previous_); + else + ModelPortRegistry::shutdown(); } }; @@ -136,10 +139,10 @@ namespace test { VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) ); CHECK (registry.contains (pipeB)); - CHECK (p1.id == pipeA); - CHECK (p2.id == pipeB); - CHECK (p1.holder == someTimeline); - CHECK (p2.holder == someTimeline); + CHECK (pipeA == p1.id()); + CHECK (pipeB == p2.id()); + CHECK (someTimeline == p1.holder()); + CHECK (someTimeline == p2.holder()); registry.commit(); } @@ -205,7 +208,7 @@ namespace test { TID anotherTimeline = getTimeline ("another_test_Timeline"); MPDescriptor p1 = registry.definePort (pipeA, anotherTimeline); CHECK (registry.contains (pipeA)); - CHECK (p1.holder == anotherTimeline); + CHECK (anotherTimeline == p1.holder()); CHECK (ModelPort(pipeA).holder() != anotherTimeline); registry.remove (pipeB); From 8a54e00b6bf1a6bce9506549847203a3d914c700 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 11 Dec 2010 23:40:12 +0100 Subject: [PATCH 11/13] ModelPort registry unit test pass (closes #727) --- .../mobject/builder/model-port-registry.cpp | 56 ++++++-------- .../mobject/builder/model-port-registry.hpp | 17 ++-- src/proc/mobject/model-port.hpp | 26 +++---- .../mobject/session/sess-manager-impl.cpp | 1 + tests/44builder.tests | 2 +- .../builder/model-port-registry-test.cpp | 77 +++++++++++-------- wiki/renderengine.html | 10 +-- 7 files changed, 92 insertions(+), 97 deletions(-) diff --git a/src/proc/mobject/builder/model-port-registry.cpp b/src/proc/mobject/builder/model-port-registry.cpp index 86a444a70..f674fd912 100644 --- a/src/proc/mobject/builder/model-port-registry.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -21,13 +21,13 @@ * *****************************************************/ -/** @file model-port-registry.cpp +/** @file model-port-registry.cpp ** Implementation details of model port descriptors and references. - ** Especially, the handling of the ModelPortTable datastructure is + ** Essentially the handling of the ModelPortRegistry datastructure is ** kept an opaque implementation detail and confined entirely within - ** this translation unit. - ** - ** TODO: comment necessary? + ** this translation unit. Both the client interface (ModelPort) and + ** the management interface (ModelPortRegistry) are backed by this + ** common translation unit. ** ** @see OutputDesignation ** @see OutputMapping @@ -39,22 +39,10 @@ #include "lib/error.hpp" #include "include/logging.h" #include "lib/sync-classlock.hpp" -//#include "lib/symbol.hpp"// #include "proc/mobject/builderfacade.hpp" #include "proc/mobject/model-port.hpp" #include "proc/mobject/builder/model-port-registry.hpp" -//#include -//#include -#include - -//using lumiera::query::QueryHandler; -//using lumiera::query::removeTerm; -//using lumiera::query::extractID; -//using lumiera::ConfigRules; - -//using lumiera::Symbol; - namespace mobject { namespace builder { @@ -65,8 +53,8 @@ namespace mobject { typedef lib::ClassLock LockRegistry; - /** storage for the link to the - global Registry instance currently in charge */ + /** storage for the link to the global + Registry instance currently in charge */ lib::OptionalRef ModelPortRegistry::theGlobalRegistry; @@ -82,18 +70,19 @@ namespace mobject { /** switch the implicit link to \em the global ModelPort registry - * to point to the given implementation instance. Typically used within - * the Builder subsystem lifecycle methods, or for unit tests to use - * a test instance of the registry temporarily + * to point to the given implementation instance. Typically used + * within the Builder subsystem lifecycle methods, or for + * temporarily exchanging the registry for unit tests * @return the registry instance previously in use or \c NULL */ ModelPortRegistry* ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) { - INFO (builder, "activating new ModelPort registry."); LockRegistry global_lock; ModelPortRegistry *previous = theGlobalRegistry.isValid()? - &(theGlobalRegistry()) : 0; + &( theGlobalRegistry()) : 0; + INFO_IF (!previous, builder, "activating new ModelPort registry."); + WARN_IF ( previous, builder, "switching ModelPort registry instance."); theGlobalRegistry.link_to (newRegistry); return previous; } @@ -112,7 +101,7 @@ namespace mobject { throw error::State ("global model port registry is not accessible" , LUMIERA_ERROR_BUILDER_LIFECYCLE); } - + /** does the transaction currently being built @@ -128,8 +117,8 @@ namespace mobject { /** @return true if the given pipe-ID actually denotes an - * existing, connected and usable model port. - * @note reflects the state of the publicly visible + * existing, connected and usable model port. + * @note reflects the state of the publicly visible * model port registry, \em not any model ports * being registered within a pending transaction * (ongoing build process). */ @@ -240,22 +229,22 @@ namespace mobject { { LockRegistry global_lock; TRACE (builder, "discarding changes to ModelPort list (rollback)...."); - MPTable newTransaction(transaction_); + MPTable newTransaction(currentReg_); swap (transaction_, newTransaction); } - + LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port"); - - } // namespace builder + + }// namespace builder LUMIERA_ERROR_DEFINE (INVALID_MODEL_PORT, "Referral to unknown model port"); LUMIERA_ERROR_DEFINE (UNCONNECTED_MODEL_PORT, "Attempt to operate on an existing but unconnected model port"); - + ModelPort::ModelPort (ID refID) : id_(refID) @@ -307,8 +296,7 @@ namespace mobject { { return this->id_.streamType(); } - - + } // namespace mobject diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index e0c79e77f..be69cac8b 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -57,19 +57,11 @@ #include -//#include "lib/opaque-holder.hpp" -//#include "lib/meta/typelist-util.hpp" - -//extern "C" { -//#include "lib/luid.h" -//} - namespace mobject { namespace builder { using asset::ID; using asset::Pipe; -//using asset::PPipe; using asset::Struct; LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port @@ -92,18 +84,19 @@ namespace builder { typedef ID StID; public: + /** @internal record to describe a model port */ struct ModelPortDescriptor; static void shutdown (); - + static ModelPortRegistry* setActiveInstance (ModelPortRegistry& newRegistry); static ModelPortRegistry& globalInstance(); - + static ModelPortDescriptor const& accessDescriptor (PID); @@ -134,6 +127,7 @@ namespace builder { */ void rollback(); + private: static lib::OptionalRef theGlobalRegistry; @@ -145,6 +139,7 @@ namespace builder { + /** ModelPortDescriptor records are used as actual storage * within the model port registration table; they are immutable * value objects and never exposed to client code directly. @@ -179,7 +174,7 @@ namespace builder { const PID id() const { return id_; } const StID holder() const { return holder_; } }; - + }} // namespace mobject::builder diff --git a/src/proc/mobject/model-port.hpp b/src/proc/mobject/model-port.hpp index cf98bc149..7b033218b 100644 --- a/src/proc/mobject/model-port.hpp +++ b/src/proc/mobject/model-port.hpp @@ -24,7 +24,7 @@ ** Organising the output data calculation possibilities. ** Model ports are conceptual entities, denoting the points where output might ** possibly be produced. There is an actual representation, a collection of small - ** descriptor objects managed by the Fixture and organised within the ModelPortTable + ** descriptor objects managed by the Fixture and organised within the registry ** datastructure. Thus, while the actual ModelPort descriptor entities are located ** within and managed by the Fixture -- model port as a concept spans the high-level ** and low-level view. A model port can be associated both to a pipe within a timeline @@ -44,16 +44,16 @@ ** to become the valid current model port configuration. After that switch, model ports ** are immutable. ** - ** Model ports are to be accessed, enumerated and grouped in various ways, because each - ** port belongs to a specific timeline and is used for producing data of a single + ** Model ports are to be accessed, enumerated and grouped in various ways, because + ** each port belongs to a specific timeline and is used to produce data of a single ** StreamType solely. But all those referrals, searching and grouping happens only ** after the build process has discovered all model ports currently available. ** Thus actually the ModelPort elements handed out to client code are just ** smart-handles, accessing a global ModelPortRegistry behind the scenes. ** Validity of these handles will be checked on each access. The actual ** model port descriptors are owned and managed by the fixture; - ** they are bulk allocated in a similar manner than the - ** ProcNode and WiringDescriptor objects. + ** @todo they might bulk allocated in a similar manner than the + ** ProcNode and WiringDescriptor objects are. ** ** @see ModelPortRegistry_test abstract usage example ** @see ModelPortRegistry management interface @@ -72,8 +72,8 @@ namespace mobject { - LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port - LUMIERA_ERROR_DECLARE (UNCONNECTED_MODEL_PORT); ///< Attempt to operate on an existing but unconnected model port + LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port + LUMIERA_ERROR_DECLARE (UNCONNECTED_MODEL_PORT); ///< Attempt to operate on an existing but unconnected model port using asset::ID; @@ -81,21 +81,21 @@ namespace mobject { /** - * Handle denoting a port within the model, + * Handle denoting a point within the model, * where actually output data can be pulled. * ModelPort is a frontend to be used by clients. * These ModelPort handle objects may be copied and stored - * at will, but their validity will be verified on each access. + * at will, but their validity will be verified on each access. * Actually, the Builder will discover any model ports and * maintain a ModelPortRegistry behind the scenes. * * Each model port corresponds to a (global) pipe within a - * specific Timeline; consequently each such port is also + * specific Timeline ("holder"); consequently each such port is also * bound to produce data of a specific StreamType (as defined by * the corresponding pipe). A model port may be in \em unconnected - * state, which can be checked by \c bool conversion. While the - * ModelPort handles are value objects, the identity of the - * underlying model port (descriptor) is given by the + * state, which can be checked through \c bool conversion. While + * the ModelPort handles are value objects, the identity of the + * underlying model port (descriptor) is given by the * corresponding pipe-ID, thus effectively resulting * in a global namespace for model ports. * diff --git a/src/proc/mobject/session/sess-manager-impl.cpp b/src/proc/mobject/session/sess-manager-impl.cpp index 51696928f..c3294d891 100644 --- a/src/proc/mobject/session/sess-manager-impl.cpp +++ b/src/proc/mobject/session/sess-manager-impl.cpp @@ -152,6 +152,7 @@ namespace session { closeSessionInterface() { /////////////////////// TICKET #699 INFO (session, "closing session interfaces."); + TODO ("actually close session interfaces :) and don't babble in the log when NOT closing anything..."); } diff --git a/tests/44builder.tests b/tests/44builder.tests index 5df8ef0a4..f54364132 100644 --- a/tests/44builder.tests +++ b/tests/44builder.tests @@ -15,7 +15,7 @@ PLANNED "BuildSegment_test" BuildSegment_test < -//#include -#include - namespace mobject { namespace builder { namespace test { - //using boost::format; - //using boost::scoped_ptr; using util::isSameObject; using util::isnil; - using std::string; using asset::Pipe; using asset::PPipe; @@ -53,6 +46,8 @@ namespace test { typedef asset::ID PID; typedef asset::ID TID; + + typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; namespace { // test environment @@ -69,8 +64,6 @@ namespace test { return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); } - typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; - struct TestContext { ModelPortRegistry registry_; @@ -91,14 +84,15 @@ namespace test { ModelPortRegistry::shutdown(); } }; - } + + /********************************************************************************* * @test create a standalone model port registry to verify the behaviour of - * model ports, accessed through reference handles. This test provides - * an example setup detached from the real usage situation within the builder. + * model ports, accessed through reference handles. This test provides an + * example setup detached from the real usage situation within the builder. * The ModelPortRegistry management interface is used to create and track a * set of model ports, to be made visible by an atomic, transactional switch. * The access for client code through the ModelPort frontend is then verified. @@ -126,7 +120,6 @@ namespace test { /* == some Assets to play with == */ PID pipeA = getPipe ("pipeA"); PID pipeB = getPipe ("pipeB"); - PID pipeWC = getPipe ("WCpipe"); TID someTimeline = getTimeline ("some_test_Timeline"); // start out with defining some new model ports...... @@ -158,17 +151,18 @@ namespace test { ModelPort mp1(pipeA); ModelPort mp2(pipeB); - VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort(pipeWC)); + VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort unbefitting(pipeWC) ); - ModelPort mp1x(pipeA); - ModelPort mpNull; + ModelPort mp1x(pipeA); // can be created multiple times + ModelPort mp2x(mp1x); // can be copied at will + ModelPort mpNull; // can be default constructed (->unconnected) CHECK (mp1); CHECK (mp2); CHECK (mp1x); - CHECK (!mpNull); + CHECK (!mpNull); // bool check verifies setup and connected state - CHECK ( ModelPort::exists (pipeA)); + CHECK ( ModelPort::exists (pipeA)); // this is the same check, but invoked just with an pipe-ID CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); @@ -182,7 +176,8 @@ namespace test { CHECK (mp1.pipe() == pipeA); CHECK (mp2.pipe() == pipeB); CHECK (mp1x.pipe() == pipeA); - VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); + VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); // any further operations on an unconnected port will throw + VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.holder()); CHECK (mp1.streamType() == pipeA.streamType()); } @@ -201,8 +196,8 @@ namespace test { CHECK (ModelPort::exists (pipeA)); CHECK (registry.contains (pipeA)); registry.remove (pipeA); - CHECK ( ModelPort::exists (pipeA)); - CHECK (!registry.contains (pipeA)); + CHECK (!registry.contains (pipeA)); // removed from the current (pending) transaction + CHECK ( ModelPort::exists (pipeA)); // but not yet publicly visible // now create a new and differing definition of port A TID anotherTimeline = getTimeline ("another_test_Timeline"); @@ -211,16 +206,18 @@ namespace test { CHECK (anotherTimeline == p1.holder()); CHECK (ModelPort(pipeA).holder() != anotherTimeline); - registry.remove (pipeB); - registry.definePort (pipeWC,anotherTimeline); + registry.remove (pipeB); // some more wired definitions + registry.definePort (pipeWC, anotherTimeline); CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); - - ModelPort portA(pipeA); + CHECK ( registry.isRegistered (pipeB)); // this is the same as ModelPort::exists + CHECK (!registry.isRegistered (pipeWC)); // + // Note: pending transaction not yet committed + ModelPort portA(pipeA); // ...... thus the changes aren't reflected to client code ModelPort portB(pipeB); - VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort(pipeWC)); + VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort ineptly(pipeWC)); CHECK (portA); CHECK (portB); CHECK (portA.pipe() == pipeA); @@ -228,30 +225,44 @@ namespace test { CHECK (portA.holder() != anotherTimeline); registry.commit(); - CHECK ( ModelPort::exists (pipeA)); + CHECK ( ModelPort::exists (pipeA)); // now all our changes got publicly visible CHECK (!ModelPort::exists (pipeB)); CHECK ( ModelPort::exists (pipeWC)); CHECK ( portA); CHECK (!portB); CHECK (portA.holder() == anotherTimeline); CHECK (portA.pipe() == pipeA); - VERIFY_ERROR (UNCONNECTED_MODEL_PORT, portB.pipe()); + VERIFY_ERROR (INVALID_MODEL_PORT, portB.pipe()); - ModelPort pwc(pipeWC); + ModelPort pwc(pipeWC); // now clients may also use the now officially promoted new port CHECK (pwc); CHECK (pwc.pipe() == pipeWC); CHECK (pwc.holder() == anotherTimeline); + // Next: doing several changes, + // but finally *not* committing them... + CHECK ( registry.contains (pipeA)); + CHECK (!registry.contains (pipeB)); + CHECK ( registry.contains (pipeWC)); registry.remove (pipeA); - registry.clear(); + registry.clear(); // remove everything from the pending transaction CHECK (!registry.contains (pipeA)); CHECK (!registry.contains (pipeB)); CHECK (!registry.contains (pipeWC)); - registry.rollback(); - CHECK ( registry.contains (pipeA)); + registry.definePort (pipeB, anotherTimeline); CHECK ( registry.contains (pipeB)); + CHECK (!portB); // not committed and thus not visible + CHECK (portA); + CHECK (pwc); + + registry.rollback(); + CHECK ( registry.contains (pipeA)); // no effect to the officialy visible state + CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); + + VERIFY_ERROR(INVALID_MODEL_PORT, registry.get(pipeB) ); + CHECK (!portB); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 09e2a5006..6d8c87fce 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2819,7 +2819,7 @@ These are used as token for dealing with other objects and have no identity of t
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
 A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
@@ -2827,13 +2827,13 @@ A model port is rather derived than configured; it emerges when a pipe [[claims|
 Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
 
 !formal specification
-Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being denoted by a ~Pipe-ID.
+Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
 
-Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are bound to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
+Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are bound to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. To stress this usage pattern, actually {{{ModelPort}}} instances are small copyable value objects (smart handles), which can be used to access data within an opaque registry. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
 
 
-
+
Model ports are conceptual entities, denoting the points where output might possibly be produced &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.
 
@@ -2851,7 +2851,7 @@ Model ports are added once and never changed. The corresponding timeline and pip
 !Implementation considerations
 The transactional switch creates a clear partitioning in the lifespan of the model port table. //Before// that point, entries are just added, but not accessed in any way. //After// that point, no further mutation occurs, but lookup is frequent and happens in a variety of different configurations and transient orderings.
 
-This observation leads to the idea of using //model port references// to provide all kinds of access, searching and reordering. These encapsulate the actual access by silently assuming reference to "the" global current model port configuration. Thus, the actual model port descriptors could be bulk allocated in a similar manner as the processing nodes and wiring descriptors. Access to stale model ports could be detected by the port references, allowing also for a {{{bool}}} checkable "has no port" information.
+This observation leads to the idea of using //model port references// as frontend to provide all kinds of access, searching and reordering. These encapsulate the actual access by silently assuming reference to "the" global current model port configuration. This way the actual model port descriptors could be bulk allocated in a similar manner as the processing nodes and wiring descriptors. Access to stale model ports could be detected by the port references, allowing also for a {{{bool}}} checkable "has no port" information.
 
 A model port registry, maintained by the builder, is responsible for storing the discovered model ports within a model port table, which is then swapped in after completing the build process. The {{{builder::ModelPortRegistry}}} acts as management interface, while client code accesses just the {{{ModelPort}}} frontend. A link to the actual registry instance is hooked into that frontend when bringing up the builder subsystem.
 
From c4282560ce03679f96c79e5f9f373a0feb69c4e6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 12 Dec 2010 01:53:23 +0100 Subject: [PATCH 12/13] fix two regressions --- src/proc/asset/struct-scheme.hpp | 9 +++++++++ src/proc/control/styperegistry.hpp | 4 ++-- tests/components/proc/mobject/output-mapping-test.cpp | 2 +- tests/lib/scoped-holder-transfer-test.cpp | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index c8e0d09a4..dd6f6f512 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -52,6 +52,9 @@ namespace session { class Clip; }} +namespace lumiera { + class StreamType; +} namespace asset{ @@ -104,6 +107,12 @@ namespace asset{ static Symbol catFolder() { return "pipes";} static Symbol idSymbol() { return "pipe"; } }; + template<> struct StructTraits + { + static Symbol namePrefix() { return "type"; } + static Symbol catFolder() { return "stream-types";} + static Symbol idSymbol() { return "stype"; } + }; template<> struct StructTraits { static Symbol namePrefix() { return "patt"; } diff --git a/src/proc/control/styperegistry.hpp b/src/proc/control/styperegistry.hpp index 5979582d2..63c33e841 100644 --- a/src/proc/control/styperegistry.hpp +++ b/src/proc/control/styperegistry.hpp @@ -23,7 +23,7 @@ /** @file styperegistry.hpp ** This is part of the \i implementation of the stream type manager (include). - ** Only used in stypemanager.cpp and accompaning unit tests. + ** Only used in stypemanager.cpp and accompanying unit tests. ** ** @see control::STypeManager ** @see lumiera::StreamType @@ -62,7 +62,7 @@ namespace control { /** - * @internal Helper for organizing preconfigured default objects. + * @internal Helper for organising preconfigured default objects. * Maintaines a collection of objects known or encountered as "default" * for a given type. This collection is ordered by "degree of constriction", * which is implemented by counting the number of predicates in the query diff --git a/tests/components/proc/mobject/output-mapping-test.cpp b/tests/components/proc/mobject/output-mapping-test.cpp index 5f4a5be1a..18dd9eb60 100644 --- a/tests/components/proc/mobject/output-mapping-test.cpp +++ b/tests/components/proc/mobject/output-mapping-test.cpp @@ -76,7 +76,7 @@ namespace test { { PPipe srcP = Pipe::lookup (sourcePipeID); format queryPattern ("id(master_%1%), stream(%1%), ord(%2%)"); - return Query (str(queryPattern % srcP->getStreamID() % seqNr)); + return Query (str(queryPattern % srcP->getStreamID().getSym() % seqNr)); } }; diff --git a/tests/lib/scoped-holder-transfer-test.cpp b/tests/lib/scoped-holder-transfer-test.cpp index ace319736..7bd65a530 100644 --- a/tests/lib/scoped-holder-transfer-test.cpp +++ b/tests/lib/scoped-holder-transfer-test.cpp @@ -69,6 +69,7 @@ namespace lib { throw to.getVal(); swap (from,to); + from.setVal(0); // remove the old Dummy from accounting (checksum) } }; From e7191ed3c6d6e0466dd05d332d770bb7b03bacf2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 13 Dec 2010 03:22:11 +0100 Subject: [PATCH 13/13] Planning Fixure: Segmentation datastructure (#726) --- src/proc/Makefile.am | 6 +- .../builder/fixture-change-detector.hpp | 81 +++++++++++++ ...entationtool.cpp => segmentation-tool.cpp} | 96 ++++++++------- .../mobject/builder/segmentation-tool.hpp | 78 ++++++++++++ src/proc/mobject/builder/segmentation.cpp | 85 +++++++++++++ src/proc/mobject/builder/segmentation.hpp | 87 ++++++++++++++ src/proc/mobject/builder/segmentationtool.hpp | 80 ------------- src/proc/mobject/builder/toolfactory.hpp | 2 +- tests/44builder.tests | 10 ++ tests/components/Makefile.am | 2 + .../builder/fixture-change-detector-test.cpp | 113 ++++++++++++++++++ .../segmentation-datastructure-test.cpp | 113 ++++++++++++++++++ wiki/renderengine.html | 46 ++++++- 13 files changed, 665 insertions(+), 134 deletions(-) create mode 100644 src/proc/mobject/builder/fixture-change-detector.hpp rename src/proc/mobject/builder/{segmentationtool.cpp => segmentation-tool.cpp} (50%) create mode 100644 src/proc/mobject/builder/segmentation-tool.hpp create mode 100644 src/proc/mobject/builder/segmentation.cpp create mode 100644 src/proc/mobject/builder/segmentation.hpp delete mode 100644 src/proc/mobject/builder/segmentationtool.hpp create mode 100644 tests/components/proc/mobject/builder/fixture-change-detector-test.cpp create mode 100644 tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp diff --git a/src/proc/Makefile.am b/src/proc/Makefile.am index d2a185dbf..d8e58f786 100644 --- a/src/proc/Makefile.am +++ b/src/proc/Makefile.am @@ -109,7 +109,8 @@ liblumiprocmobjectbuilder_la_SOURCES = \ $(liblumiprocmobjectbuilder_la_srcdir)/assembler.cpp \ $(liblumiprocmobjectbuilder_la_srcdir)/conmanager.cpp \ $(liblumiprocmobjectbuilder_la_srcdir)/nodecreatortool.cpp \ - $(liblumiprocmobjectbuilder_la_srcdir)/segmentationtool.cpp \ + $(liblumiprocmobjectbuilder_la_srcdir)/segmentation.cpp \ + $(liblumiprocmobjectbuilder_la_srcdir)/segmentation-tool.cpp \ $(liblumiprocmobjectbuilder_la_srcdir)/toolfactory.cpp @@ -219,7 +220,8 @@ noinst_HEADERS += \ $(liblumiproc_la_srcdir)/mobject/builder/assembler.hpp \ $(liblumiproc_la_srcdir)/mobject/builder/buildertool.hpp \ $(liblumiproc_la_srcdir)/mobject/builder/conmanager.hpp \ - $(liblumiproc_la_srcdir)/mobject/builder/segmentationtool.hpp \ + $(liblumiproc_la_srcdir)/mobject/builder/segmentation.hpp \ + $(liblumiproc_la_srcdir)/mobject/builder/segmentation-tool.hpp \ $(liblumiproc_la_srcdir)/mobject/builder/toolfactory.hpp \ $(liblumiproc_la_srcdir)/mobject/builderfacade.hpp \ $(liblumiproc_la_srcdir)/mobject/explicitplacement.hpp \ diff --git a/src/proc/mobject/builder/fixture-change-detector.hpp b/src/proc/mobject/builder/fixture-change-detector.hpp new file mode 100644 index 000000000..7d25225f3 --- /dev/null +++ b/src/proc/mobject/builder/fixture-change-detector.hpp @@ -0,0 +1,81 @@ +/* + FIXTURE-CHANGE-DETECTOR.hpp - isolating changed segments and tainted processes + + Copyright (C) Lumiera.org + 2010, 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 fixture-change-detector.hpp + ** Work out the part of the Fixture changed by a build process. + ** This facility helps to deal with ongoing render/playback processes, which might be + ** affected by the results of a build process. It's comprised of two distinct parts: + ** - a comparison tool allowing to spot equal and changed segments when considering + ** the old and the new version of the fixture before/after a build process. + ** - a registration service to establish a relation between play/render processes + ** and specific segments of the fixture. + ** Together, these allow to identify those ongoing processes which need to be cancelled + ** or restarted, because their results might be tainted by the changes induced by the + ** build process. Typically, these detection process runs just before commiting the + ** newly built fixture datastructure. + ** + ** @todo WIP-WIP-WIP as of 12/2010 + ** + ** @see Fixture + ** @see ModelPort + */ + + +#ifndef PROC_MOBJECT_BUILDER_FIXTURE_CHANGE_DETECTOR_H +#define PROC_MOBJECT_BUILDER_FIXTURE_CHANGE_DETECTOR_H + +#include "lib/error.hpp" +//#include "lib/optional-ref.hpp" +#include "proc/asset/pipe.hpp" +//#include "proc/asset/struct.hpp" +//#include "proc/mobject/model-port.hpp" + +//#include + +namespace mobject { +namespace builder { + + using asset::ID; + using asset::Pipe; +//using asset::Struct; + +//LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port + + + /** + * TODO type comment + */ + class FixtureChangeDetector + : boost::noncopyable + { + + typedef ID PID; +// typedef ID StID; + + public: + + }; + + + +}} // namespace mobject::builder +#endif diff --git a/src/proc/mobject/builder/segmentationtool.cpp b/src/proc/mobject/builder/segmentation-tool.cpp similarity index 50% rename from src/proc/mobject/builder/segmentationtool.cpp rename to src/proc/mobject/builder/segmentation-tool.cpp index d0a18595d..b87109d68 100644 --- a/src/proc/mobject/builder/segmentationtool.cpp +++ b/src/proc/mobject/builder/segmentation-tool.cpp @@ -21,7 +21,7 @@ * *****************************************************/ -#include "proc/mobject/builder/segmentationtool.hpp" +#include "proc/mobject/builder/segmentation-tool.hpp" using mobject::Buildable; @@ -30,52 +30,50 @@ using mobject::session::Effect; namespace mobject { - namespace builder { - - /////////////////////////////////TICKET #414 - +namespace builder { - SegmentationTool::SegmentationTool(mobject::session::Fixture&) - { - UNIMPLEMENTED ("create new SegmentationTool"); - } - - - void - SegmentationTool::treat (Buildable& something) - { - UNIMPLEMENTED ("??? when partitioning timeline"); - } - - - void - SegmentationTool::treat (Clip& clip) - { - UNIMPLEMENTED ("consider clip when partitioning timeline"); - } - - - void - SegmentationTool::treat (Effect& effect) - { - UNIMPLEMENTED ("note applied effect when partitioning timeline"); - } - - - void - SegmentationTool::onUnknown (Buildable& target) - { - UNIMPLEMENTED ("catch-all when partitioning timeline"); - } - - - bool - SegmentationTool::empty() const - { - UNIMPLEMENTED ("detect an empty segmentation"); - } - - - } // namespace mobject::builder - -} // namespace mobject + /////////////////////////////////TICKET #414 + + + SegmentationTool::SegmentationTool(mobject::session::Fixture&) + { + UNIMPLEMENTED ("create new SegmentationTool"); + } + + + void + SegmentationTool::treat (Buildable& something) + { + UNIMPLEMENTED ("??? when partitioning timeline"); + } + + + void + SegmentationTool::treat (Clip& clip) + { + UNIMPLEMENTED ("consider clip when partitioning timeline"); + } + + + void + SegmentationTool::treat (Effect& effect) + { + UNIMPLEMENTED ("note applied effect when partitioning timeline"); + } + + + void + SegmentationTool::onUnknown (Buildable& target) + { + UNIMPLEMENTED ("catch-all when partitioning timeline"); + } + + + bool + SegmentationTool::empty() const + { + UNIMPLEMENTED ("detect an empty segmentation"); + } + + +}} // namespace mobject::builder diff --git a/src/proc/mobject/builder/segmentation-tool.hpp b/src/proc/mobject/builder/segmentation-tool.hpp new file mode 100644 index 000000000..a40f4c8f9 --- /dev/null +++ b/src/proc/mobject/builder/segmentation-tool.hpp @@ -0,0 +1,78 @@ +/* + SEGMENTATION-TOOL.hpp - Tool for creating a partitioning of the current timeline + + Copyright (C) Lumiera.org + 2008, 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. + +*/ + + +#ifndef MOBJECT_BUILDER_SEGMENTATION_TOOL_H +#define MOBJECT_BUILDER_SEGMENTATION_TOOL_H + + +#include "proc/mobject/builder/applicable-builder-target-types.hpp" + +#include "proc/mobject/session/segmentation.hpp" +#include "proc/mobject/session/fixture.hpp" //////TODO really on the header?? + + +#include +using std::list; + + + +namespace mobject { +namespace builder { + + + /** + * Tool implementation for deriving a partitioning of the current + * timeline, such that each Segment has a constant configuration. + * "Constant" means here, that any remaining changes over time + * can be represented by automation solely, without the need + * to change the node connections. + */ + class SegmentationTool + : public ApplicableBuilderTargetTypes + { + + public: + SegmentationTool (session::Fixture &) ; + + void treat (mobject::session::Clip& clip) ; + void treat (mobject::session::Effect& effect) ; + + void treat (mobject::Buildable& something) ; + + void onUnknown (Buildable& target) ; /////////TODO why doesn't the treat(Buildable) function shadow this?? + + bool empty() const; + + private: + typedef mobject::session::Segment Segment; + + /** Partitioning of the Timeline to be created by this tool. */ + //session::Segmentation& segments_; + ///////////////////////////////////////////TODO: either put it inline, or use a scopend_ptr!!!!!!!!!! + + }; + + + +}} // namespace mobject::builder +#endif diff --git a/src/proc/mobject/builder/segmentation.cpp b/src/proc/mobject/builder/segmentation.cpp new file mode 100644 index 000000000..7231dfd0a --- /dev/null +++ b/src/proc/mobject/builder/segmentation.cpp @@ -0,0 +1,85 @@ +/* + Segmentation - partitioning the effective timeline + + Copyright (C) Lumiera.org + 2010, 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 segmentation.cpp + ** Implementation details of fixture data structures. + ** + ** /////TODO file comment necessary? + ** + ** @see mobject::session::Fixture + ** + */ + + +#include "lib/error.hpp" +//#include "include/logging.h" +//#include "lib/sync-classlock.hpp" +//#include "proc/mobject/builderfacade.hpp" +#include "proc/mobject/builder/segmentation.hpp" +#include "proc/mobject/builder/fixture-change-detector.hpp" + +namespace mobject { +namespace builder { + + namespace error = lumiera::error; + + +// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + + + /** storage for the link to the global + Registry instance currently in charge */ +// lib::OptionalRef ModelPortRegistry::theGlobalRegistry; + + + /** access the globally valid registry instance. + * @throw error::State if this global registry is + * already closed or not yet initialised. */ +//ModelPortRegistry& +//ModelPortRegistry::globalInstance() +//{ +// LockRegistry global_lock; +// if (theGlobalRegistry.isValid()) +// return theGlobalRegistry(); +// +// throw error::State ("global model port registry is not accessible" +// , LUMIERA_ERROR_BUILDER_LIFECYCLE); +//} + + + + /** */ +// bool +// ModelPortRegistry::contains (ID key) const +// { +// return bool(key) +// && util::contains (transaction_, key); +// } + + + +//LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port"); + + + +}}// namespace mobject::builder diff --git a/src/proc/mobject/builder/segmentation.hpp b/src/proc/mobject/builder/segmentation.hpp new file mode 100644 index 000000000..c3c73249c --- /dev/null +++ b/src/proc/mobject/builder/segmentation.hpp @@ -0,0 +1,87 @@ +/* + SEGMENTATION.hpp - partitioning the effective timeline + + Copyright (C) Lumiera.org + 2010, 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 segmentation.hpp + ** Part of the Fixture datastructure to manage time segments of constant structure. + ** The Fixture is result of the build process and separation between high-level and + ** low-level model. It's kind of an effective resulting timeline, and split into segments + ** of constant wiring structure: whenever the processing nodes need to be wired differently + ** for some timespan, we start a new segment of the timeline. This might be for the duration + ** of a clip, or just for the duration of a transition, when the pipes of both clips need to + ** be wired up in parallel. + ** + ** @see Fixture + ** @see ModelPort + */ + + +#ifndef PROC_MOBJECT_BUILDER_SEGMENTATION_H +#define PROC_MOBJECT_BUILDER_SEGMENTATION_H + +#include "lib/error.hpp" +//#include "lib/optional-ref.hpp" +#include "proc/asset/pipe.hpp" +//#include "proc/asset/struct.hpp" +//#include "proc/mobject/model-port.hpp" + +//#include + +namespace mobject { +namespace builder { + + using asset::ID; + using asset::Pipe; +//using asset::Struct; + +//LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port + + + /** + * TODO type comment + */ + class Segment + : boost::noncopyable + { + + typedef ID PID; +// typedef ID StID; + + public: + + }; + + + + + /** + * TODO type comment + */ + class Segmentation + { + + public: + }; + + + +}} // namespace mobject::builder +#endif diff --git a/src/proc/mobject/builder/segmentationtool.hpp b/src/proc/mobject/builder/segmentationtool.hpp deleted file mode 100644 index 2b88f572a..000000000 --- a/src/proc/mobject/builder/segmentationtool.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - SEGMENTATIONTOOL.hpp - Tool for creating a partitioning of the current timeline - - Copyright (C) Lumiera.org - 2008, 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. - -*/ - - -#ifndef MOBJECT_BUILDER_SEGMENTATIONTOOL_H -#define MOBJECT_BUILDER_SEGMENTATIONTOOL_H - - -#include "proc/mobject/builder/applicable-builder-target-types.hpp" - -#include "proc/mobject/session/segmentation.hpp" -#include "proc/mobject/session/fixture.hpp" //////TODO really on the header?? - - -#include -using std::list; - - - -namespace mobject { - namespace builder { - - - /** - * Tool implementation for deriving a partitioning of the current - * timeline, such that each Segment has a constant configuration. - * "Constant" means here, that any remaining changes over time - * can be represented by automation solely, without the need - * to change the node connections. - */ - class SegmentationTool - : public ApplicableBuilderTargetTypes - { - - public: - SegmentationTool (session::Fixture &) ; - - void treat (mobject::session::Clip& clip) ; - void treat (mobject::session::Effect& effect) ; - - void treat (mobject::Buildable& something) ; - - void onUnknown (Buildable& target) ; /////////TODO why doesn't the treat(Buildable) function shadow this?? - - bool empty() const; - - private: - typedef mobject::session::Segment Segment; - - /** Partitioning of the Timeline to be created by this tool. */ - //session::Segmentation& segments_; - ///////////////////////////////////////////TODO: either put it inline, or use a scopend_ptr!!!!!!!!!! - - }; - - - - } // namespace mobject::builder - -} // namespace mobject -#endif diff --git a/src/proc/mobject/builder/toolfactory.hpp b/src/proc/mobject/builder/toolfactory.hpp index b283bea60..e17687ad4 100644 --- a/src/proc/mobject/builder/toolfactory.hpp +++ b/src/proc/mobject/builder/toolfactory.hpp @@ -25,7 +25,7 @@ #define MOBJECT_BUILDER_TOOLFACTORY_H #include "proc/mobject/session/fixture.hpp" -#include "proc/mobject/builder/segmentationtool.hpp" +#include "proc/mobject/builder/segmentation-tool.hpp" #include "proc/mobject/builder/nodecreatortool.hpp" #include "proc/mobject/builder/mould.hpp" #include "proc/engine/rendergraph.hpp" diff --git a/tests/44builder.tests b/tests/44builder.tests index f54364132..c5d20d870 100644 --- a/tests/44builder.tests +++ b/tests/44builder.tests @@ -15,6 +15,11 @@ PLANNED "BuildSegment_test" BuildSegment_test < + + 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. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "proc/mobject/builder/fixture-change-detector.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/pipe.hpp" +#include "lib/query.hpp" +#include "lib/util.hpp" + + +namespace mobject { +namespace builder { +namespace test { + +// using util::isSameObject; +// using util::isnil; +// + using asset::Pipe; + using asset::PPipe; + using asset::Struct; + using asset::Timeline; + using asset::PTimeline; + using lumiera::Query; +// + typedef asset::ID PID; + typedef asset::ID TID; +// +// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + + + namespace { // test environment + + inline PID + getPipe (string id) + { + return Pipe::query("id("+id+")"); + } + + inline TID + getTimeline (string id) + { + return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); + } + + struct TestContext + { + + /** setup */ + TestContext() + { } + + /** tear-down */ + ~TestContext() + { + } + }; + } + + + + + /********************************************************************************* + * @test TODO blubb + * + * @see mobject::builder::FixtureChangeDetector + */ + class FixtureChangeDetector_test : public Test + { + + virtual void + run (Arg) + { + TestContext ctx; + } + + + void + fabricating_ModelPorts () + { + } + + + }; + + + /** Register this test class... */ + LAUNCHER (FixtureChangeDetector_test, "unit builder"); + + + +}}} // namespace mobject::builder::test diff --git a/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp b/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp new file mode 100644 index 000000000..ac6c66d30 --- /dev/null +++ b/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp @@ -0,0 +1,113 @@ +/* + SegmentationDatastructure(Test) - verify basic properties of the Segmentation + + Copyright (C) Lumiera.org + 2010, 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. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "proc/mobject/builder/fixture-change-detector.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/pipe.hpp" +#include "lib/query.hpp" +#include "lib/util.hpp" + + +namespace mobject { +namespace builder { +namespace test { + +// using util::isSameObject; +// using util::isnil; +// + using asset::Pipe; + using asset::PPipe; + using asset::Struct; + using asset::Timeline; + using asset::PTimeline; + using lumiera::Query; +// + typedef asset::ID PID; + typedef asset::ID TID; +// +// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + + + namespace { // test environment + + inline PID + getPipe (string id) + { + return Pipe::query("id("+id+")"); + } + + inline TID + getTimeline (string id) + { + return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); + } + + struct TestContext + { + + /** setup */ + TestContext() + { } + + /** tear-down */ + ~TestContext() + { + } + }; + } + + + + + /********************************************************************************* + * @test TODO blubb + * + * @see mobject::builder::FixtureChangeDetector + */ + class SegmentationDatastructure_test : public Test + { + + virtual void + run (Arg) + { + TestContext ctx; + } + + + void + fabricating_ModelPorts () + { + } + + + }; + + + /** Register this test class... */ + LAUNCHER (SegmentationDatastructure_test, "unit builder"); + + + +}}} // namespace mobject::builder::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 6d8c87fce..5837913f4 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1728,8 +1728,12 @@ To make the intended use of the classes more clear, consider the following two e
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
 
-
-
A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. But contrary to the (standard) placements, which may exhibit all kinds of fancy dynamic and scope dependent behaviour, within an explicit placement all properties are resolved and materialised. While the (standard) placement may contain an arbitrary list of LocatingPin objects, the resolution into an explicit placement performs a kind of »orthogonalisation«: each remaining LocatingPin defines exactly one degree of freedom independent of all others. Most notably, the explicit placement always specifies a absolute time and [[output designation|OutputDesignation]] for for locating the Subject.
+
+
A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. But contrary to the (standard) placements, which may exhibit all kinds of fancy dynamic and scope dependent behaviour, within an explicit placement all properties are resolved and materialised. While the (standard) placement may contain an arbitrary list of LocatingPin objects, the resolution into an explicit placement performs a kind of »orthogonalisation«: each remaining LocatingPin defines exactly one degree of freedom independent of all others. Most notably, the explicit placement always specifies a absolute time and [[output designation|OutputDesignation]] for for locating the Subject. Explicit placements are ''immutable''.
+
+!!Implementation considerations
+Explicit placements are just created and never mutated, but copying and storage might become a problem.
+It would thus be desirable to have a fixed-sized allocation, able to hold the placement body as well as the (fixed) locating pins inline.
 
@@ -4675,6 +4679,44 @@ We need to detect attaching and detaching of * root &harr; [[Track]]
+
+
//Segmentation of timeline// denotes a data structure and a step in the BuildProcess.
+When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Placements -- are grouped below each timeline using them; Placements are then to be resolved into [[explicit Placements|ExplicitPlacement]], resulting in a single well defined time interval for each object. This allows to cut this effective timeline into slices of constant wiring structure, which are represented through the ''Segmentation Datastructure'', a time axis with segments holding object placements and [[exit nodes|ExitNode]]. &nbsp;&rarr; see [[structure of the Fixture|Fixture]]
+* for each Timeline we get a Segmentation
+** which in turn is a list of non-overlapping segments
+*** each holding
+**** an ExplicitPlacement for each covered object
+**** an ExitNode for each ModelPort of the corresponding timeline
+
+!Storage considerations
+;(1) build process
+:&rarr; a tree walk yields the placements per timeline, which then get //resolved//
+:&rarr; after //sorting,// the segmentation can be established, thereby copying placements spanning multiple segments
+:&rarr; only //after running the complete build process for each segment,// the list of model ports and exit nodes can be established
+;(2) commit stage
+: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel an restart, to pick up the changes introduced by this build process, which might include adding and deleting of timelines as a whole, and any changes to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated.
+;(3) rendering use
+:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' connected with objects in the high-level model. It is likely that there might be some sharing, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]]
+!!!observations
+* Storage and initialisation for explicit placements is an issue. We should strive at making that inline as much as possible.
+* the overall segmentation emerges from a sorting of time points, which are start points of explicit placements
+* after the segmentation has been built, the usage pattern changes entirely into a lookup of segment by time
+* the individual segments act as umbrella for a lot of further objects hooked up behind.
+* we need the ability to exchange or swap-in whole segments
+* each segment controls an AllocationCluster
+* we need to track processes for tainting
+* access happens per ModelPort
+
+!!!conclusions
+The Fixture is mostly comprised of the Segementation datastructure, but some other facilities are involved too
+# at top level, we need somehow to organise access per groups of model ports, actually grouped by timeline
+# during the build process, there is a collection of placements; this can be discarded afterwards
+# the backbone of the segmentation is closely linked to an ordering by time. Initially it should support sorting, access by time interval search later on.
+# discarding a segment (or failing to do so) has an high impact on the whole application. We should employ a reliable mechanism for that.
+# the frame dispatch and the tracking of processes can be combined; data duplication is a virtue when it comes to parallel processes
+# the process of comparing and tainting is broken out into a separate data structure to be used just once
+
+
A sequence is a collection of media objects, arranged onto a track tree. Sequences are the building blocks within the session. To be visible and editable, a session needs to be bound into a top-level [[Timeline]]. Alternatively, it may be used as a VirtualClip nested within another sequence.