Compare commits
10 commits
eeabbb4f60
...
252fbabedb
| Author | SHA1 | Date | |
|---|---|---|---|
| 252fbabedb | |||
| 46d438bbe4 | |||
| d6df336f9b | |||
| 717babbd5e | |||
| 651a463566 | |||
| 777b1286fd | |||
| e219dcbb8c | |||
| 89d2ceed4b | |||
| ce40b83bb6 | |||
| b804015cd7 |
39 changed files with 7317 additions and 3599 deletions
|
|
@ -156,10 +156,8 @@ the application itself.
|
|||
|
||||
_[aqua teal-background]#this is a mere idea, and certainly not a priority as of 2020#_
|
||||
|
||||
{nbsp}
|
||||
|
||||
....
|
||||
|
||||
....
|
||||
|
||||
|
||||
Lifecycle
|
||||
|
|
|
|||
1959
doc/design/workflow/Verwijlen/Discussion25Digest.txt
Normal file
1959
doc/design/workflow/Verwijlen/Discussion25Digest.txt
Normal file
File diff suppressed because it is too large
Load diff
338
doc/design/workflow/Verwijlen/Froscon26Agenda.txt
Normal file
338
doc/design/workflow/Verwijlen/Froscon26Agenda.txt
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
Workflow Workshop at FrOSCon-26 -- working Agenda
|
||||
=================================================
|
||||
:Date: 2026-07-27
|
||||
:Author: Hermann Voßeler (Compilation)
|
||||
:toc:
|
||||
|
||||
//MENU: label Agenda draft
|
||||
|
||||
|
||||
[abstract]
|
||||
Working agenda for the planned in-person meeting at FrOSCon-2026 in St.Augustin,
|
||||
continuing the workflow design discussion between _Wouter Verwijlen_ and the
|
||||
Lumiera team. The items collected here are derived from the status summaries of
|
||||
the -> link:Discussion25Digest.html[Workflow Discussion Digest]: every point
|
||||
marked as _open_ there is a candidate; this page arranges those points into
|
||||
workable blocks and indicates the intended outcome for each.
|
||||
|
||||
CAUTION: Ordering, weighting and the time budget are to be decided by the
|
||||
participants -- this is a menu to choose from, not a schedule.
|
||||
The compilation was assisted by Knowledge-Technology (»AI«),
|
||||
drawing on the status summaries of the discussion digest.
|
||||
|
||||
|
||||
Orientation
|
||||
-----------
|
||||
|
||||
Three topics stand out as _ripe for decision,_ since the discussion has already
|
||||
circled them from several sides and merely stops short of a conclusion:
|
||||
the *attachment gesture* (block 1), the *moving semantics* (block 2) and the
|
||||
cluster around *nested clips and vertical space* (block 5) -- the latter being
|
||||
the only topic that the digest itself flags as _``a prime candidate for the
|
||||
next in-person discussion''._
|
||||
|
||||
The remaining blocks are exploratory: they open ground that the eMail discussion
|
||||
either never reached (*Routing*), never treated systematically (*Trimming*),
|
||||
or left at the level of principles without tangible form (*tools and modes*).
|
||||
|
||||
Whenever a proposal is weighed during the meeting, the working principles
|
||||
distilled in the digest provide the yardstick:
|
||||
|
||||
- minimise the number of design elements
|
||||
- map down / simplify down to known software behaviour
|
||||
- the Gimp modifier pattern
|
||||
- modifiers as limited resource
|
||||
- power of defaults
|
||||
|
||||
And three standing checks apply to any concrete design considered here,
|
||||
each of them an open point carried along since FrOSCon-25:
|
||||
|
||||
- can the gesture be mapped coherently onto _all_ control systems --
|
||||
mouse, keyboard, pen, hardware controller?
|
||||
- does it avoid ``surprising or non-deterministic (suddenly jumping)
|
||||
behaviour'' -- the risk inherent in the placement concept?
|
||||
- how does it play out for each of the
|
||||
-> link:Discussion25Digest.html#personas[personas]?
|
||||
|
||||
|
||||
Preparation
|
||||
-----------
|
||||
|
||||
- Read (or skim) the -> link:Discussion25Digest.html[Workflow Discussion Digest];
|
||||
flag any point where the account misrepresents your position.
|
||||
- _Wouter:_ current state of the editing *scenario catalogue* -- even a partial
|
||||
collection provides test material for the blocks 1, 2 and 6.
|
||||
- _Hermann:_ check which parts of the timeline UI could serve for demonstration,
|
||||
even in rudimentary or mock-up form.
|
||||
- Collect any mockups and sketches produced since December 2025
|
||||
(connection visualisation, placement adjustment, palette layout).
|
||||
|
||||
|
||||
Block 1 -- Attaching clips: the decisive gesture
|
||||
------------------------------------------------
|
||||
_-- ready for decision --_
|
||||
|
||||
The point where the discussion paused in December 2025. Three status summaries
|
||||
converge onto this single question, which is a good indication of its weight:
|
||||
|
||||
- how the placement prototype is established when
|
||||
-> link:Discussion25Digest.html#adding[adding clips]
|
||||
- the ``UI mechanics'' ensuring that clips added to the timeline indeed
|
||||
get connected into clusters, whenever applicable
|
||||
(-> link:Discussion25Digest.html#grouping-devices[grouping devices])
|
||||
- how to establish the prototype in the first place
|
||||
(-> link:Discussion25Digest.html#prototypes[placement prototypes])
|
||||
|
||||
Three candidate designs are on the table:
|
||||
|
||||
1. *context rule at drop location* -- proximity decides;
|
||||
correction afterwards through the tool palette
|
||||
2. *the hooking gesture* -- a marked change of drag direction over the target;
|
||||
plus insertion by dropping onto the seam between two clips
|
||||
3. *arrow connect* -- drop onto a clip, then position in a second step
|
||||
|
||||
Regarding the hooking gesture, Wouter stated he ``would like to try this,
|
||||
to see how that would feel'' -- so if anything tangible can be shown at the
|
||||
meeting, this is where it pays off. Otherwise a walk-through on paper,
|
||||
against situations from the scenario catalogue.
|
||||
|
||||
Decisions to take along the way:
|
||||
|
||||
- how far the automatic mode reaches: magnetic only, or content-anchoring
|
||||
as well (Wouter argued content-anchoring should require a deliberate act)
|
||||
- the drop-location rules and their tuning: _how close is close?_
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: settle on one primary design (or a definite combination), spelled
|
||||
out precisely enough to be built; record the fallback option and the
|
||||
criterion that would prompt a revision.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 2 -- Moving, rearranging and collisions
|
||||
---------------------------------------------
|
||||
_-- ready for decision --_
|
||||
|
||||
-> link:Discussion25Digest.html#moving[Moving, rearranging and ripple behaviour]
|
||||
|
||||
Individually small questions, each with wide consequences:
|
||||
|
||||
Collision default:: blocking (with a push-through resistance) versus
|
||||
never-blocking / making way, as DaVinci Resolve does. Wouter suggested
|
||||
Kdenlive's default mode as a specimen of blocking behaviour to try out.
|
||||
Making way:: if clips move aside, may their magnetic connections break?
|
||||
And what is shown while the gesture is still in progress?
|
||||
Reordering:: the mechanism for drag-and-drop reordering under magnetic
|
||||
placement -- ``dragging while respecting all placements'' versus
|
||||
``dragging while ignoring the selected clip's children''. Which is default?
|
||||
Direction:: identical connection semantics for horizontal and vertical
|
||||
dragging were proposed; the reaction to overlaps remains to be defined.
|
||||
Modifier budget:: confirm the table -- Shift = add to selection,
|
||||
Alt = duplicate, Ctrl = the common ``alternative behaviour'' modifier
|
||||
following the Gimp pattern; snapping as a toggle rather than a modifier.
|
||||
|
||||
The discussion regarding a general *ripple-trim function* is parked; it need
|
||||
only be reopened should the decisions here or in block 1 change the premise
|
||||
-- namely, that most clips end up connected by placements anyway.
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: fix the defaults for collision behaviour and for reordering;
|
||||
confirm the modifier table; hand the remaining corner cases over to
|
||||
the scenario catalogue.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 3 -- Tools, modes and the contextual palette
|
||||
---------------------------------------------------
|
||||
_-- design work --_
|
||||
|
||||
-> link:Discussion25Digest.html#tools-modes[Tools, modes, views and the contextual tool palette]
|
||||
|
||||
The principles are agreed -- tools as primary system, a default navigation tool,
|
||||
contextual modes with fluid entry and exit, the palette as central device with
|
||||
the Gimp modifier pattern -- while _most aspects of the tangible manifestation_
|
||||
are still open. Notably:
|
||||
|
||||
- which modes are actually to be provided, and how they mesh up:
|
||||
an inventory, together with the transitions between them
|
||||
- how effect parameters and mixer strips can be reached under an overarching
|
||||
navigation tool -- explicitly unresolved since FrOSCon-25
|
||||
- and more generally: how far the navigation of the selection focus can
|
||||
span the whole UI, beyond the timeline
|
||||
- palette layout, and avoiding occlusion of the timeline content beneath it
|
||||
|
||||
This block underpins several others: the palette is the agreed place for
|
||||
correcting placement prototypes (block 1), for the trim sub-modes (block 6),
|
||||
and possibly for the gear switch (block 8).
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: a first inventory of the modes and tools intended, with their
|
||||
entry and exit conditions; agreement on what the palette must be able
|
||||
to display.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 4 -- Visual language
|
||||
--------------------------
|
||||
_-- exploration, mockups --_
|
||||
|
||||
-> link:Discussion25Digest.html#grouping-devices[Grouping devices] /
|
||||
-> link:Discussion25Digest.html#prototypes[Placement prototypes]
|
||||
|
||||
No existing NLE provides a model to copy for displaying clip connections;
|
||||
and colour as a cue is largely spent already, since clip colourisation is a
|
||||
vital organisational tool for the editor. To be worked on:
|
||||
|
||||
- indicating the placement type per clip -- lines and borders rather than colour
|
||||
- marking the *group leader* of an implicit group
|
||||
- indicators for collisions, and the _derivation_ display
|
||||
answering ``why does this clip sit here?''
|
||||
- the interplay of implicit groups with explicit multi-selection
|
||||
- the Priority-2 gesture cues: which of the interacting clips stay locked
|
||||
in place, which will move and in which direction
|
||||
(-> link:Discussion25Digest.html#gesture-concept[the gesture concept]) --
|
||||
noting that their feasibility depends on connectivity hints being pushed
|
||||
into the GUI beforehand, which is yet to be sounded out
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: a shortlist of visual candidates worth elaborating after the
|
||||
meeting; agreement on the minimum set of indicators and what each
|
||||
of them must convey.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 5 -- Nested clips, sections and vertical space
|
||||
-----------------------------------------------------
|
||||
_-- deep dive; flagged in the digest --_
|
||||
|
||||
-> link:Discussion25Digest.html#sections[Sections and the narrative structure] /
|
||||
-> link:Discussion25Digest.html#layering[Tracks, layering and vertical space]
|
||||
|
||||
The least resolved cluster of the Grouping area, and the one the digest
|
||||
itself marks for in-person discussion.
|
||||
|
||||
- can nested sequences actually deliver the lightweight, always-visible
|
||||
quality of Wouter's *sections* proposal -- drag-to-reorder of scenes,
|
||||
section headers in the ruler, per-section versioning? What is missing?
|
||||
- vertical screen real estate: inline rendering of nested content sharing
|
||||
the surrounding track space, taking the sketches from April 2025 as
|
||||
starting point (now reproduced in the digest)
|
||||
- a precise definition of the gradual *expand / collapse* steps, so that the
|
||||
overall timeline stays balanced and excess complexity can remain hidden
|
||||
- the *popup portal* idea needs elaboration -- including the misclick problem
|
||||
Wouter raised himself
|
||||
- the layering-order override (content fixed above a reference clip):
|
||||
define the practical test that FrOSCon-25 called for
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: a shared picture of how a nested clip appears in each of its states
|
||||
-- collapsed, condensed, expanded, opened -- concrete enough to be drawn
|
||||
up; and a decision whether sections require any dedicated support beyond
|
||||
nested sequences plus placements.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 6 -- Trimming
|
||||
-------------------
|
||||
_-- deep dive --_
|
||||
|
||||
-> link:Discussion25Digest.html#trimming[Trimming]
|
||||
|
||||
Trimming was touched frequently, yet never received a dedicated round;
|
||||
together with Routing it is the least covered core area -- while both
|
||||
participants consider it the very heart of the editor's work.
|
||||
|
||||
- walk through Wouter's document proposal as a whole: clip-based trim-side
|
||||
selection, the three commands, trim mode as a contextual mode, no sync locks
|
||||
- the interplay with placement connections in detail: what exactly ripples
|
||||
when trimming magnetic or content-anchored neighbours
|
||||
(the prototype behaviour matrix serves as reference)
|
||||
- clip selection versus trim-side selection: mutually exclusive, or toggled?
|
||||
And how does either respect a sticky selection, without introducing yet
|
||||
another mode?
|
||||
- keyboard bindings for the trim operations; dynamic trimming during playback
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: confirm or amend the document proposal as the working design for
|
||||
trimming; compile the list of trim situations to be added to the
|
||||
scenario catalogue.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 7 -- Routing groundwork
|
||||
-----------------------------
|
||||
_-- exploration: opening the quadrant --_
|
||||
|
||||
-> link:Discussion25Digest.html#tagging[Organising footage] /
|
||||
-> link:Discussion25Digest.html#layering[Layering]
|
||||
|
||||
The one area of the 2025 topic plan that was never entered. A caveat applies
|
||||
throughout: everything concerning the organisation of material within the
|
||||
session is to be treated as an indication of _``features we would like to
|
||||
have''_ -- the handling of »assets« in Lumiera is still at a very early,
|
||||
conceptual stage.
|
||||
|
||||
- default routing rules: what happens without any user intervention
|
||||
- tag-based routing in detail: tag taxonomy, the manual effort involved,
|
||||
automatic detection, and the relation to bins and the project view
|
||||
- explicit wiring (»plugs«): which user-visible form, if any, in a first
|
||||
iteration
|
||||
- mixing groups and sub-mixes -- and what replaces mute/solo per track,
|
||||
once the global track order is given up
|
||||
|
||||
[role="summary"]
|
||||
**********************************************************************
|
||||
Aim:: not decisions, but a structured map of the routing questions, so
|
||||
that this area can be worked through by eMail the way Grouping was --
|
||||
separating what can be settled now from what has to wait until the
|
||||
handling of assets has matured.
|
||||
**********************************************************************
|
||||
|
||||
|
||||
Block 8 -- Round-up
|
||||
-------------------
|
||||
_-- smaller items --_
|
||||
|
||||
Gear switch:: -> link:Discussion25Digest.html#gear[The gear switch] +
|
||||
Open: the activation model (sub-mode of tools versus always available for
|
||||
keyboard users); whether dedicated per-gear bindings should exist in
|
||||
parallel; to what degree and in which scope a gear setting is remembered;
|
||||
and the actual gear scales per context. Since feasibility presumably has
|
||||
to be determined by prototyping, the aim here is to scope such an
|
||||
experiment rather than to decide the design.
|
||||
Placement edge cases:: -> link:Discussion25Digest.html#prototypes[Placement prototypes] +
|
||||
From Wouter's catalogue: swapping clips of differing placement types,
|
||||
the inheritance of offsets, and the deletion of an anchor.
|
||||
Placement selection by keyboard:: -> link:Discussion25Digest.html#keybindings[Keybindings] +
|
||||
Cycling through the prototypes versus Blender-style key-popup menus --
|
||||
a choice that hinges on the number of prototypes remaining at three.
|
||||
Default keybindings:: -> link:Discussion25Digest.html#keybindings[Keybindings] +
|
||||
Still open, yet presumably premature: the concrete default bindings can
|
||||
only settle once the gesture vocabulary from the blocks 1, 3 and 6 has
|
||||
taken shape. Worth a brief decision whether to open this at all.
|
||||
|
||||
|
||||
Block 9 -- Process
|
||||
------------------
|
||||
|
||||
- publication path for the *scenario catalogue* (PDF -> Asciidoc, as was
|
||||
done with the Proposals document)
|
||||
- Wouter's offer to work directly in Asciidoc and Git for future updates
|
||||
of the »Workflow Proposals«
|
||||
- the road towards an actual *design document*
|
||||
(-> link:Discussion25Digest.html#method[working method]):
|
||||
which of the digest topics are ripe to be rewritten as specification,
|
||||
and in which order
|
||||
- applying the -> link:Discussion25Digest.html#personas[personas]:
|
||||
a first pass at ordering the agreed features by priority
|
||||
- the toolkit question (-> link:Discussion25Digest.html#scope[scope and priorities]):
|
||||
no decision is pending and no change of course intended, yet Wouter's
|
||||
concern is on record and the direction of GTK development is being
|
||||
watched -- a brief status exchange, if desired
|
||||
- rhythm and channel for continuing the discussion after the meeting
|
||||
|
|
@ -4,6 +4,8 @@ Meeting at FrOSCon 2025
|
|||
:Author: Benny Lyons and Hermann Voßeler
|
||||
:toc:
|
||||
|
||||
//MENU: label Froscon-25
|
||||
|
||||
Present::
|
||||
- Wouter Verwijlen
|
||||
- Benny Lyons
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Lumiera Workflow Proposals
|
||||
==========================
|
||||
:Author: Wouter Verwijlen
|
||||
:Date: 16 March 2026
|
||||
:Date: 19 July 2026
|
||||
:TOC:
|
||||
|
||||
I would like to share a collection of workflow ideas for Lumiera. These ideas come from
|
||||
|
|
@ -310,7 +310,8 @@ rearranged, in contrast to Text View, which only supports automatic sorting.
|
|||
|
||||
* Metadata View (Resolve): similar to Avid's Script View, except you can't reorder clips.
|
||||
|
||||
* Filmstrip View (Final Cut Pro): similar to Icon View, but [red]#TODO: missing text??#
|
||||
* Filmstrip View (Final Cut Pro): similar to Icon View, but
|
||||
multiple thumbnails might be disaplayed for each clip depending on the clip’s length.
|
||||
|
||||
Avid is famous for allowing you to save your Text or Script View configuration as a preset that can
|
||||
be quickly recalled. Premiere allows this through its Metadata Display window, Final Cut Pro by
|
||||
|
|
@ -711,6 +712,7 @@ mode/tool system. FCP's Select and Position tools also change the behaviour of t
|
|||
can therefore indeed also be considered modes, which gives us a hybrid design as well. Specialised
|
||||
views can be combined with tools or modes.
|
||||
|
||||
[[contextual_bar]]
|
||||
To conclude: all methods have pros and cons. Tools, modes and views aren't mutually exclusive, so
|
||||
I think we should use whichever method or combination of methods is appropriate for different
|
||||
actions in Lumiera. Whenever we can, we should try to avoid all three and design the app to be as
|
||||
|
|
@ -721,7 +723,7 @@ side automatically enters trim mode. However, a user does still need to explicit
|
|||
|
||||
Regardless of the method, immediate visual differentiation of tools/modes/views is important and
|
||||
I'd like this to extend to any timeline option that can be toggled on/off. Therefore I'd like to propose
|
||||
the use of a _contextual bar_ that appears over the bottom part of the timeline whenever a contextual
|
||||
the use of a *contextual bar* that appears over the bottom part of the timeline whenever a contextual
|
||||
mode is active.
|
||||
|
||||
.Contextual bar with options for the mode/tool
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ Date: 2025
|
|||
|
||||
//MENU: label Workflow Proposals Verwijlen
|
||||
//MENU: prepend WorkflowProposals
|
||||
//MENU: append child Froscon26Agenda
|
||||
|
||||
************************
|
||||
Wouter Verwijlen is a documentary filmmaker from Utrecht (Netherlands).
|
||||
|
|
@ -22,4 +23,5 @@ this handling might be improved.
|
|||
|
||||
- link:WorkflowProposals.html[»Lumiera Workflow Proposals«] by Wouter Verwijlen
|
||||
- link:FrosconMeeting.html[Discussion at FrOSCon 2025]
|
||||
|
||||
- link:Discussion25Digest.html[A Digest of the discussion in 2025]
|
||||
- link:Froscon26Agenda.html[(draft) Agenda for further discussion]
|
||||
|
|
|
|||
|
|
@ -200,9 +200,9 @@ Instead, Ticket https://issues.lumiera.org/ticket/1212[#1212 »Extension Concept
|
|||
was added to the list of relevant
|
||||
https://issues.lumiera.org/report/17[»Focus Topics«] for further development.
|
||||
|
||||
.........
|
||||
{nbsp}
|
||||
|
||||
.........
|
||||
{nbsp}
|
||||
|
||||
''''
|
||||
Back to link:/x/DesignProcess.html[Lumiera Design Process overview]
|
||||
|
|
|
|||
|
|
@ -87,10 +87,11 @@ textual replacements and environment variables, rather looking for ways to
|
|||
record any application of rules or facts in the global _Event Log._
|
||||
|
||||
Ichthyostega:: '2025-09-20'
|
||||
....
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
~What follows below is extended documentation for _Cehteh_'s Config System proposal...~
|
||||
|
||||
|
|
|
|||
|
|
@ -134,11 +134,13 @@ We have now a ``compatibility wiki'', finalized this proposal
|
|||
ct:: '2008-03-26T13:43:26Z'
|
||||
|
||||
''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
Assessment 2025
|
||||
~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ Made this `final', this proposal got accepted and is already in use without
|
|||
much discussion
|
||||
|
||||
ct:: '2007-06-27T16:07:13Z'
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
[large]#🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘 🌑#
|
||||
|
||||
[underline]#Historical remark#: This was ultimately the first
|
||||
|
|
|
|||
|
|
@ -120,11 +120,13 @@ struct budget
|
|||
------------------------------------------------------------------------------
|
||||
|
||||
ct:: '2010-07-23 20:55:49 CEST'
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
[[caveat]]
|
||||
|
|
|
|||
|
|
@ -181,11 +181,13 @@ struct profile
|
|||
------------------------------------------------------------------------------
|
||||
|
||||
ct:: '2010-07-23 20:33:13 CEST'
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
[[caveat]]
|
||||
|
|
|
|||
|
|
@ -33,13 +33,16 @@ We decided to use a TiddlyWiki for now until this is further worked out
|
|||
|
||||
ct:: '2008-03-08T03:38:50Z'
|
||||
|
||||
....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
....
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
.looking back at another era...
|
||||
What shall I say, just Whew! 😖
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
Before Width: | Height: | Size: 231 KiB After Width: | Height: | Size: 256 KiB |
File diff suppressed because it is too large
Load diff
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 83 KiB |
|
|
@ -124,11 +124,13 @@ Special _alias targets_ may be defined, based on other targets, to set off stand
|
|||
build sequences. Notably, a _default_ target can be defined for the build.
|
||||
|
||||
''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
Organisation of the Lumiera SCons build
|
||||
|
|
|
|||
|
|
@ -201,11 +201,13 @@ cache xref:caching[<-] and profiling xref:profiling[<-].
|
|||
// ################################################
|
||||
|
||||
''''''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
The visible Universe
|
||||
|
|
@ -351,11 +353,13 @@ purposes, and the contents of a clip bin can be played like a storyboard.
|
|||
// ################################################
|
||||
|
||||
''''''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
Dark Matter
|
||||
|
|
@ -552,11 +556,13 @@ Integration with external libraries, frameworks and applications
|
|||
[red]#to be written#
|
||||
|
||||
''''''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
Further reading
|
||||
|
|
|
|||
|
|
@ -280,12 +280,14 @@ TIP: The `mk-build-deps` tool reads the package manifest and fabricates a
|
|||
''''
|
||||
~What follows is background information and configuration for advanced users.~ +
|
||||
^💡 You can safely skip and ignore the remainder of this page if in doubt.^
|
||||
....
|
||||
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -315,11 +315,13 @@ Current goals of development::
|
|||
|
||||
|
||||
''''
|
||||
....
|
||||
|
||||
|
||||
|
||||
....
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
{nbsp}
|
||||
|
||||
|
||||
Do you have any further ideas?
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ namespace engine{
|
|||
|
||||
|
||||
void
|
||||
sendToOutput (vault::out::DataSink)
|
||||
sendToOutput (vault::out::OutputSlot::DataSink&)
|
||||
{
|
||||
UNIMPLEMENTED ("set up dispatcher to start calculating and feeding to the given output sink");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,13 +48,14 @@ namespace engine {
|
|||
using std::make_pair;
|
||||
using mobject::ModelPort;
|
||||
using vault::out::Timings;
|
||||
using vault::out::DataSink;
|
||||
using lib::time::TimeValue;
|
||||
using lib::time::TimeSpan;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::time::FSecs;
|
||||
using lib::time::Time;
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
/**
|
||||
* Internal abstraction: a service within the engine
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ using std::function;
|
|||
using std::placeholders::_1;
|
||||
using lib::iter_source::transform;
|
||||
using lib::append_all;
|
||||
using vault::out::DataSink;
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
namespace steam {
|
||||
namespace engine{
|
||||
|
|
@ -73,7 +74,7 @@ namespace engine{
|
|||
Quality serviceQuality)
|
||||
{ //////////////////////////////////////////////////TICKET #1301 : prepare proper Dispatcher here, including translation ModelPort -> portIDX
|
||||
RenderEnvironment& renderConfig = configureCalculation (mPort,nominalTimings,serviceQuality);
|
||||
function<CalcStream(DataSink)> triggerRenderStart = bind (activateCalculation, _1, ref(renderConfig));
|
||||
function<CalcStream(DataSink&)> triggerRenderStart = bind (activateCalculation, _1, ref(renderConfig));
|
||||
|
||||
CalcStreams runningCalculations;
|
||||
append_all (transform (outputSlot.getOpenedSinks()
|
||||
|
|
@ -106,7 +107,7 @@ namespace engine{
|
|||
* to be re-configured and adjusted while running.
|
||||
*/
|
||||
CalcStream
|
||||
EngineService::activateCalculation (DataSink sink, RenderEnvironment& engineCallback)
|
||||
EngineService::activateCalculation (DataSink& sink, RenderEnvironment& engineCallback)
|
||||
{
|
||||
CalcStream calcStream(engineCallback);
|
||||
calcStream.sendToOutput (sink); ////////////////////////////////////////////////////TICKET #1297 : need to re-think the association ModelPort ⟷ output sink
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ namespace engine{
|
|||
friend class EngineDiagnostics;
|
||||
|
||||
private:
|
||||
static CalcStream activateCalculation (vault::out::DataSink, RenderEnvironment&);
|
||||
static CalcStream activateCalculation (vault::out::OutputSlot::DataSink&, RenderEnvironment&);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,11 @@ namespace engine {
|
|||
using lib::time::Time;
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::Duration;
|
||||
using vault::out::DataSink;
|
||||
using vault::out::Timings;
|
||||
using vault::gear::Job;
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -47,8 +47,19 @@ namespace mem {
|
|||
|
||||
/**
|
||||
* Framework for configuring the actual BufferPovider backend implementation.
|
||||
* The functionality is provided by the »two pillars«: BufferState (metadata)
|
||||
* and BufferStore (storage provider). The actual functionality exposed through
|
||||
* this API can be adapted in a wide range by using suitable back-ends
|
||||
* - the NaiveBufferSetup ignores aspects of performance and concurrency,
|
||||
* but allows to investigate each requested buffer after the fact
|
||||
* - a _production-grade implementation_ uses thread-local services
|
||||
* in concert with the EngineBufferMetadata and EngineBufferManager.
|
||||
* Allocations are exchanged via asynchronous messaging.
|
||||
*
|
||||
* @todo WIP-WIP-WIP as of 2/2026 -- reworking BufferProvider implementation ////////////////////////////TICKET #1410
|
||||
* Generally speaking, this template controls the actual setup by delegating
|
||||
* to the CONF policy passed as template argument. Furthermore, proxying
|
||||
* of already instantiated back-ends is [supported](\ref #decorate)
|
||||
* for subclasses (notably the DiagnosticBufferProvider)
|
||||
*/
|
||||
class BufferProviderSetup
|
||||
: public BufferProvider
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ namespace mem {
|
|||
size_t buffSize() const;
|
||||
|
||||
operator HashVal() const { return subClassification_; }
|
||||
friend bool operator== (BuffDescr const&, BuffDescr const&) =default;
|
||||
|
||||
/** callback to engage buffer handling cycle */
|
||||
uint announce (uint count);
|
||||
|
|
|
|||
|
|
@ -53,8 +53,6 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
//#include "lib/hash-value.h"
|
||||
#include "include/logging.h"
|
||||
#include "vault/mem/buffer-metadata.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/sync.hpp"
|
||||
|
|
@ -64,7 +62,6 @@
|
|||
|
||||
namespace vault{
|
||||
namespace mem {
|
||||
// namespace err = lumiera::error;
|
||||
|
||||
using std::move;
|
||||
using util::unConst;
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ namespace out {
|
|||
* where each element is a DataSink functor that uses this connection
|
||||
* to start an output cycle by locking a buffer by invoking Connection::claimBufferFor(FrameID)
|
||||
* @param lifecycleManager the ref-counting shared_ptr which controls the lifecycle of this Allocation
|
||||
* @note two statefull objects are embedded into the DataSink functor
|
||||
* @note two statefull objects are embedded into the DataSink handle
|
||||
* - a BuffDescr preconfigured to point into this connection, using the proper buffer size
|
||||
* - the \a lifecycleManager, so that the DataSink participates in the ref-count
|
||||
*/
|
||||
|
|
@ -207,14 +207,8 @@ namespace out {
|
|||
connect (PAlloc& lifecycleManager) override
|
||||
{
|
||||
auto buildDataSink = [&](CON& connection)
|
||||
{ // setup functor to lock a buffer using this output connection
|
||||
return DataSink{[bufferDescriptor = bufferProxy_.getDescriptorFor (connection)
|
||||
,lifecycleManager
|
||||
]
|
||||
(FrameID frame) mutable -> BuffHandle
|
||||
{
|
||||
return bufferDescriptor.lockBuffer (frame);
|
||||
}};
|
||||
{ // attach to ref-count preconfigure output connection to address
|
||||
return DataSink{lifecycleManager, bufferProxy_.getDescriptorFor (connection)};
|
||||
};
|
||||
|
||||
return lib::explore (connections_)
|
||||
|
|
@ -261,7 +255,7 @@ namespace out {
|
|||
}
|
||||
|
||||
/**
|
||||
* @remark in moste cases this variant is preferable,
|
||||
* @remark for most cases this variant is preferable,
|
||||
* since it creates all connection objects from the same set of constructor arguments
|
||||
* @note args will be copied into a _populator functor_ that is then applied for each connection to create
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
namespace vault {
|
||||
namespace out {
|
||||
|
||||
using vault::mem::BuffDescr;
|
||||
using vault::mem::BuffHandle;
|
||||
using lib::time::FrameCnt;
|
||||
using std::unique_ptr;
|
||||
|
|
@ -90,37 +91,6 @@ namespace out {
|
|||
using FrameID = FrameCnt;
|
||||
|
||||
|
||||
/**
|
||||
* Handle to represent an opened connection ready to receive media data for output.
|
||||
* Each DataSink (handle) corresponds to an OutputSlot::Connection entry. Data is
|
||||
* published frame wise in a two-phase protocol: in the first stage, the client
|
||||
* gets exclusive access to an output buffer. Once the data is ready for output,
|
||||
* the client signals `emit()` and the buffer enters the second stage, where the
|
||||
* intern output mechanism of that specific connection gets exclusive access.
|
||||
*
|
||||
* The DataSink handle is a _functor_ and can be invoked with a frame number,
|
||||
* to start such a two-phase transaction for _this specific frame._ The result
|
||||
* is a BuffHandle, to be used in the usual way as with a BufferProvider:
|
||||
* - the client can access the buffer memory, either raw or with a forced cast
|
||||
* - the client invokes BuffHandle::emit() to indicate that data is ready for output
|
||||
* - once the client is done with that handle, it **must** invoke BuffHandle::release()
|
||||
* Each output mechanism defines specific constraints regarding the time window when
|
||||
* to get such a BuffHandle and when `emit()` must have been called. These constraints
|
||||
* are indicated by the vault::out::Timings, that can be retrieved from the OutputSlot.
|
||||
*
|
||||
* @note DataSink embeds a ref-counting handle to detect automatically when some
|
||||
* connection can be released; the _allocation of an OutputSlot_ is released
|
||||
* once all connections were discarded.
|
||||
*/
|
||||
class DataSink
|
||||
: public std::function<BuffHandle(FrameID)>
|
||||
{
|
||||
public:
|
||||
/////////////////////////////////////////////TICKET #1377 : presumably we want some stream-type hash ID here -- to allow building a processing-ID for each Render Node invocation
|
||||
};
|
||||
|
||||
|
||||
|
||||
/****************************************************************************//**
|
||||
* Interface: Generic output sink.
|
||||
* An OutputSlot represents the possibility to send data through multiple
|
||||
|
|
@ -143,6 +113,7 @@ namespace out {
|
|||
{
|
||||
|
||||
public:
|
||||
class DataSink;
|
||||
using OpenedSinks = lib::IterSource<DataSink>::iterator;
|
||||
|
||||
OpenedSinks getOpenedSinks();
|
||||
|
|
@ -217,6 +188,63 @@ namespace out {
|
|||
|
||||
template<class CON, class FUN>
|
||||
static OutputSlot allocate (size_t cnt, FUN&& populator);
|
||||
|
||||
|
||||
|
||||
/* ========== Data Sink handles ========== */
|
||||
|
||||
/**
|
||||
* Handle to represent an opened connection ready to receive media data for output.
|
||||
* Each DataSink (handle) corresponds to an OutputSlot::Connection entry. Data is
|
||||
* published frame wise in a two-phase protocol: in the first stage, the client
|
||||
* gets exclusive access to an output buffer. Once the data is ready for output,
|
||||
* the client signals `emit()` and the buffer enters the second stage, where the
|
||||
* intern output mechanism of that specific connection gets exclusive access.
|
||||
*
|
||||
* The DataSink handle is essentially a _functor_ and can be invoked with some
|
||||
* frame number, to start such a two-phase transaction for _this specific frame._
|
||||
* The result is a BuffHandle, to be used in the usual way as with a BufferProvider:
|
||||
* - the client can access the buffer memory, either raw or with a forced cast
|
||||
* - the client invokes BuffHandle::emit() to indicate that data is ready for output
|
||||
* - once the client is done with that handle, it **must** invoke BuffHandle::release()
|
||||
* Each output mechanism defines specific constraints regarding the time window when
|
||||
* to get such a BuffHandle and when `emit()` must have been called. These constraints
|
||||
* are indicated by the vault::out::Timings, that can be retrieved from the OutputSlot.
|
||||
* @remark DataSink has reference semantics and thus an implied identity, that is
|
||||
* tied to the backing active output configuration and the data feed number.
|
||||
* @note DataSink embeds a ref-counting handle to detect automatically when some
|
||||
* connection can be released; the _allocation of an OutputSlot_ is released
|
||||
* once all connections were discarded.
|
||||
*/
|
||||
class DataSink
|
||||
: util::NonAssign
|
||||
{
|
||||
PAlloc lifeCount_;
|
||||
BuffDescr outputBufferType_;
|
||||
|
||||
template<class CON, bool isTest>
|
||||
friend class AllocState; // created with back-link
|
||||
|
||||
DataSink (PAlloc const& instance, BuffDescr type)
|
||||
: lifeCount_{instance}
|
||||
, outputBufferType_{type}
|
||||
{ }
|
||||
|
||||
public:
|
||||
BuffHandle
|
||||
lockFrame (FrameID frame)
|
||||
{
|
||||
return outputBufferType_.lockBuffer (frame);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////TICKET #1377 : presumably we want some stream-type hash ID here -- to allow building a processing-ID for each Render Node invocation
|
||||
friend bool
|
||||
operator== (DataSink const& s1, DataSink const& s2)
|
||||
{
|
||||
return s1.lifeCount_ == s2.lifeCount_
|
||||
and s1.outputBufferType_ == s2.outputBufferType_;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -79,11 +79,12 @@ namespace test {
|
|||
using util::isnil;
|
||||
using util::isSameObject;
|
||||
using fixture::Segmentation;
|
||||
using vault::out::DataSink;
|
||||
using vault::RealClock;
|
||||
using vault::gear::Job;
|
||||
using vault::gear::JobClosure;
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -405,7 +406,7 @@ namespace test {
|
|||
/**
|
||||
* Test support: verify the given Job is consistent with this Dispatcher.
|
||||
*/
|
||||
bool verify(Job const& job, ModelPort const& port, vault::out::DataSink const& sink)
|
||||
bool verify(Job const& job, ModelPort const& port, DataSink const& sink)
|
||||
{
|
||||
if (not dummySetup_.isSupported (port, sink)) return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -389,21 +389,10 @@ namespace test {
|
|||
auto [port0,sink0] = dispatcher.getDummyConnection(0);
|
||||
auto [port1,sink1] = dispatcher.getDummyConnection(1);
|
||||
CHECK (port0 != port1);
|
||||
UNIMPLEMENTED ("Identity of a DataSink");
|
||||
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code to allow refactoring of OutputSlot
|
||||
CHECK (sink0 != sink1);
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
|
||||
CHECK (port0.isValid());
|
||||
CHECK (port1.isValid());
|
||||
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code to allow refactoring of OutputSlot
|
||||
/////////////////////////OOO seemingly this is no longer possible (and unnecessary), since DataSink participates in the ref-count now
|
||||
CHECK (sink0.isValid());
|
||||
CHECK (sink1.isValid());
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
|
||||
CHECK (not ModelPort().isValid());
|
||||
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code to allow refactoring of OutputSlot
|
||||
CHECK (not DataSink().isValid());
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
|
||||
|
||||
CHECK (0 == dispatcher.resolveModelPort(port0));
|
||||
CHECK (1 == dispatcher.resolveModelPort(port1));
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
** _component integration test_ -- yet for this to work, we'd need
|
||||
** - a fake calculation
|
||||
** - a fake OutputSlot
|
||||
** @todo 5/2026 now I have beefed up the OutputSlot so that it can receive
|
||||
** two feeds with an arbitrary number of frames. Still not sure if it's needed;
|
||||
** however it should be noted that the MockDispatcher relies on the DummyPlayConnection,
|
||||
** which in turn uses SimulatedBuilderContext, mostly to get _just some model ports_.
|
||||
**
|
||||
** @see mock-dispatcher.hpp
|
||||
** @see JobPlanningPipeline_test
|
||||
|
|
@ -47,6 +51,8 @@
|
|||
#define STEAM_PLAY_DUMMY_BUILDER_CONTEXT_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "steam/mobject/model-port.hpp"
|
||||
#include "steam/fixture/model-port-registry.hpp"
|
||||
#include "vault/out/output-slot-connection.hpp"
|
||||
|
|
@ -61,56 +67,99 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
|
||||
|
||||
namespace steam{
|
||||
namespace play {
|
||||
namespace test {
|
||||
namespace err = lumiera::error;
|
||||
|
||||
using fixture::ModelPortRegistry;
|
||||
using vault::mem::Buff;
|
||||
using vault::mem::BuffHandle;
|
||||
using vault::out::DataSink;
|
||||
using vault::out::FrameID;
|
||||
using lib::time::TimeValue;
|
||||
using util::_Fmt;
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo 5/2023 quick-n-dirty placeholder to be able to fabricate fake DataSink handles (`Handle<Connection>`)
|
||||
*/ /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : after the refactoring of OutputSlot 3/2026 it is not that easy any more. We need a Fake OutputSlot
|
||||
class UnimplementedConnection
|
||||
* A placeholder/dummy OutputSlot that captures all
|
||||
* delivered output data into heap-allocated memory.
|
||||
* @todo 5/2026 it can now be created and allocates a fixed layout
|
||||
* of buffer storage, which implies it should not be copied
|
||||
* and is thus used differently than a real OutputSlot;
|
||||
* should reconsider what is actually needed here!
|
||||
* @warning this used to be placeholder code, and now it was adapted
|
||||
* to store frames, yet this new functionality is untested as of 5/2026
|
||||
*/
|
||||
struct FakeOutput
|
||||
: OutputSlot
|
||||
, util::NonCopyable
|
||||
{
|
||||
static constexpr uint NUM_FEEDS = 2;
|
||||
static constexpr bool SINGLE_THREADED_TEST = true;
|
||||
|
||||
using DummyDat = uint64_t;
|
||||
static constexpr size_t DUMMY_BUFF_SIZ = 1_KiB * sizeof(DummyDat);
|
||||
|
||||
using DummyBuff = std::array<DummyDat, DUMMY_BUFF_SIZ>;
|
||||
using DummyBuffSeq = std::deque<DummyBuff>;
|
||||
|
||||
/**
|
||||
* @todo 5/2026 quick-n-dirty placeholder code
|
||||
* to expose some output buffers and provide
|
||||
* the corresponding DataSink handles
|
||||
*/
|
||||
class DummyOutputConnection
|
||||
: public vault::out::OutputSlot::Connection
|
||||
{
|
||||
size_t getBufferSize() const override { UNIMPLEMENTED ("getBufferSize()"); }
|
||||
DummyBuffSeq& buffers_;
|
||||
|
||||
size_t getBufferSize() const override { return DUMMY_BUFF_SIZ; }
|
||||
Buff* claimBufferFor (FrameID) override;
|
||||
void publish (Buff*) override { UNIMPLEMENTED ("publish buffer content"); }
|
||||
void release (Buff*) override { UNIMPLEMENTED ("complete buffer cycle"); }
|
||||
void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); }
|
||||
void publish (Buff*) override { /* fake "publish buffer content" */ }
|
||||
void release (Buff*) override { /* fake "complete buffer cycle" */ }
|
||||
void shutDown () override { /* fake "shutDown() Connection" */ }
|
||||
|
||||
public:
|
||||
~UnimplementedConnection();
|
||||
UnimplementedConnection() = default;
|
||||
~DummyOutputConnection();
|
||||
DummyOutputConnection (DummyBuffSeq& buffers)
|
||||
: buffers_{buffers}
|
||||
{ }
|
||||
// OutputSlot::Connection is non-copyable
|
||||
};
|
||||
|
||||
using Connections = lib::ScopedCollection<DummyOutputConnection>;
|
||||
using MockConnectState = OutputSlot::AllocState<DummyOutputConnection, SINGLE_THREADED_TEST>;
|
||||
|
||||
/**
|
||||
* A placeholder/dummy OutputSlot
|
||||
* @todo 5/2026 it can barely be created right now;
|
||||
* should reconsider what is actually needed here!
|
||||
*/
|
||||
struct FakeSlot
|
||||
: OutputSlot
|
||||
using OutBuffs = std::array<DummyBuffSeq, NUM_FEEDS>;
|
||||
OutBuffs buffers_{};
|
||||
|
||||
FakeOutput()
|
||||
: OutputSlot{std::make_unique<MockConnectState> (NUM_FEEDS
|
||||
,[&,i=0](auto& storage) mutable
|
||||
{
|
||||
static constexpr uint NUM_CONNECTIONS = 2;
|
||||
static constexpr bool SINGLE_THREADED_TEST = true;
|
||||
using Connections = lib::ScopedCollection<UnimplementedConnection>;
|
||||
using MockConnectState = OutputSlot::AllocState<UnimplementedConnection, SINGLE_THREADED_TEST>;
|
||||
|
||||
FakeSlot()
|
||||
: OutputSlot{std::make_unique<MockConnectState> (NUM_CONNECTIONS, Connections::fill())}
|
||||
storage.template create<DummyOutputConnection> (buffers_[i++]);
|
||||
})}
|
||||
{ }
|
||||
};
|
||||
|
||||
inline Buff*
|
||||
FakeOutput::DummyOutputConnection::claimBufferFor (FrameID fra)
|
||||
{
|
||||
REQUIRE (fra >= 0);
|
||||
if (uint64_t(fra) >= buffers_.size())
|
||||
buffers_.resize(fra+10);
|
||||
return reinterpret_cast<Buff*> (& buffers_[fra]);
|
||||
}
|
||||
|
||||
|
||||
using asset::Pipe;
|
||||
using asset::PPipe;
|
||||
using asset::Struct;
|
||||
|
|
@ -159,6 +208,8 @@ namespace test {
|
|||
std::vector<ModelPort> modelPorts_;
|
||||
std::vector<DataSink> dataSinks_;
|
||||
|
||||
FakeOutput fakeOutput_;
|
||||
|
||||
/** setup */
|
||||
SimulatedBuilderContext()
|
||||
: registry_()
|
||||
|
|
@ -194,11 +245,9 @@ namespace test {
|
|||
modelPorts_.push_back (ModelPort(pipeB));
|
||||
|
||||
// prepare corresponding placeholder DataSink (a fake active output connection)
|
||||
FakeSlot fakeSlot;
|
||||
dataSinks_.clear();
|
||||
lib::explore(fakeSlot.getOpenedSinks())
|
||||
lib::explore(fakeOutput_.getOpenedSinks())
|
||||
.effuse (dataSinks_);
|
||||
// Note: keeps FakeSlot alive (due to embedded ref-count)...
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -216,12 +265,32 @@ namespace test {
|
|||
,dataSinks_[index]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
inline vault::mem::Buff* UnimplementedConnection::claimBufferFor (FrameID)
|
||||
/**
|
||||
* Access a generated output frame.
|
||||
* @remark FakeOutput maintains Deque-Storage for each feed.
|
||||
* @tparam X data type to assume as content of the frame (only checked for size)
|
||||
* @param feed number of the output feed to access
|
||||
* @param frame frame numer within the output feed
|
||||
*/
|
||||
template<typename X>
|
||||
X&
|
||||
accessOutputBufferAs (uint feed, FrameID frame)
|
||||
{
|
||||
UNIMPLEMENTED ("claimBufferFor(FrameID)");
|
||||
static_assert (sizeof(X) <= FakeOutput::DUMMY_BUFF_SIZ
|
||||
,"desired data type would not fit into FakeOutput buffer");
|
||||
if (feed >= FakeOutput::NUM_FEEDS)
|
||||
throw err::Invalid{_Fmt{"Feed #%i > %i allowed"}
|
||||
% feed % (FakeOutput::NUM_FEEDS-1)};
|
||||
|
||||
auto availFrames = fakeOutput_.buffers_[feed].size();
|
||||
if (frame >= availFrames)
|
||||
throw err::Invalid{_Fmt{"Frame #%02i > %02i available frames"}
|
||||
% frame % availFrames};
|
||||
|
||||
return reinterpret_cast<X&> (fakeOutput_.buffers_[feed][frame]);
|
||||
}
|
||||
};
|
||||
|
||||
}}} // namespace steam::play::test
|
||||
#endif /*STEAM_PLAY_DUMMY_BUILDER_CONTEXT_H*/
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace test {
|
|||
|
||||
} // (End) hidden service impl details
|
||||
|
||||
UnimplementedConnection::~UnimplementedConnection() { } ///< emit VTable here...
|
||||
FakeOutput::DummyOutputConnection::~DummyOutputConnection() { } ///< emit VTable here...
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ namespace steam{
|
|||
namespace play {
|
||||
namespace test {
|
||||
|
||||
// using std::string;
|
||||
using lib::time::Duration;
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
|
||||
|
|
@ -143,11 +143,8 @@ namespace test {
|
|||
if (port == *it)
|
||||
{
|
||||
auto [refPort, refSink] = getModelPort(i);
|
||||
UNIMPLEMENTED ("need a fake OutputSlot and need to place some Stream-ID into the data sink");
|
||||
#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code to allow refactoring of the OutputSlot default implementation
|
||||
if (refSink == sink)
|
||||
return true;
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -891,7 +891,7 @@ namespace test {
|
|||
CHECK (micros > 80);
|
||||
|
||||
micros = cpuLoad.benchmark();
|
||||
CHECK (micros < 110);
|
||||
CHECK (micros < 115);
|
||||
CHECK (micros > 90);
|
||||
|
||||
cpuLoad.timeBase = 1ms;
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ namespace vault::out::test {
|
|||
CHECK (not sinks);
|
||||
|
||||
log.event("lock buffer");
|
||||
BuffHandle han = sink(FRA_NO);
|
||||
BuffHandle han = sink.lockFrame (FRA_NO);
|
||||
CHECK (han);
|
||||
CHECK (han.size() == sizeof(DUMMY_BUFF));
|
||||
CHECK (isSameAdr (han.rawStorage(), &DUMMY_BUFF));
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ namespace vault{
|
|||
namespace out {
|
||||
namespace test {
|
||||
|
||||
using DataSink = vault::out::OutputSlot::DataSink;
|
||||
|
||||
|
||||
|
||||
/***************************************************************//**
|
||||
|
|
@ -80,15 +82,15 @@ namespace test {
|
|||
// within the frame-calculation "loop"
|
||||
// we perform a data exchange cycle
|
||||
auto MARK_0 = markTime();
|
||||
BuffHandle buff00 = sink0(START);
|
||||
BuffHandle buff10 = sink1(START);
|
||||
BuffHandle buff00 = sink0.lockFrame (START);
|
||||
BuffHandle buff10 = sink1.lockFrame (START);
|
||||
|
||||
// rendering process calculates content....
|
||||
buff00.emplace<TestFrame> (testData(0,0));
|
||||
|
||||
// while further frames might be processed in parallel
|
||||
auto MARK_1 = markTime();
|
||||
BuffHandle buff11 = sink1(START+1);
|
||||
BuffHandle buff11 = sink1.lockFrame (START+1);
|
||||
buff11.emplace<TestFrame> (testData(1,1));
|
||||
buff10.emplace<TestFrame> (testData(1,0));
|
||||
|
||||
|
|
|
|||
|
|
@ -5179,7 +5179,7 @@ Thus the mapping is a copyable value object, using an associative array. It may
|
|||
First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="OutputSlot" modifier="Ichthyostega" created="201106162339" modified="202604110029" tags="def Concepts Player spec img" changecount="29">
|
||||
<div title="OutputSlot" modifier="Ichthyostega" created="201106162339" modified="202606011947" tags="def Concepts Player spec img" changecount="30">
|
||||
<pre>Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
|
||||
This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, using a beamer, sound, Jack, rendering to file) can be added and removed dynamically from various components (Vault, Stage), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
|
||||
|
||||
|
|
@ -5237,7 +5237,7 @@ Thus there are two serious problem situations
|
|||
&rarr; OutputSlotImpl
|
||||
</pre>
|
||||
</div>
|
||||
<div title="OutputSlotDesign" modifier="Ichthyostega" created="201112190322" modified="202604102358" tags="design decision" changecount="13">
|
||||
<div title="OutputSlotDesign" modifier="Ichthyostega" created="201112190322" modified="202606011945" tags="design decision" changecount="15">
|
||||
<pre>The OutputSlot interface describes a point where generated media data can actually be sent to the external world. It is expected to be implemented by adapters and bridges to existing drivers or external interface libraries, like a viewer widget in the GUI, ALSA or Jack sound output, rendering to file, using an external media format library. The design of this core facility was rather difficult and stretched out over quite some time span -- this page documents the considerations and the decisions taken.
|
||||
|
||||
!intention
|
||||
|
|
@ -5272,7 +5272,8 @@ Besides the sink handles, allocation of an output slot defines some timing const
|
|||
|
||||
{{red{WIP 3/2026}}} after a long break, we are now facing the problem how to make BufferProvider and OutputSlot work together. Both define a protocol and data exchange models -- and both exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
|
||||
* For handles in the Render Engine we can generally assume that the regular call path is always completed, which ensures that every handle gets released. Exceptions may happen, but are //not handled// -- rather the Engine will be shut down in such a case, which could be arranged to discard all buffer memory unconditionally
|
||||
* Regarding the »Output Slot Protocol« it is not clear yet which part will be responsible for checking the [[timing constraints|Timings]]; the actual {{{Connection}}} implementation looks like a plausible candidate. Furthermore, it is not clear yet {{red{3/26}}} where precisely an underlying buffer provider will be integrated, and if doing so is mandatory
|
||||
* Regarding the »Output Slot Protocol« it is not clear yet which part will be responsible for checking the [[timing constraints|Timings]]; the actual {{{Connection}}} implementation looks like a plausible candidate. The latter is also responsible for providing the actual data buffer, which is deemed an implementation detail.
|
||||
* The {{{DataSink}}} handle obtained for each data feed is the //actual access point// for clients to deliver data for output; in this case it seems adequate to rely on a ref-count, in order to ensure that the output configuration is available as long as any client processing is underway
|
||||
|
||||
-----
|
||||
!Implementation / design problems
|
||||
|
|
@ -5317,18 +5318,16 @@ So the problem is that BufferProvider is conceived as a central service facility
|
|||
!!! Establish a proper Lifecycle
|
||||
While the original design for OutputSlot seems //elegant, on a theoretical level,// the implementation was constantly plagued with complexities related to the lifecycle. Some relevant aspects could not be settled at all -- which was not noticed until the recent attempts to integrate the OutputSlot into the (likewise reworked) Render Engine implementation. A turning point in all this evolution of design is to remove the //state abstraction// -- in favour of representing the progression of state directly as a sequence of context data frames. The deeper change, which motivated this shift, was to abandon the idea of a single, global, overarching and final scheme of »Rendering«. It is acknowledged rather that the actual rendering for some unspecified »rendering« will be done by an external library -- and each such library establishes its own terminology, processing scheme and conventions, which finally are rooted in some specific ''Domain Ontology''. The Lumiera Render Engine is now understood as a connected graph of //adaptors//, organised around some minimal common denominator, like the fact that data frames are calculated, and that each such frame can be addressed by a media type and a point in time.
|
||||
|
||||
In line with this shift in focus, the OutputSlot is seen as yet another element of representational state: it is a token to embody an allocated output connection. This implies to unify the notion of the {{{OutputSlot}}} with the {{{Allocation}}}, and turn both into a handle passed to the client process. All aspects of lifecycle can be managed automatically with such ref-counting handles. This has some cost in terms of performance (thread contention when accessing the atomic ref count), but that is deemed acceptable, since output is handled on a rather slow time scale, like producing a data frame every 10 milliseconds. The specific output mechanism remains »allocated« thus as long as some element of the client processing still holds onto any such handle, either the {{{OutputSlot}}} or some {{{DataSink}}}. The latter is reduced into a //named function//, which however also embeds a shared handle to the common allocation state. With this rearrangement in the design, there is now a cohesive domain of connection management, while the actual, technical connection is separated out and packaged behind the {{{Connection}}} interface.
|
||||
In line with this shift in focus, the OutputSlot is seen as yet another element of representational state: it is a token to embody an allocated output connection. This implies to unify the notion of the {{{OutputSlot}}} with the {{{Allocation}}}, and turn both into a handle passed to the client process. All aspects of lifecycle can be managed automatically with such ref-counting handles. This has some cost in terms of performance (thread contention when accessing the atomic ref count), but that is deemed acceptable, since output is handled on a rather slow time scale, like producing a data frame every 10 milliseconds. The specific output mechanism remains »allocated« thus as long as some element of the client processing still holds onto any such handle, either the {{{OutputSlot}}} or some {{{DataSink}}}. The latter is a copyable value, which however also embeds a shared handle to the common allocation state. From a given {{{DataSink}}}, it is thus possible to start the computation cycle for each frame within the individual data feed of the corresponding {{{Connection}}}. With this rearrangement in the design, there is now a cohesive domain of connection management, while the actual, technical connection is separated out and packaged behind the {{{Connection}}} interface.
|
||||
|
||||
!!! How actually to arrange the delegation
|
||||
Taken together, this implies that the {{{OutputSlot::AllocState<CON>}}} turns into a delegation hub. It has to maintain the active connections and can thus be used to back a custom made {{{BufferStore}}} implementation, using the information embedded into the {{{LocalTag}}} to select the proper {{{Connection}}} for delegation -- which provides the actual implementation of all buffer management and state handling for the specific output mechanism. Yet this does not imply to route every actual call through the {{{AllocState}}} -- while this is possible, and could be characterised as the naive solution, it would lead to several delegation chains, with a set of delegation methods at each intermediary step. Furthermore, it would require to wire a back-reference to this {{{AllocState}}} through the proxy-{{{BufferProvider}}}.
|
||||
|
||||
Since the entrance point to each buffer usage cycle is the functor in the {{{DataSink}}}, a direct call-path can be established. For one, the C++ standard library offers an [[aliasing-constructor|https://cppreference.com/w/cpp/memory/shared_ptr/shared_ptr.html]]; this is a rather obscure feature, yet allows to setup a subordinate shared-ptr, that participates in the lifecycle management (ref-count) of the main pointer, while pointing at some //alias object//, which typically is a subclass, sub-object or otherwise a part of the automatically managed compound. In the case at hand here, a shared-pointer //to the individual connection// can be established within the λ-capture. Furthermore, the {{{OutputBufferProxy}}} (the proxy-{{{BufferProvider}}}) can be constructed such as to delegate //directly to that {{{Connection}}}.//
|
||||
Taken together, this implies that the {{{OutputSlot::AllocState<CON>}}} turns into a delegation hub. It has to maintain the active connections and can thus be used to back a custom made {{{BufferStore}}} implementation, using the information embedded into the {{{LocalTag}}} to select the proper {{{Connection}}} for delegation -- which provides the actual implementation of all buffer management and state handling for the specific output mechanism. Yet this does not imply to route every actual call through the {{{AllocState}}} -- in fact it is sufficient to forward the buffer lifecycle calls directly to the //corresponding connection//, which is implicitly available through the {{{LocalTag}}} in the buffer type. Besides that, this buffer type (which is a hash), together with the identity of the reference counting handle, constitutes an //identity of the {{{DataSink}}}, so that equality comparisons can be supported.
|
||||
|
||||
And this raises another intricate question of design: //shall {{{Connection}}} be a ''concept''?//
|
||||
This would be a very elegant, and modern solution, offering much flexibility for the Implementer of said {{{Connection}}}. The {{{OutputBufferProxy}}} could be templated to that {{{Connection}}} type and thus allow the optimiser to inline and reduce the callback functionality. However -- C++ concepts are a rather new and arcane feature, as they rely on very special syntax and semantics that still requires some time in practical use to catch on and prove its viability. A further objection that can be raised is the fact that compliance to a concept leaves no traces within the implementation -- quite contrary to a classical ~OO-style interface, where the implementation is very obviously marked as such. These considerations bring me to the decision to //reject the use of a concept// here, and to rely on a conventional interface for {{{Connection}}}. Notably it can be expected that new kinds of connection will be added frequently, and so the task for a developer not familiar with all fine points of this design should be reasonably straight forward: „You have to implement the {{{Connection}}} interface“
|
||||
</pre>
|
||||
</div>
|
||||
<div title="OutputSlotImpl" modifier="Ichthyostega" created="201107102343" modified="202605312031" tags="spec operational" changecount="10">
|
||||
<div title="OutputSlotImpl" modifier="Ichthyostega" created="201107102343" modified="202606011929" tags="spec operational" changecount="11">
|
||||
<pre>OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs.
|
||||
|
||||
Each distinct output capability must be registered through an OutputManager. When a client wants to output data, it obtains a suitable OutputSlot through the OutputDirector. This OutputSlot is created and initialised properly for this single use by the {{{OutputManager}}} in charge for that kind of output capability; notably it must be outfitted with an actual implementation of the {{{OutputSlot::Allocation}}} interface, and typically {{{OutputSlot::AllocState<CON>}}} will be used for that purpose.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue