Commit graph

1158 commits

Author SHA1 Message Date
180c6b8d84 Block-Flow: define next steps to construct
...continue to proceed test-driven
...scheduler internals turn out to be intricate and cohesive,
   and thus the only hope is to adhere to strict testing discipline
2023-07-13 01:51:21 +02:00
18904e5b58 Block-Flow: completed implementation of low-level cyclic extent storage
..verified boundary cases for expansion while retaining addresses
of currently active extents...
2023-07-12 21:55:50 +02:00
824a626c2e Block-Flow: investigate proper working of on-demand allocation
Library: add "obvious" utility to the IterExplorer, allowing to
         materialise all contents of the Pipeline into a container

...use this to take a snapshot of all currently active Extent addresses
2023-07-12 19:19:41 +02:00
f5813a1f29 Block-Flow: veryfy proper handling of extent reuse
- use a checksum to prove that ctor / dtor of "content" is not invoked
- let the usage of active extents "wrap around" so that the mem block is re-used
- verify that the same data is still there
2023-07-12 04:53:30 +02:00
6409e0eb36 Block-Flow: implement iteration and expansion of ExtentFamily
The low-level allocator is basically implemented now,
but we still need to check thoroughly that the tricky
wrap-around and expansion logic behaves sane...
(see #1311)
2023-07-11 03:52:24 +02:00
3b929cf014 Block-Flow: better setup for iterator implementation
Using a Storage* within a wrapper as "pos" will work,
but is borderline trickery, since it amounts to subverting
the idea behind IterAdapter (which is to encapsulate a target
pointer with some control-logic in the managing container).

Using the same storage size and implementation overhead,
it is much more straight-forward to package the complete
iteration logic into a »State Core«, which in this case
however maintains a back-link to the ExtentFamily.
2023-07-11 02:03:50 +02:00
3401f18c2c Block-Flow: consider usage in ActivityTerm and rectify iteration
Iteration should just yield an Reference to an Extent,
thereby hiding all details of the actual raw storage (char[]).
This can be achieved by usind a wrapper type around a pointer
into the managing vector; from this pointer we may convert
into a vector::iterator with the trick described here

https://stackoverflow.com/a/37101607/444796


Furthermore, continued planning of the Activity-Language,
basically clarified the complete usage scenario for now;
seems all implementable right away without further difficulties
2023-07-11 01:08:26 +02:00
e86cb017a5 Block-Flow: implement cyclic usage of an extent pool
..with the ability to grow on demand..
..possibly add the new extents in the middle, by first allocating at the end
  and then using the std::rotate() algo to bring them to the point
  in the middle where new extents are required
2023-07-10 05:40:50 +02:00
c1b16349f2 Block-Flow: define next steps for implementation of low-level allocator 2023-07-09 04:03:02 +02:00
ccf0710903 Block-Flow: maintain an »Epoch« within the raw allocation Extent
- the idea is to use slot-0 in each extent for administrative metadata
- to that end, a specialised GATE-Activity is placed into slot-0
- decision to use the next-pointer for managing the next free slot
- thus we need the help of the underlying ExtentFamily for navigating Extents

Decision to refrain from any attempt to "fix" excessive memory usage,
caused by Epochs still blocked by pending IO operations. Rather, we
assume the engine uses sane parametrisation (possibly with dynamic adjustment)
Yet still there will be some safety limit, but when exceeding this limit,
the allocator will just throw, thereby killing the playback/render process
2023-07-09 01:32:27 +02:00
533112a4b0 Block-Flow: provide specialised ctor notation
...now able to create instances for all the relevant Activity verbs
2023-07-07 03:41:30 +02:00
f34ecafa1a Block-Flow: consider data storage for render activities
- decision to favour small memory footprint
- rather use several Activity records to express invocation
- design Activity record as »POD with constructor«
- conceptually, Activity is polymorphic, but on implementation
  level, this is "folded down" into union-based data storage,
  layering accessor functions on top
2023-07-06 16:35:42 +02:00
4ac995548a Block-Flow: identify required API operations
- decision how to handle the Extent storage (by forced-cast)
- decision to place the administrative record directly into the Extent

TODO not clear yet how to handle the implicit limitation for future deadlines
2023-07-05 15:12:20 +02:00
022d40a8cf Block-Flow: initial draft of ExtentFamily storage
using a simple yet performant data structure.
Not clear yet if this approach is sustainable

- assuming that no value initialisation happens for POD payload
- performance trade-off growth when in wrapped-state vs using a list
2023-07-04 04:42:53 +02:00
23a6fbdf4f Scheduler: investigate modes of operation
- analysis of Activity usage
- derive possible memory management schemes
- research regarding asynchronous IO
- decision regarding the memory management scheme
2023-07-03 18:40:37 +02:00
4176576db0 Scheduler: consider what operations are necessary for layer-1
....still about to find out what kinds of Activities there are,
and what reasonably to implement on layer-2 vs. layer-1

It is clear that the worker will typically invoke a doWork()
operation on layer-2, which in turn will iterate layer-1.

Each worker pulls and performs internal managmenet tasks exclusively
until encountering the next real render task, at which point it will
drop an exclusion flag and then engage into performing the actual
extended work for rendering...
2023-06-27 03:21:10 +02:00
3b6519a7c0 Scheduler: pass activity marker (low-level)
- define a simple record to represent the Activity
- define a handle with an ordering function
- low-level functions to...
  + accept such a handle
  + pick it from the entrace queue
  + pass it for priorisation into the PriQueue
  + dequeue the top priority element
2023-06-26 02:16:50 +02:00
bdcfc94b57 Scheduler: implementation technology
- use Boost-Lockfree as entrance queue for instructions
- use the STL Heap-Algo and Priority-Queue adaptor for time order
2023-06-25 01:02:12 +02:00
3169ba88ad Scheduler: devise the arrangement of basic components
- define organisation of vault-layer namespaces
- define the ground plan of the scheduler implementation
2023-06-24 03:14:17 +02:00
d109f5e1fb bye bye Monad (closes #1276)
after completing the recent clean-up and refactoring work,
the monad based framework for recursive tree expansion
can be abandoned and retracted.

This approach from functional programming leads to code,
which is ''cool to write'' yet ''hard to understand.''

A second design attempt was based on the pipeline and decorator pattern
and integrates the monadic expansion as a special case, used here to
discover the prerequisites for a render job. This turned out to be
more effective and prolific and became standard for several exploring
and backtracking algorithms in Lumiera.
2023-06-22 20:23:55 +02:00
42f4e403ac Job-Planning: rework of dispatcher and pipeline builder complete (see #1301)
An extended series of refactoring and partial rewrites resulted
in a new definition of the `Dispatcher` interface and completes
the buildup of a Job-Planning pipeline, including the ability
to discover prerequisites and compute scheduling deadlines.

At this point, I am about to ''switch to the topic'' of the `Scheduler`,
''postponing'' the completion of the `RenderDrive` until the related
questions regarding memory management and Scheduler interface are settled.
2023-06-22 03:55:09 +02:00
8c78e50730 Job-Planning: extended deadline integration test
- allow to configure the expected job runtime in the test spec
- remove link to EngineConfig and hard-wire the engine latency for now

... extended integration testing reveals two further bugs ;-)
... document deadline calculation
2023-06-21 04:04:11 +02:00
1f840730a0 Job-Planning: build and verify complete pipeline
- strip the builder
- add a terminal / front-end with convenience functions
- verify integration, incl multi-step prerequisites and deadlines
2023-06-20 01:46:44 +02:00
848bb6fb86 Job-Planning: implement handling of deadlines for prerequisites
...simple implementation
...decide *not* to cache the deadlines for now (possibly quadratic!)
...Test GREEN
2023-06-19 18:28:01 +02:00
b8309e5565 Job-Planning: define expectation for prerequisites 2023-06-19 16:58:32 +02:00
dc1bbfc918 Job-Planning: rework pipeline to enable dependency planning
This finishes the last series of refactorings; the basic concept
remains the same, but in the initial version we arranged the expander
function in the pipeline to maintain a Tuple (parent, child) for the
JobTickets. Unfortunately this turned out to be insufficient, since
JobTicket is effectively const and responsible for a complete Sement,
so there is no room to memorise a Deadline for the parent dependency.

This leads to the better idea to link the JobPlanning aggregators
themselves by parent-child references, which is possible since the
whole dependency chain actually sits in the stack embedded into the
Expander (in the pipeline)
2023-06-19 03:56:11 +02:00
2b92dab377 Library: change »assignment« of ItemWrapper to destroy-create
This very deep change (which requires almost complete rebuild)
was prompted by the need to process an object (JobPlanning),
which holds several references and is thus move-only, in the
middle of a complex processing pipeline with child expansion.

If this works out well, a long-standing and obnoxious problem
with transforming iterators would be solved, albeit by incurring
a (presumably small) performance overhead, since now the new
value is no longer *assigned*, but rather the existing payload
is destroyed and a new instance is copy/move constructed into
the inline buffer.

The primary purpose (and widely used in Lumieara) is to have a
Lambda create a new Object, which is then returned by value
and thus immediately moved into this inline buffer, where it
resides for further use (as long as the enclosing pipeline
stays alive). Unless such an object does very elaborate
allocations and registrations behind the scene, the
expense of assigning vs creating should be the same.
2023-06-19 02:33:50 +02:00
9ef3d98de7 Job-Planning: replace FrameCoord by direct references
...in the hope that the Optimiser is able to elide those references entirely,
when (as is here the case) they point into another field of a larger object compound
2023-06-19 01:51:48 +02:00
a1c1456849 Job-Planning: dispose of FrameCoord in pipeline and Dispatcher interface
...as a preparation for solving a logical problem with the Planning-Pipeline;
it can not quite work as intended just by passing down the pair of
current ticket and dependent ticket, since we have to calculate a chained
calculation of job deadlines, leading up to the root ticket for a frame.

My solution idea is to create the JobPlanning earlier in the pipeline,
already *before* the expansion of prerequisites, and rather to integrate
the representation of the dependency relation direcly into JobPlanning
2023-06-18 03:50:48 +02:00
661d768fad Job-Planning: frame number now additionally required in FrameCoord
...which was the reason why the test failed;
the calculation works as expected


PS: rename JobPlanningSetup_test to JobPlanningPipeline_test
2023-06-17 03:10:57 +02:00
6228c623b4 Job-Planning: implement braindead deadline calculation
...using hard coded values instead of observation of actual runtimes,
but at least the calculation scheme (now relocated from TimeAnchor to JobPlanning)
should be a reasonable starting point.

TODO: test fails...
2023-06-16 04:09:38 +02:00
73a9e4495a Job-Planning: code up simplest use case 2023-06-16 01:50:11 +02:00
b9f8356afc Job-Planning: investigate input data
- collect list of entities to be picked up from the dispatcher-pipeline
- as it turns out: there is no sensible use for the realTimeDeadline in
  in the FrameCoord record ==> remove it
2023-06-15 18:24:25 +02:00
a551314e80 Job-Planning: start rework of the planning data aggregation
The initial implementation effort for Player and Job-Planning
has been reviewed and largely reworked, and some parts are now
obsoleted by the reworked alternative and can be disabled.

The basic idea will be retained though: JobPlanning is a
data aggregator and performs the final step of creating a Job
2023-06-15 03:51:07 +02:00
f84517547b Dispatcher-Pipeline: coordination of base tick and prerequisite expansion
- had to fix a logical inconsistency in the underlying Expander implementation
  in TreeExplorer: the source-pipeline was pulled in advance on expansion,
  in order to "consume" the expanded element immediately; now we retain
  this element (actually inaccessible) until all of the immediate
  children are consumed; thus the (visible) state of the PipeFrameTick
  stays at the frame number corresponding to the top-level frame Job,
  while possibly expanding a complete tree of flexible prerequisites

This test now gives a nice visualisation of the interconnected states
in the Job-Planning pipeline. This can be quite complex, yet I still think
that this semi-functional approach with a stateful pipeline and expand functors
is the cleanest way to handle this while encapsulating all details
2023-06-14 18:12:41 +02:00
08dfe1007c Dispatcher-Pipeline: verify the expansion of prerequisites
- fix a bug in the MockDispatcher, when duplicating the ExitNodes.
  A vector-ctor with curly braces will be interpreted as std::initializer_list

- add visualisation of the contents appearing at the end of the pipeline

*** something still broken here, increments don't happen as expected
2023-06-14 04:20:50 +02:00
542017aa65 Dispatcher-Pipeline: mocked Dispatcher implementation complete (closes: #1294)
`steam/engine/mock-dispatcher.hpp |cpp` now integrates this
''complete mock setup for render jobs and frame dispatching.''
The exising `DummyJob` has been slightly adapted and renamed
to `MockJob` and is tightly integrated with the other mocks.

The implementation of a `MockDispatcher` necessitated to change
the use of `MockJobTicket`. The initial attempts used a complete
mock implementation, but this approach turned out not to be viable.
Instead — based on the ideas developed for the mock setup —
now the prospective real implementation of `JobTicket` is available
and will be used by the mock setup too. Instead of a synthetic spec,
now a setup of recursively connected `ExitNode`(s) is used; the latter
seems to develop into some kind of Facade for the render node network.

Based on this mock setup, we can now demonstrate the (mostly) complete
Job-Planning pipeline, starting from a segmentation up to render jobs,
and verify proper connectivity and job invocation.
✔
2023-06-13 20:23:33 +02:00
0b9705692b Dispatcher-Pipeline: now (finally) able to implement MockDispatcher
MockSupport_test      : PASS
JobPlanningSetup_test : PASS(as far as defined)
2023-06-13 03:47:42 +02:00
122addbff5 Dispatcher-Pipeline: expected behaviour of (mock)Dispatcher 2023-06-13 00:15:16 +02:00
2031a58775 Dispatcher-Pipeline: decide upon the translation into portIDX
- has to be prepared / supported by the RenderEnvironmentClosure
- actual translation happens when building the Dispatcher-Pipeline
- implementation delegate through
    virtual size_t Dispatcher::resolveModelPort (ModelPort)
2023-06-12 19:21:14 +02:00
e6dcb6253c Dispatcher-Pipeline: resolve further problems with re-entrant allocation
...ouch this was insidious: the STL implementation for list does not
return a pointer to the element just allocated, but rather retrieves
and dereferences the back() / front() iterator after returning from emplace_back|front()

...which in case of re-entrant allocations is something wildly different
than the initial allocation. Thus a *cheap* and dirty placeholder implementation
just using a STL container is not possible, and we need at least
to code up likewise cheesy placeholder implementation by hand.
- separate allocation and ctor all
- use an inline buffer in the STL container
- explicitly handle ctor failures to discard allocation
- NOT THREADSAFE and likely WASTFUL in terms of performance


==> MockSupport_test now back to GREEN after complete refactoring
2023-06-12 17:21:41 +02:00
bf3e612c55 Dispatcher-Pipeline: create hook for self-validation
...later to be extended into the render nodes network
2023-06-12 01:18:59 +02:00
0933d2bba8 Dispatcher-Pipeline: simplify JobTicket and remove channel differentiation
The existing implementation of the Player from 2012~2015 inclduded
an additional differentiation by media channel (for multichannel media)
and would build a separate CalcStream for each channel.

The in-depth analysis conducted for the ongoing »Vertical Slice« effort
revealed that this differentiation is besides the point and would never
be materialised: Since -- by definition -- all media processing has
to be done by the engine, also the generation of the final output format
including any channel multiplexing will happen in render nodes.
The only exception would be when only a single channel of multichannel
media is extracted -- yet this case would then translate into a
dedicated ModelPort.

Based on this reasoning, a lot of complexity (and some contradictions)
within the JobTicket implementation can be removed -- together with
some further leftovers of the fist attempt to build JobTickets always
from a Mock specification (we now use construction by the Segment,
based on an ExitNode, which is the expected actual implementation
for production setup)
2023-06-12 00:04:45 +02:00
b18e79d077 Dispatcher-Pipeline: solve allocation of JobTicket instances
...by defining a new scheme for access to custom allocators
...and then passing a reference to such an accessor into the
   JobTicket ctor, thereby allowing the ticket istelf recursively
   to place further JobTicket instances into the allocation space

--> success, test passes (finally)
2023-06-11 04:37:38 +02:00
f25ec2f5ef Dispatcher-Pipeline: switch JobTicket creation to use ExitNode directly
Up to now, a draft/mock implementation was used, relying on a »spec tuple«,
which was fabricated by MockJobTicket. But with the introduction of
NodeGraphAttachment, the MockSequence now generates a nested ExitNode structure,
and thus the JobTicket will be created through the "real" ctor, and
no longer via MockJobTicket.

Thus it is possible to skip this whole interspersed »spec tuple«,
since ExitNode *is* already this aggregated / abstracted Spec
2023-06-10 04:52:40 +02:00
2c3b85a122 Dispatcher-Pipeline: allocate JobTicket in Segment
PROBLEM: can not implement Spec-generation, since
 - we must use a λ for internal allocation of JobTickets
 - but recursive type inference is not possible

Will thus need to abandon the Spec-Tuple and relocate this
traversal-and-generation code into JobTicket itself
2023-06-09 02:48:38 +02:00
c246c21e41 Dispatcher-Pipeline: remould Segment for on-demand JobTicket generation
Use another unit-test (FixtureSegment_test) to guide and cover
the transition from the existing fake-implementation to the
actual implementation, where the JobTicket will be generated
on-demand, from a NodeGraphAttachment
2023-06-08 03:21:43 +02:00
0c57a61545 Dispatcher-Pipeline: implement Generation of fake-ExitNodes
...similar to the Generation of complete JobTickets in the initial implementation
2023-06-08 00:28:44 +02:00
f6af4c6a16 Dispatcher-Pipeline: prepare test for the new NodeGraphAttachment
It turns out that the real (not mocked) implementation of JobTicket creation
is already required now for this planned (mock)Dispatcher setup;
moreover, this real implementation turns out to be almost identical
to the mock implementation written recently -- just nested structure
of prerequiste JobTickets need to be changed into a similar structur
of ExitNodes

-- as an aside: rearrange various tests to be more in-line
   with the envisioned architecture of playback and engine
2023-06-07 04:03:00 +02:00
3b2e5db7b4 Dispatcher-Pipeline: consider how to access render nodes from job
...this opens up yet another difficult question and a host of new problems
- how are prerequisites detected or arranged by the Builder
- how are prerequisites represented?
- what is an ExitNode in terms of implementation? A subclass of ProcNode?
- how will the actual implementation of JobTicket creation (on-demand) work?
- how to adapt the Mock implementation, while retaining the Specification
  for Segments and prerequisites?
2023-06-06 04:25:12 +02:00
7d5c32e6b6 Dispatcher-Pipeline: draft test for JobTicket access 2023-06-05 18:09:42 +02:00
a5b3a16f3e Dispatcher-Pipeline: clean setup for builder and pipeline types
..this is now the third attempt, and it seems this one leads to a
clean solution for the type rebinding problem, while also allowing
to unit-test each step in isolation.

The idea is to layer a *templated* builder class on top,
but to slice it away in each step, re-assemble the pipeline
and decorate a new builder instance on top. The net result
is a tightly interconnected processing pipeline without
any spurious interspersed leftovers from the builder,
while all intermediate steps are fully operational
and can thus be unit-tested...
2023-06-05 02:52:10 +02:00
41a8c7ec1a Dispatcher-Pipeline: draft Step-2 and Step-3
...still very rough edged...
...based on the idea to have a pair(Dependent,Dependency) and to shift these on each level of expansion

PROBLEMS:
 - what to use as root level?
 - can not handle JobTicket const& in transform-iterator (assignement operator)
2023-06-04 03:58:15 +02:00
4601c6350e Dispatcher-Pipeline: arrangement of builder types
...it turns out that we actually do not need to wrap TreeExplorer
on the builder types, because basically there is only a single active
builder type, and the complete processing pipeline can be assembled
in a single terminal function.

The type rebinding problem can thus be solved just by a simple
marker struct, which inherits from a template parameter
2023-06-04 02:01:07 +02:00
71ea10bf21 Dispatcher-Pipeline: implement the frame-tick core
splitting into a sequence of builder types seems to have done the trick
2023-06-03 18:38:37 +02:00
81ee9a2e67 Dispatcher-Pipeline: builder type rebinding problems
...hard to tackle...
The idea is to wrap the TreeExplorer builder, so that our specific
builder functions can delegated to the (inherited) generic builder functions
and would just need to supply some cleverly bound lambdas. However,
resulting types are recursive, which does not play nice with type inference,
and working around that problem leads to capturing a self reference,
which at time of invocation is already invalidated (due to moving the
whole pipeline into the final storage)
2023-06-03 03:44:22 +02:00
94fe4a4bec Dispatcher-Pipeline: draft builder-API
...which leads to the next daunting problems:
- we need some mocked ModelPort and DataSink placeholders
- we need a way how to inherit from a partial TreeExplorer pipeline
2023-06-02 05:32:15 +02:00
dda37365cc Dispatcher-Pipeline: direct implementation of frame-tick
start with demonstration of base technique in test setup
2023-06-01 18:09:26 +02:00
ad173540d9 Library: allow for a stop condition in iterator pipeline
...introduced in preparation for building the Dispatcher pipeline,
which at its core means to iterate over a sequence of frame positions;
thus we need a way to stop rendering at a predetermined point...
2023-06-01 16:48:27 +02:00
fbfbd2a078 Dispatcher+Scheduler: decision to dispose of the TimeAnchor
several years ago, it seemed like a good idea to incorporate
the link between nominal time and wall-clock time into a dedicated
anchor point, which also regulates the continued frame planning.

But it turned out that such a design mixes up several concepts
and introduces confusion regarding the meaning of "real time"
- latency can not be reasonably defined for a whole planning chunk
- skipping or sliding due to missed deadlines can not reasonably handled
  within such an abstract entity; it must be handled rather at the
  level of a playback process
- linking the frame grid generation directly to a planning chunk
  undercuts the possible abstraction of a planning pipeline
2023-05-31 03:27:13 +02:00
87f40c8169 Dispatcher+Scheduler: Requirement analysis and planning work 2023-05-29 04:43:10 +02:00
b4c0ffab25 Job-Planning: Analysis for the next step
...which is build a »Job planning pipeline« step by step
in a test setup, and then factor that out as RenderDrive,
to supersede the existing CalcPlanContinuation and get
rid of the Monads this way...

Challenges
- there is a inconsistency with channel usage
- need to establish a way how to transport the output-Sink into the JobFunctor
- need a way to propagate the current frame number to the next planning chunk
2023-05-26 04:20:12 +02:00
e33689e5d6 Job-Planning: verify and complete the build-up of mock structures (see #1294)
The prototypical setup of data structures and test support components
is largely complete by now — with the exception of the `MockDispatcher`,
which will be completed while moving to the next steps pertaining the
setup of a frame dispatch pipeline.

 * the existing `DummyJob` was augmented to allow verification of
   association between Job and `JobTicket`
 * the existing implementation of `JobTicket` was verified and augmented
   to allow coverage of the whole usage cycle
 * a `MockJobTicket` was implemented on top, which can be generated
   from a symbolical test specification (rather than from the real
   Fixture data structure)
 * a complete `MockSegmentation` was developed, allowing to establish
   all the aforementioned data structures without an actual backing
   Render Engine. Moreover, `MockSegmentation` can be generated
   from the aforementioned symbolic test specification.
 * as part of this work, an algorithm to split an existing Segmentation
   and to splice in new segments was developed and verified
2023-05-24 03:38:12 +02:00
4f37b0412c Job-Planning: finally complete the MockSegmentation tests
Last testcase: add deeply nested Prerequisites.
Turns out that the allocator must be able to handle
re-entrant allocations, which std::deque can not fulfil.
Thus using std::list here for the Mock implementation.

In the end, the real allocations will be done by our custom
allocator (AllocationCluster), which can be arranged easily
to support re-entrant allocation calls (since the whole point
is to just place those objects into a pre-allocated large block
and only de-allocate them later in one sway. Thus the allocator
does not need to wait for the object constructor to finish, which
trivially allows for re-entrant calls)
2023-05-23 06:40:18 +02:00
94cec423d0 Job-Planning: switch to processing references
...which uncovers further deeply nested problems,
especially when referring to non-copyable types.

Thus need to construct a common type that can be used
both to refer to the source elements and the expanded elements,
and use this common type as result type and also attempt to
produce better diagnostic messages on type mismatch....
2023-05-23 01:08:05 +02:00
bf6951afcf Job-Planning: verify pipeline can now be constructed (after bugfix)
verify by in-depth investigation that all nested typedefs are now properly constructed
2023-05-23 01:07:53 +02:00
0df0fd001e Library: fix follow-up problems with const correctness
...the improved const correctness on STL iterators uncovered another
latent problem with out diagnositc format helper, which provide
consistently rounded float and double output, but failed to take
CV-qualifiaction into account
2023-05-23 01:07:53 +02:00
e176e54004 Library: adjust and fix semantics of nested 'value_type' binding
This is a subtle and far reaching fix, which hopefully removes
a roadblock regarding a Dispatcher pipeline: Our type rebinding
template used to pick up nested type definitions, especially
'value_type' and 'reference' from iterators and containers,
took an overly simplistic approach, which was then fixed
at various places driven by individual problems.

Now:
 - value_type is conceptually the "thing" exposed by the iterator
 - and pointers are treated as simple values, and no longer linked
   to their pointee type; rather we handle the twist regarding
   STL const_iterator direcly (it defines a non const value_type,
   which is sensible from the STL point of view, but breaks our
   generic iterator wrapping mechanism)
2023-05-23 01:07:53 +02:00
46ab053b8a Library: investigate / consolidate usages of type rebinding from iterators
...in an attempt to resolve the deeply nested problems encountered
while building an iterator pipeline for the Dispatcher. It seems
that I was sloppy some years ago and just "bashed them into submission",
thereby mixing up two different meanings of "value_type"

Moreover I seemingly implemented the same helper trait template twice,
so the first step is to switch all usages to meta::TypeBinding
2023-05-23 01:07:53 +02:00
67468f15d5 Job-Planning: Attempt to build a prerequisite-Pipeline failed -- investigate why
To complete the mock setup, the next step would be to extend the GenNode-based spec langage
to allow defining prerequisite Mock-JobTickets. Setting this up seems rather straight forward --

however, defining a simple testcase to cover this extension runs into surprisingly tricky problems..
- for one, the singleValIterator from Itertools has serious difficulties handling references
- but even more surprising, it seems impossible to make the "prerequisites iterator"
  fit into the Tree-Explorer framework (which I intend to use as replacement
  for the monadic approach)

after some extended analysis of generic types and template instances,
it seems that not TreeExplorer as such is the primary problem, but rather
there is a conceptual mismatch somewhere deep down in Itertools or Iter-Adapter
2023-05-23 01:07:07 +02:00
27a8e91fa2 Job-Ticket: consider how to deal with channels and prerequisites
By reasoning and analysis I conclude that the differentiation into
multiple channels is likely misplaced in JobTicket; it belongs ratther
into the Segment and should provide a suitable JobTicket for each ModelPort

Handling of prerequisites also needs to be reshaped entirely after
switching to a pipeline builder for the Job-planning pipeline; as
preliminary access point, just add an iterator over the immediate
prerequisites, thereby shifting the exploration mechanism entirely
out of the JobTicket implementation
2023-05-11 22:47:56 +02:00
db87de3c92 Segmentation: complete implementation of mock / test stub setup
We are now able to build and refine a segmentation
and to get distinct MockJobs from these segments
and to verify invocation of a specific job
2023-05-10 15:00:10 +02:00
566f73de2a Segmentation: verify standard cases..
Testcase: A simple Sementation with a single and bounded Segment


As aside, figured out how to unpack an iterator such as to
tie a fixed number of references through a structural binding:

auto const& [s1,s2,s3] = seqTuple<3> (mockSegs.eachSeg());
2023-05-10 03:59:46 +02:00
e5cdb86ac3 Segmentation: integrate SplitSplice and build mock-segmentation
...now able to build a mock segmentation which issues dummy jobs,
and is wired such as to verify the right job is invoked for each segment.

And this allows to build and verify the Dispatcher,
without being able to invoke actual render jobs yet.
2023-05-05 03:46:42 +02:00
52dad70753 Segmentation: Split-Splice algorithm implementation complete
...and verified including corner cases...
2023-05-04 15:59:17 +02:00
3f2f3db568 Segmentation: verify Split-Splice standard cases
Algorithm seems to work basically...
There was a copy-n-paste error in the treatment of the Successor,
leading to spurious duplication in some cases
2023-05-04 14:43:40 +02:00
f9a4d6134c Segmentation: setup for simple demo test case works
due to having extracted the Algo implementation as template,
we can now instantiate it for isolated tests with simple integer intervals
2023-05-04 02:07:38 +02:00
00ca84a2aa test-helper for comparison with expected (string) result
...this is something I should have done since YEARS, really...

Whenever working with symbolically represented data, tests
typically involve checking *hundreds* of expected results,
and thus it can be really hard to find out where the
failure actually happens; it is better for readability
to have the expected result string immediately in the
test code; now this expected result can be marked
with a user-defined literal, and then on mismatch
the expected and the real value will be printed.
2023-05-04 00:48:29 +02:00
476c0f6493 Segmentation: build test-setup for Split-Splice-Algo
use simple intervals over integer numbers as test setup
- detect possible memory leaks
- detect corrupted Segmentation sequences
2023-05-03 15:27:46 +02:00
640f029496 Segmentation: extract split-splice algorithm into library header 2023-05-03 05:01:45 +02:00
139b0d4c7b Segmentation: make implementation generic
The algorithm coded thus far turns out to be rather generic,
and thus it can be rewritten into a template, while all specific parts
are supplied as λ-Functors.

- instead of Time we use a generic ordering type
- the Iterator is likewise turned into a template parameter
- all the operations are directly supplied as functor types
- C++17 is able to pick up all those λ-Types from the ctor call

This change looks like "low hanging fruit"; the legibility of the code
is not seriously hampered, yet we get the benefit to test this rather
technical piece of logic by an isolated test (which for now is the
primary motivation), and we can hope to re-use it for similar tasks.
2023-05-03 04:43:10 +02:00
2378be5d86 Segmentation: code up the algorithm according to spec
...derive the implementation mostly by logical reasoning
...indicate positions by Iterators of a random-access list
2023-05-03 03:32:49 +02:00
bf978fcda1 Segmentation: devise algorithm for splitSplice operation 2023-05-02 21:37:15 +02:00
b582c35c9f Segmentation: structure analysis for splitSplice operation
There are 12 distinct cases regarding the orientation of two intervals;
The Segmentation::splitSplice() operation shall insert a new Segment
and adjust / truncate / expand / split / delete existing segments
such as to retain the *Invariant* (seamless segmentation covering
the complete time axis)
2023-05-02 04:29:34 +02:00
a807705185 Segmentation: draft simple mock-based setup for tests
- how to pass-in a specification given as GenNode
- now this might be translated into a MockJobTicket allocated in the MockSegmentation

Unimplemented: actually build the Segment with suitable start/end time
2023-05-01 17:02:11 +02:00
56405b2e2d Job-Planning: simulate backing by specific JobTicket
right now we're lacking a complete working implementation of render node invocation,
and thus the Dispatcher implementation can only be verified with the help
of mocked jobs. However, at least a preliminary implementation of tagging the
invocation instance is available, and thus we're able to verify that
a given job instance indeed belongs to and is "backed" by a specific JobTicket.

This is prerequisite for building up a (likewise mocked) Fixture datastructure,
and this in turn was meant to form the basis for attacking an actual Scheduler
implementation, followed by a real render node invocation.
2023-05-01 14:07:21 +02:00
f6fbc15e5f Job-Planning: provide stub implementation for NOP job (see #1296)
- can now create a Job from JobTicket::NIL
- on invocation this Job will to nothing

Only when the first real output backend is implemented,
we can decide if this simplistic implementation is enough,
or if an empty output must be explicitly generated...
2023-05-01 01:48:36 +02:00
fef0c05b64 Job-Planning: base implementation of job instance creation
* using a simplified preliminary implementation of hash chaining (see #1293)
 * simplistic implementation of hashing for time values (half-rotation)
 * for now just hashing the time into the upper part of the LUID

Maybe we can even live with that implementation for some time,
depending on how important uniform distribution of hash values is
for proper usage of the frame cache.

Needless to say, various further fine points need more consideration,
especially questions of portability (32bit anyone?). Moreover, since
frame times are typically quantised, the search space for the hashed
time values is drastically reduced; conceivably we should rather
research and implement a good hash function for 128bit and then combine
all information into a single hash key....
2023-04-30 22:33:42 +02:00
8aa0c258ba Job-Planning: investigate invocation of jobs
...using the MockJobTicket setup as point of reference,
since the actual invocation of render nodes will only be drafted
later in this "Vertical Slice" integration effort...
2023-04-30 02:18:56 +02:00
685b5beba6 Segmentation: simple implementation of time-based access
- introduce a JobTicket::NOP (null-object pattern)
- assuming that the function splitSplice() will retain complete coverage allways

Remark:
`Fixture::getPlaylistForRender()` is a leftover from the very early implementation drafts.
This function was more or less based on the way Cinelerra works; it is clear by now
that Lumiera can not possibly work this way, given that we'll build a low-level model
and dispatch precompiled render jobs....
2023-04-27 22:30:49 +02:00
d73b316ead Segmentation: consider preliminary data structure
...and consider how that can be extended later into the full
structure, which has to support a transactional switch
2023-04-27 19:38:37 +02:00
90593776f6 Segmentation: a plan to bootstrap into the required structure
The idea is to escape a "design deadlock" by using a test-driven prototype
implementation of the data structure to back a further development
of the Dispatcher and Scheduler implementation, which then can be used
to gradually elaborate and switch over to an actual implementation
data structure
2023-04-25 13:40:20 +02:00
b93a9a7985 Job-Planning: elaborate mock setup for render job 2023-04-21 05:29:10 +02:00
305eb825af Job-Planning: first testcase - empty JobTicket
...requires a first attempt towards defining a `JobTiket`.
This turns out quite tricky, due to using those `LinkedElements`
(intrusive single linked list), which requires all added records
actually to live elsewhere. Since we want to use a custom allocator
later (the `AllocationCluster`), this boils down to allocating those
records only when about to construct the `JobTicket` itself.

What makes matters even worse: at the moment we use a separate spec
per Media channel (maybe these specs can be collapsed later non).
And thus we need to pass a collection -- or better an iterator
with raw specs, which in turn must reveal yet another nested
sequence for the prerequisite `JobTickets`.

Anyhow, now we're able at least to create an empty `JobTicket`,
backed by a dummy `JobFunctor`....
2023-04-20 23:55:02 +02:00
d341f003ca Job-Planning: attempt to stake claims
desperately trying to move forward and define a minimal first test case...
2023-04-18 20:02:36 +02:00
1dd1ec0e79 Job-Planning: decision how to rework bottom-up and test driven
- build the reworked Job-planning pipeline more or less from scratch
- back that with mocked `Dispatcher` and `JobTicket`
- then transfer this into a `RenderDrive`, which can be tested as well
- could continue then to a `CalcStream` integration test....
2023-04-17 17:10:53 +02:00
25c8579695 Job-Planning: new draft - organise the overall planning process
- introduce a new entity: RenderDrive
- it supersedes the CalcPlanCalculation, but is managed by CalcStream
- moreover, the RenderDrive will house a IterTreeExplorer-Pipeline
- define the concerns and relationships more clearly (see Drawing)
- prerequisite to disentangle the Job-planning "mechanics"
2023-04-17 04:51:38 +02:00
bcd2b3d632 PlaybackVerticalSlice: design analysis for Frame Dispatcher and Scheduler
- decision: the Monad-style iteration framework will be abandoned
- the job-planning will be recast in terms of the iter-tree-explorer
- job-planning and frame dispatch will be disentangled
- the Scheduler will deliberately offer a high-level interface
- on this high-level, Scheduler will support dependency management
- the low-level implementation of the Scheduler will be based on Activity verbs
2023-04-14 04:43:39 +02:00
197a840ffa PlaybackVerticalSlice: plan for the next integration effort 2023-04-04 06:11:36 +02:00
dfcb17b890 GUI: close out rework of top-level and timeline
This finishes a long lasting effort to rework the top-level of the Lumiera GTK UI,
to adapt to GTK-3 and the new asynchronous message based architecture.

Special credits and thanks to
 * Joel Holdsworth
 * Stefan Kangas

Without their relentless foundational work, the Lumiera UI could
never be where it is now. Even if some code was rewritten and several
parts of the old GTK-2 implementation are now obsolete, numerous ideas
solutions and inspirations were drawn from those early contributions
and live on as part of the reworked GUI.
2023-03-22 02:58:04 +01:00
5b64507560 Timeline: remove visual debugging and add timeline style
for sake of developement of the timeline body drawing code,
several tweaks were added to make the impact of the styling stand
out clearly. This changeset removes all those tweaks and restors
the code to intended neutral behaviour

Moreover, the cursom drawing of the timeline now requires some
basic aids to be present in the stylesheet, otherwise the track structure
will not be visible. Thus add some minimalistic styling to the
"light-theme-complement"-stylesheet, mostly based on the usual
predefined theme colours and some box-shadow settings.

This is by no means an adequate graphical solution,
yet it should be enough to get on with coding....
2023-03-19 14:32:11 +01:00
28331f9f8a Timeline: complete the rework of UI backbone and Timeline (closes: #1014)
check private notes and mindmap and fix some remaining minor inconsistencies,
notably the calculations in the overlay renderer in the `BodyCanvasWidget`.
2023-03-18 00:58:55 +01:00
8d9846837d Timeline: document use of custom CSS rules
The drawing code extracts style information from some "virtual"
widgets, which serve as logical placeholder for the actual nested
structure of tracks.

For sake of demonstration, I used rather obvious colours and
also all kinds of margin and padding; a screenshot was added
with annotations to indicate where some specific style settings
are utilised from the drawing code
2023-03-17 01:10:33 +01:00
8fec337f60 Timeline: investigate setup of initial canvas widthin pixels
Not sure if the initial window width is used properly for calibration
of ZoomWindow "pixel width" base setting.


Follow-up to the layout logic established with this commit:
09714cfe28
2023-03-15 02:05:34 +01:00
d564ce429a Timeline: remove diagnostic functions (closes #1201)
An extended round of rebuilding and reworking the global UI structures
can be concluded now. A flexible recursive structure of Tracks has
been implemented for the new Timeline-UI, allowing to contro all
relevant aspects of structure composition by **Diff Messages** sent
up from the Steam-Layer into the UI.

Moreover, the ability to control the custom drawing code through regular
**CSS style rules** has been demonstrated, allowing for seamless integration
of Lumiera UI elements with the existing desktop theme.
2023-03-10 17:23:17 +01:00
fc0fcad4d6 Timeline: implement structure connections for sub-Tracks (closes: #1018)
This completes the initial implementation round for the TrackHead.
- arrangement and layout for nested sub-Tracks is now settled
- a graphical representation of scope nesting was implemented

Postponed for later...
- still some minor discrepancies on synchronisation of vertical space
  between TrackHead and custom drawing in the body (off-by-one?)
- Expanding / Collapsing of Tracks
- Implement actual Controls to influence the Scope, e.g. Volume, Mix-Mode
- Dynamically indicate selection and Muting on the structure display
2023-03-06 01:51:35 +01:00
caffcef559 Timeline: Bugfix - can not assume scale factor one
While by default the Cairo Context is scaled to device units,
we must not assume that the given Context is unscaled; rather
it might have been deliberately scaled...

A notable example is the Gtk Inspector, which offers a "Magnifier" feature
2023-03-05 17:20:24 +01:00
fc74fbcd4f Timeline: complete integration of bracket drawing into the Layout
- pick up all relevant values from CSS
- also control the width of the StaveBracket
- observe the given overall height

Moreover, complete documentation drawing in Inkscape
and add a page to the TiddlyWiki, describing the principles
underlying this design and construction.
2023-03-05 04:00:49 +01:00
47108fa2d8 Timeline: expand and document system of marker style classes
.timeline__head : The complete header container on the left side
.timeline__navi : navigation control at top
.timeline__pbay : container holding the patchbay on the left side
.fork__head     : each individual TrackHeadWidget (possibly nested)
.fork__control  : container for the control components for each track scope
.fork__bracket  : the StaveBracket drawing to indicate the nesting structure
2023-03-03 18:36:33 +01:00
c234ab733b Timeline: Bugfix in drawing code
...the gap at the lower end of the line was caused
by an unintended conversion from double to int, prior to scaling
2023-03-02 17:59:54 +01:00
6108fdb738 Timeline: now able to implement placement and complete drawing
The tricky part is to derive the anchor point for the upper
and the lower cap of the bracket, taking into account possible padding.
There seems to be a bug hidden somewhere in this logic, since the
line turns out too short at the lower end....?
2023-03-02 02:06:56 +01:00
2565856f2e Timeline: investigation how to retrieve font size
According to the documentation, we should be able to get a Pango
font description from the CSS style context, and from there we should
be able to retrieve a font-size specification (and a DPI for the display)

Running this experimental code yields a font-size value of 9pt,
leading to a scale factor of about ~6, which seems plausible.
2023-03-01 01:43:57 +01:00
d66949a70a Timeline: setup for layout control of scope brackets
after weighting in the pro, and cons,
I decided to follow the standard path and pick up values
for each StaveBracket instance individually from Gtk::StyleContext,
assuming that the GTK framework will care for caching and performance
2023-02-27 02:04:56 +01:00
b7ea68e285 Timeline: draft Cairo drawing code to implement this shape
...this settles the most tricky part: how to get the curved end caps
defined and placed precisely; the solution approach is thereby confirmed.
2023-02-26 16:58:07 +01:00
c2bb45f2dd Timeline: manually extract construction coordinates from FreeCAD
Identify the elements of the construction geometry in the "Sketch"
object in the FreeCAD document and paste the corresponding coordinate
values into the SVG drawing prepared for documentation.

The arc segment parameters seemingly are given in radians;
and while FreeCAD uses the common mathematical right-handed orientation,
the orientation in SVG is applied clockwise rather.
2023-02-26 01:29:13 +01:00
ef4401a185 Timeline: rearrange drawing in accordance to construction
...since the construction is determined now (and was worked out in FreeCAD),
the SVG will serve to document the construction; thus the drawing
primitives are rearranged to use the unscaled reference coordinates
to be extracted from the FreeCAD document; all scaling and placement
in the SVG document will be applied through common groups.
2023-02-25 15:32:35 +01:00
3ad5eaa9ba Timeline: construct a design for track scope brackets
My idea was to use the brackets from musical notation as inspiration;
if you know some principles of typography, it is rather straight-forward
to come up with a pleasing design of such a bracket, using a
cascade of golden ratio relationships.

BUT ... all of this is geometry, and translating that into a symbolic
or numerical calculation is excessively complicated. Thus I looked
for ways to use some geometry or CAD software to build such a construction.

The geometry software I tired was woefully inadequate for this task.
Using the Constraint system in FreeCAD, building the construction went
smooth and straight forward, but then I was unable to export that drawing
in a way indicating the construction clearly.

So in the end, I'll have to hand-pick the resulting numerical coordinates
from the FreeCAD XML document and integrate them directly into Cairo
drawing code...
2023-02-25 01:49:58 +01:00
171c3e30d7 Timeline: fix horizontal allocation on leaf-tracks
...as it turned out, it suffices just to state desired behaviour explicitly
- make the StaveBracket expand=false
- declare the HeadControl expand=true

The reason lies in the fact that on leaf-tracks there are just two
adjacent cells in a single row, lacking any exterior source of layout guidance.
2023-02-11 04:30:26 +01:00
4444e2b0ec Timeline: setup framework for custom drawing for the track structure
...just drawing a marker cross for now to indicate allocated size;
speaking of size -- GTK sometimes expands allocation horizontally,
while we'd prefer an absolutely fixed size for the purpose at hand.
2023-02-10 17:51:02 +01:00
70b8b86182 Timeline: determine remaining work to wrap-up
Intention is to shift focus of development work down towards Player and Scheduler soon.
However, since the timeline display saw substantial improvement it seems prudent
to finish work on some open ends, notably

- the track head structure
- the drawing and styling code

While content rendering for Clip widgets can safely be postponed, regarding the TrackHeadWidget,
where custom drawing is planned to display a structural outline of the nested scopes, some
ground work should be completed to make those plannings explicit.
2023-02-09 01:30:32 +01:00
37543024fd Timeline: use ElementBoxWidget to display the track header
This both demonstrates the layout of the `TrackHeadWidget`
and puts `ElementBoxWidget` into intended use to indicate
the scope of a track and to provide a placement icon and
an expander/collapser button.

see #1018
see #1219
2023-02-03 04:38:56 +01:00
25be54ecbe Timeline: remove diagnostic code (closes: #1211)
Layout calculation and balancing between body and track header
now works reasonably well, labels are placed properly and the
calculated layout remains stable when changing window size,
connected panes and scrollbars behave as expected now.

Quite a feat!
2023-01-25 01:42:40 +01:00
e0e0192938 Timeline: fix height calculation
...to properly account for
- the "prelude" padding above the root track
- overview rulers located into a fixed separate canvas at top
- slope-down and slope-up borders around nested tracks.

After testing, results seem now to be accurate up to ± 1px

Finally (sigh)
2023-01-23 23:44:55 +01:00
9bfae3cee1 Timeline: control alignment to sub-tracks through additional padding
...a huge improvement;
however, it becomes obiouvs now that we'll have to deal specifically
with the root track ruler canvas and "prelude" padding...
2023-01-22 00:50:34 +01:00
b38481fa89 Timeline: solve basic layout issue by properly calculating the height
As it turns out, we need to take the actual allocation of the cells
within the grid explicitly into account: combined cells will report
their extension for each of the underlying cells, thus leading to
excessively overestimated measurements.

So we now calculate the overall height based on the actual structure
- first row holds the label
- left column below used as expander
- right column holds individual content

Remaining problems:
- height of ruler canvas at top not taken into account (11px in this example)
- start of sub-track headers not synchronised with start of sub-tracks
  in the body area
2023-01-20 23:57:21 +01:00
4887194589 Timeline: investigate vertical space allocation on nested tracks
...seemingly the allocation of grid cells in the `TrackHeadWidget`
is not quite correct yet: even when there are nested sub-tracks,
we always need another row to hold the controls corresponding to
the track itself and the whole scope. And this row is also what
should be adjusted to match the vertical extension of the content
area.

As it turns out, the whole topic how to handle collapsed tracks
was not even considered yet; the calculation of the "track profile"
would need to be reworked to accommodate collapsed tracks, see: #1265
2023-01-19 23:58:42 +01:00
a04d2b01c6 Timeline: hunt down and fix a numeric-wrap problem
Sometimes, fractional seconds in the ZoomWindow metric can build up
to numerator and denominator values in the 64-bit integer domain.
Thus the division and truncation of the Window pixel with value
must be done in int64_t, while the result value is then
guaranteed to be a small integer < 100000

This caused the canvas to flicker and jump in size and the
resulting scrollbar change caused various cascading effects
on the further layout calculations...
2023-01-17 02:16:30 +01:00
2fb182e7ff Timeline: analyse and fix re-entrance problem in DisplayEvaluation
Note: the actual root cause, why this re-entrance happens,
is due to another obvious numerics bug not yet solved.
Here, the canvas width was suddenly set to zero, causing
the scrollbar position to change and thus the ZoomWindow
to re-fire the structure change signal.

However, such invalidation of previously established baseline
values can never be totally excluded in advanced layout calculations,
and thus the evaluation mechanism must be prepared and re-triggered
to start over, until a stable layout is achieved.
2023-01-16 00:07:05 +01:00
0a99e5dfc8 Timeline: reorganise additional padding in TrackHeadWidget
- rearrange cell content and disable auto-expand to prevent
  the content area from becoming oversized initially

- fix autocompletion error in signal binding,
  causing segfault when moving the scrollbar
2023-01-15 02:51:00 +01:00
09714cfe28 Timeline: draft solution to interconnect ZoomWindow and scrollbar
After quite some tinkering, instead of extending the DisplayManager interface,
I now prefer to treat this connection rather as an intricate implementation detail:

The TimelineLayout implementatino now provides two translation functions,
which are directly wired as slots from the Signals emitted by moving the
hand of the scrollbar; the idea is that these functions mutate the ZoomWindow,
which then triggers a DisplayEvaltuation, which in turn causes the
drawing code to pick up and translate back the new metric and position.

Results look promising, insofar the DisplayEvaluation is now triggered
repeatedly, and the actual window width in pixel is propagated;
however, the response of the layout code is seemingly random at times,
the allocated height grows monontonously and the code Segfaults when
moving the scrollbar...
2023-01-06 03:09:28 +01:00
941d3088d5 Timeline: rearrange ruler canvas into a slave scrolled pane
this makes the arrangement more symmetric and natural
and also makes the overview ruler scroll alongside the content pane,
thereby creating the (intended) impression of one uniform layout space
2023-01-05 02:45:11 +01:00
f3098daeb4 Timeline: investigate problem with horizontal scrollbar
As it turns out, this happens as side-effect from the workaround 2019-08-22

fc5eaf857c

Obviously, just set_size() on the canvas is not sufficient for
GTK actually to establish a size-request (seemingly the canvas counts
as /empty/ and only real widgets would make a difference).
However, since the ruler canvas is directly placed into the box,
and not adapted by a ScrolledWindow, explicitly set_size_request()
also causes the enclosing Box to "inherit" this minimal required size,
thereby also spreading out the BodyCanvasWidget beyond the size
actually available. GTK handles this situation by hard-clipping
on the right side, which causes the vertical scrollbar to disappear
and keeps the horizontal scrollbar disabled (since nominally it still
spans the whole size available, even while this size is then clipped
subsequently).

This changeset adds a lot of debug printing and demonstrates this
behaviour by setting only a minimal horizontal size_request, so that
the window is no longer expanded and clipped.
2023-01-04 23:55:21 +01:00
c22bbe5f93 Timeline: Refactor DisplayEvaluation to integrate zoom calibration
This does not really change the logic of the DisplayEvaluation mechanism,
but makes it much more flexible: instead of having two hard wired components,
the DisplayEvaluation visitor now holds a collection of LayoutElement pointers.
This way, the Layout manager itself (on behalf of the ZoomWindow) can
participate in the process, and on activation will now establish the
window width in pixel.

This works now insofar the drawing on the canvas is adapted coherently;
however something with the setup of the Scrollbar is still not quite right;
some time ago I recall the scrollbar worked, but now it is blocked
and the canvas just clips to the right side.
2023-01-04 03:36:12 +01:00
061b70c96f Timeline: integrate ZoomWindow change listener
- connect to TimelineLayout::signalStructureChange
 - this causes the trackProfile to be reset
 - next draw() call will then triger display-evaluation
2023-01-03 02:23:22 +01:00
62bad8720a Timeline: decide upon handling of the canvas origin
It is now tied to the start of ZoomWindow::overallSpan(),
thereby defining the (technical) pixel coordinates within the window
and for drawing on the canvas to be always positive. Whenever ZoomWindow
re-calibrates, it's change signal will trigger, causing the
TimelineLayout to perform a new DisplayEvaluationPass,
which in turn prompts all embedded widgets to readjust
their positions accordingly.
2023-01-03 00:20:09 +01:00
52d3231226 Timeline: finish ZoomWindow implementation and boundrary tests 2022-12-18 03:47:40 +01:00
e436023ef9 Timeline: properly handling maximal zoom-in and alignment to µ-ticks 2022-12-17 17:47:10 +01:00
b1514f6632 Timeline: properly handling extreme scroll-steps 2022-12-17 01:15:34 +01:00
3893968502 Timeline: improve handling of window size changes at extreme positions
...again and again surprising how much inconsitencies can hide in just some lines of code...
2022-12-16 19:59:47 +01:00
77bb156615 Timeline: verify handling of extreme time offsets 2022-12-16 02:23:20 +01:00
5e595c57ca Timeline: automatically orient and shift into allowed time domain
Note: changing behaviour of TimeSpan to possibly flip start and end,
and also to use Offset as Offset and then re-orient,
since this seems the least surprising behaviour.

These changes carry over into changed default and limiting
on ZoomWindow constructor and various mutators, and most
notably shifting the time span always into allowed domain.
2022-12-14 03:00:07 +01:00
777024ee40 Timeline: resolve yet another insidious corner case at maximum zoom
...the implementation was way too naive; in some cases we could go
into an infinite loop. In the end, using Newton approximation was not
necessary (and thus there is no loop anymore), but it helped me get
at a much better solution with very small error margin on average case.

All these corner cases are obviously "academic" to some degree,
but it turns out there is no clear-cut point where you'd be able
just so set a limit and be sure that fractional integer arithmetic
works flawless in all cases.

Thus the choice is
 - give up (fractional) integers and work with floats and have to
   deal with error accumulation
 - or do something as chosen here, namely add a boundary zone, where
   fractional integer arithmetic can be kept under control, while admitting
   small errors, and in turn get the absolutely precise integers in all
   everyday standard cases
2022-12-13 01:21:18 +01:00
c31522c236 Timeline: define better internal zoom-out limit
The value used previously was too conservative, and prevented ZommWindow
from zooming out to the complete Time domain. This was due to missing the
Time::SCALE denominator, which increaded the limit by factor 1e6

In fact the code is able to handle even this extremely reduced limit,
but doing so seems over the top, since now detox() kicks in on several
calculations, leading to rather coarse grained errors.

Thus I decided to use a compromise: lower the limit only by factor 1000;
with typical screen pixel widths, we can reach the full time domain,
while most scaling and zoom calculations can be performed precisely,
without detox() kicking in. Obviously this change requires adjusting
a lot of the test case expectations, since we can now zoom out maximally.
2022-12-10 04:26:22 +01:00
40f003a962 Timeline: stress-test with excessive zoom-out reveals further weakness
As it turns out, the calculation path initially choosen for the mutateScale(Rat)
was needlessly indirect, and also duplicated several of the safeguards,
meanwhile implemented way better in conformWindowToMetric(Rat)

Thus, instead of relatively re-scaling the window, now we just
limit the given zoomFactor and pass it to conformWindowToMetric()
2022-12-09 23:13:27 +01:00
ce3713d872 Timeline: now able to increase to maximum pixel count
There is a built-in limitation, which now is even
lowered to 100000 pixels horizontally.

With the techniques introduced in this changeset, it seems possible
to support more -- yet this would be a case of unnecessary genricity;
handling such large numbers will drive more computations into the
danger zone, and doing so incurs cost in terms of testing and debugging.

Placing that into context, contemporary displays are not even 4K on
average, and it does not look likely even for cinema display to go
way beyond 8k -- so yes, I want display hardware with 100000 pixels!!


The key takeaway of this changeset:
 - can calculate px = trunc(zoomFactor * duration) step wise,
   even when the direct calculation would lead to wrap-around
 - can safely adjust and fix the zoomFactor using Newton approximation
2022-12-09 19:37:35 +01:00
068549dace Timeline: now able to handle maximal zoom-out
...even zooming out to span the complete time domain (~19000 years).
But only under the condition that the display window is sufficiently
large in terms of pixels, so we can handle the computation without
glitches.

This should not be a relevant limitation in practice, since a window
size of some 100 pixels is enough to handle Duration::MAX. Needless to add
that it's hard to imagine a media timeline of such tremendous size...
building on these Library changes, plus the safe-add function
developed some days ago, it is now possible to mark a large displacement
as `time::Offset`, and apply this to yield any valid time position,
even extreme negative values
2022-12-08 18:06:37 +01:00
006758f349 Library/Timeline: now able to scroll to extreme positions (closes #1263)
...building on these Library changes, plus the safe-add function
developed some days ago, it is now possible to mark a large displacement
as `time::Offset`, and apply this to yield any valid time position,
even extreme negative values
2022-12-05 03:34:04 +01:00
13adc56f34 Library: rectify confusingly named function on the Grid API
The APIs for time quantisation were drafted in an early stage of the project
and then never followed-up. Especially Grid::gridAlign has no
real-world usage yet, and is only massaged in some tests.

When looking at QuantiserBasics_test, I was puzzled and led astray,
since this function suggests to materialise a continuous time into
a quantised time -- which it doesn't (there is another dedicated
function Quantiser::materialise() to that end); so, without engaging
into the discussion if this function is of any use, I'll hereby
choose a name better reflecting what it does.
2022-12-05 01:05:23 +01:00
50c602ec3f Library: rectify clipping of time::Duration (see #1263)
This is a deep refactoring to allow to represent the distance
between all valid time points as a time::Offset or time::Duration.

By design this is possible, since Time::MAX was defined as 1/30 of
the maximum value technically representable as int64_t. However,
introducing a different limiter for offsets and durations turns
out difficult, due to the inconsistencies in the exiting hierarchy
of temporal entities. Which in turn seems to stem from the unfortunate
decision to make time entities immutable, see #1261

Since the limiter is hard wired into the `time::TimeValue` constructor,
we are forced to create a "backdoor" of sorts, to pass up values
with different limiting from child classes. This would not be so
much of a problem if calculations weren't forced to go through `TimeVar`,
which does not distinguish between time points and time durations.

This solution rearranges all checks to be performed now by time::Offset,
while time::Duration will only take the absolute value at construction,
based on the fact that there is no valid construction path to yield
a duration which does not go through an offset first.

Later, when we're ready to sort out the implementation base of time values
(see #1258), this design issue should be revisited
- either we'll allow derived classes explicitly to invoke the limiter functions
- or we may be able to have an automatic conversion path from clearly
  marked base implementation types, in which case we wouldn't use the
  buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
4d79bdce5f Timeline: sophisticated helper function to resolve problems with numeric precision
While the calculation was already basically under control, I just was not content
with the achieved numeric precision -- and the fact that the test case in fact
misses the bar, making it difficult do demonstrate that the calculation
is not derailed. I just had the gut feeling that it must be somehow possible
to achieve an absolute error level, not just a relative error level of 1e-6

Thus I reworked this part into a generic helper function, see #1262

The end result is:
 * partial failure. we can only ''guarantee'' the relative error margin of 1e-6
 * but in most cases not out to the most extreme numbers, the sophisticated
   solution achieves much better results way below the absolute error level of 1µ-Tick

Thus with using rational numbers, we have now a solution that is absolutely precise
in the regular case, and gradually introduces errors at the domain bound
but with an guaranteed relative error margin of 1e-6 (== Time::SCALE)
2022-12-02 22:23:14 +01:00