diff --git a/src/lib/time/time.cpp b/src/lib/time/time.cpp index 96f3c5c3c..1369160af 100644 --- a/src/lib/time/time.cpp +++ b/src/lib/time/time.cpp @@ -49,6 +49,7 @@ #include "lib/rational.hpp" #include "lib/util-quant.hpp" #include "lib/format-string.hpp" +#include "lib/util.hpp" extern "C" { #include "lib/tmpbuf.h" @@ -62,12 +63,15 @@ extern "C" { #include using std::string; +using util::limited; using util::floordiv; using lib::time::FSecs; using lib::time::FrameRate; using boost::rational_cast; using boost::lexical_cast; +#undef MAX +#undef MIN namespace error = lumiera::error; @@ -263,6 +267,47 @@ namespace time { return Duration (1, *this); } + + + /** a rather arbitrary safety limit imposed on internal numbers used to represent a frame rate. + * @remark rational numbers bear the danger to overflow for quite ordinary computations; + * we stay away from the absolute maximum by an additional safety margin of 1/1000. + */ + const uint RATE_LIMIT{std::numeric_limits::max() / 1000}; + + /** + * @internal helper to work around the limitations of `uint`. + * @return a fractional number approximating the floating-point spec. + * @todo imposing a quite coarse limitation. If this turns out + * to be a problem: we can do better, use lib::reQuant (rational.hpp) + */ + boost::rational + __framerate_approximation (double fps) + { + double quantised{fabs(fps) * RATE_LIMIT}; + + uint num = uint(limited (1u, quantised + 0.5, 1000u*RATE_LIMIT)); + uint den = RATE_LIMIT; + return {num, den}; + } + + /** + * @internal helper calculate the _count per time span_ approximately, + * to the precision possible to represent as fractional `uint`. + */ + boost::rational + __framerate_approximation (size_t cnt, Duration timeReference) + { + boost::rational quot{cnt, _raw(timeReference)}; + if (quot.denominator() < RATE_LIMIT + and quot.numerator() < RATE_LIMIT/1000) + return {uint(quot.numerator()) * uint(Time::SCALE) + ,uint(quot.denominator()) + }; + // precise computation can not be handled numerically... + return __framerate_approximation (rational_cast(quot) * Time::SCALE); + } + diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index b036c9d18..acbb17e39 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -667,7 +667,11 @@ namespace time { public: FrameRate (uint fps) ; FrameRate (uint num, uint denom); - FrameRate (boost::rational const& fractionalRate); + FrameRate (size_t count, Duration timeReference); + explicit + FrameRate (boost::rational fractionalRate); + + static FrameRate approx(double fps); // standard copy acceptable; @@ -806,10 +810,25 @@ namespace time { { } inline - FrameRate::FrameRate (boost::rational const& fractionalRate) + FrameRate::FrameRate (boost::rational fractionalRate) : boost::rational (__ensure_nonzero(fractionalRate)) { } + boost::rational __framerate_approximation (size_t, Duration); + boost::rational __framerate_approximation (double); + + inline + FrameRate::FrameRate (size_t count, Duration timeReference) + : FrameRate{__framerate_approximation (count, timeReference)} + { } + + inline FrameRate + FrameRate::approx (double fps) + { + return FrameRate{__framerate_approximation (fps)}; + } + + inline double FrameRate::asDouble() const { diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp index 1fc327daa..5b9091716 100644 --- a/src/vault/gear/block-flow.hpp +++ b/src/vault/gear/block-flow.hpp @@ -104,6 +104,7 @@ namespace gear { using lib::time::FSecs; using lib::time::TimeVar; using lib::time::Duration; + using lib::time::FrameRate; namespace blockFlow {///< Parametrisation of Scheduler memory management scheme @@ -327,6 +328,7 @@ namespace gear { * Scheduling entails to provide a chain of Activity definitions, * which will then »flow« through the priority queue until invocation. * + * @see ActivityLang * @see SchedulerCommutator * @see BlockFlow_test */ @@ -385,7 +387,16 @@ namespace gear { double stretched = _raw(epochStep_) * factor; gavl_time_t microTicks(floor (stretched)); epochStep_ = TimeValue{microTicks}; - + } + + /** provide a hint to the self-regulating allocation scheme */ + void + announceAdditionalFlow (FrameRate additionalFps) + { + FrameRate currFps{config().framesPerEpoch(), epochStep_}; + /////////////////////////////////////////////////////////////////////TODO need arithmetics on FrameRate + /////////////////////////////////////////////////////////////////////TODO -> then just add the new and calculate new stepping + /////////////////////////////////////////////////////////////////////TODO !! watch out for the minimum limit !! } diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index fda2dcdc0..1c27434d7 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -229,6 +229,20 @@ namespace test{ CHECK (isnil (Duration (0, FrameRate::PAL))); CHECK (isnil (Duration (0, FrameRate(123)))); + + CHECK (FrameRate::approx(2000) == "1000FPS"_expect); // limited + CHECK (FrameRate::approx(1e-5) == "43/4294967FPS"_expect); + CHECK (FrameRate::approx(1e-6) == "4/4294967FPS"_expect); + CHECK (FrameRate::approx(1e-7) == "1/4294967FPS"_expect); // limited + CHECK (FrameRate::approx(1e-8) == "1/4294967FPS"_expect); // limited + + CHECK (FrameRate( 20'000, Duration{Time{0,10}}) == "2000FPS"_expect); + CHECK (FrameRate( 20'000, Duration{Time::MAX }) == "1/4294967FPS"_expect);// limited + + CHECK (FrameRate(size_t(2e10), Duration{Time::MAX }) == "279397/4294967FPS"_expect); + CHECK (FrameRate(size_t(2e14), Duration{Time::MAX }) == "2793967531/4294967FPS"_expect); + CHECK (FrameRate(size_t(2e15), Duration{Time::MAX }) == "1000FPS"_expect); // limited + CHECK (FrameRate(size_t(2e16), Duration{Time::MAX }) == "1000FPS"_expect); // limited } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 98cd4b60d..83be0077a 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -78951,9 +78951,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...damit sie das Grooming-Token hat und damit Zugriff auf die Scheduler-Ressourcen. Außerdem ist damit ein konsitenter Rahmen sichergestellt: geplante Startzeiten sollten frühestend 20ms danach beginnen @@ -80253,9 +80251,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

beide Probleme lassen sich nicht lösen — @@ -80441,9 +80437,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...da nun wirklich jeder Dispatch am Grooming-Token „vorbei muß“  — theoretisch wäre es sogar darstellbar, die eigentliche Job-Planung außerhalb und ohne Grooming-Token zu machen; die neuen Jobs gehen dann eben in die Instruction-Queue. Auch eine Notification (welche das Gate dekrementieren könnte) geht durch das λ-post, welches direkt durch Layer2.postDispatch geht, und damit entweder versucht, das Grooming-Token zu erlangen, oder eben in die Instruct-Queue einstellt @@ -80483,9 +80477,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

und hat damit garantiert das Grooming-Token @@ -82110,9 +82102,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

das ist das ausschlaggebende Argument: Jemand, der erst mal nur den Scheduler-Header liest, wäre sonst sofort erschlagen von technischen Details, und würde den Wald vor lauter Bäumen nicht mehr sehen. Zumal Scheduler selber nochmal zerlegt wird in einen Header-Anteil und eine eigene translation-unit (scheduler.cpp) @@ -82151,29 +82141,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...und zwar wegen der Builder-Notation; wegen der Möglichkeit, später noch Dependencies aufzuschalten, muß der activity::Term im Builder eingebettet sein, also bei der Definition der Builder-Klasse bekannt. Einziger Ausweg wäre, in die äußere Builder-Klasse einen opaque buffer zu legen, und die Implementierung in scheduler.cpp nachzutragen. Diese Lösung halte ich für unnötig komplex (wiewohl sie keinen relevanten Performance-overhead erzeugt)

- -
+
- - - +

...denn die betreffenden Header sind nicht wirklich schwergewichtig  — im Besonderen da die typischen Clients des SchedulerService selber Implementierungs-Code sind

- -
+
@@ -82191,29 +82175,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

denn dabei handelt es sich nun wirklich um eine zentrale Implementierungs-Funktion, die auch aus der Implementierung heraus aufgerufen wird. Selbst wenn der Optimizer wahrscheinlich die monomorphic optimization beherrscht, macht man sowas einfach nicht.

- -
+
- - - +

...so ziemlich jede von denen greift in der Implementierung auf die Komponenten im Scheduler zu.

- -
+
@@ -82429,37 +82407,35 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + - - - +

also sehr großzügig

- -
+
- - - +

er ist stets compulsory

- -
+
- + + @@ -82467,6 +82443,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +
@@ -94226,6 +94209,30 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + +

+ Idee: Angabe in »zusätzlichen Frames-per-second« +

+ +
+ +
+ + + + + + + + + +
@@ -95627,16 +95634,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

allerdings haben wir jetzt praktisch immer einen direkten Eingang

- -
+