diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 0e9548eae..08d3d96a6 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -188,6 +188,7 @@ namespace time { /** @internal to pass Time values to C functions */ friend gavl_time_t _raw (TimeValue const& time) { return time.t_; } + friend HashVal hash_value (TimeValue const&); static TimeValue buildRaw_(gavl_time_t); /** @internal diagnostics */ @@ -708,6 +709,25 @@ namespace time { + + /** derive a hash from the ยต-tick value + * @return rotation of the raw value to produce a suitable spacing for consecutive time + * @remark picked up by Boost-hash, or std. hashtables with the help of `hash-standard.h` + * @see https://stackoverflow.com/a/31488147 + */ + inline HashVal + hash_value (TimeValue const& time) + { + HashVal x = _raw(time); // possibly cap to size of hash + const uint width = sizeof(HashVal) * CHAR_BIT; + const uint mask = width-1; + const uint n = width / 2; + + static_assert (0 < n and n <= mask); + return (x<>((-n)&mask )); + } + + /** @internal applies a limiter on the provided * raw time value to keep it within the arbitrary * boundaries defined by (Time::MAX, Time::MIN). diff --git a/src/steam/engine/job-ticket.cpp b/src/steam/engine/job-ticket.cpp index 48f838436..65783b028 100644 --- a/src/steam/engine/job-ticket.cpp +++ b/src/steam/engine/job-ticket.cpp @@ -108,8 +108,34 @@ using lib::HashVal; Job JobTicket::createJobFor (FrameCoord coordinates) { - REQUIRE (this->isValid(), "Attempt to generate render job for incomplete or unspecified render plan."); - UNIMPLEMENTED ("job planning and generation"); + if (isnil (provision_)) + { + + } + else + { + REQUIRE (this->isValid(), "Attempt to generate render job for incomplete or unspecified render plan."); + REQUIRE (coordinates.channelNr < provision_.size(), "Inconsistent Job planning; channel beyond provision"); + Provision& provision = provision_[coordinates.channelNr]; + JobClosure& functor = static_cast (provision.jobFunctor); /////////////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) + Time nominalTime = coordinates.absoluteNominalTime; + InvocationInstanceID invoKey{timeHash (nominalTime, provision.invocationSeed)}; + + return Job(functor, invoKey, nominalTime); + } + } + + /** + * Tag the precomputed invocation ID with the nominal frame time + */ + InvocationInstanceID + JobTicket::timeHash (Time nominalTime, InvocationInstanceID const& seed) + { + InvocationInstanceID res{seed}; + HashVal timeMark = res.part.t; + lib::hash::combine (timeMark, hash_value (nominalTime)); + res.part.t = timeMark; + return res; } diff --git a/src/steam/engine/job-ticket.hpp b/src/steam/engine/job-ticket.hpp index 193034bce..11057f149 100644 --- a/src/steam/engine/job-ticket.hpp +++ b/src/steam/engine/job-ticket.hpp @@ -61,10 +61,12 @@ namespace engine { //using lib::time::Time; using vault::engine::Job; using vault::engine::JobFunctor; +using vault::engine::JobClosure; /////////////////////////////////////////////////////////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) using lib::LinkedElements; using lib::OrientationIndicator; using util::isnil; using lib::HashVal; +using lib::LUID; // //class ExitNode; @@ -109,12 +111,12 @@ using lib::HashVal; { Provision* next{nullptr}; JobFunctor& jobFunctor; - HashVal invocationSeed; + InvocationInstanceID invocationSeed; Prerequisites requirements{}; ////////////////////TODO some channel or format descriptor here Provision (JobFunctor& func, HashVal seed =0) : jobFunctor{func} - , invocationSeed{seed} + , invocationSeed(static_cast(func).buildInstanceID(seed)) ////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) { } }; @@ -159,6 +161,7 @@ using lib::HashVal; } protected: + static InvocationInstanceID timeHash (Time, InvocationInstanceID const&); bool verifyInstance (JobFunctor&, Time) const; }; diff --git a/src/vault/engine/job.h b/src/vault/engine/job.h index 5fa2660f6..61137a989 100644 --- a/src/vault/engine/job.h +++ b/src/vault/engine/job.h @@ -122,8 +122,10 @@ union InvocationInstanceID lumiera_uid luid{0}; /* ----- alternative accessors for test code ---- */ - FrameCnt frameNumber; - struct {int a,b;} metaInfo; + FrameCnt frameNumber; + struct { int32_t a,b; + int64_t t; + } part; }; diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index db13725f6..fda2dcdc0 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -70,6 +70,7 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); + checkTimeHash (ref); checkTimeConvenience (ref); verify_invalidFramerateProtection(); createOffsets (ref); @@ -201,6 +202,25 @@ namespace test{ } + /** @test calculate a generic hash value from a time spec*/ + void + checkTimeHash (TimeValue org) + { + std::hash hashFunc; + CHECK (0 == hashFunc (Time::ZERO)); + size_t hh = sizeof(size_t)*CHAR_BIT/2; + CHECK (size_t(1)< 0 || org == Time::ZERO); + CHECK (h2 - h1 == size_t(1)<{} (invoKey.metaInfo.a); - } ////////////////////////////////////////////////////////////////////////TICKET #1293 : this is dangerous and could lead to clashes + std::hash hashr; + HashVal res = hashr (invoKey.frameNumber); + lib::hash::combine (res, hashr (invoKey.part.t)); + return res; + } /* === Logging/Reporting of job invocation === */ @@ -114,17 +129,17 @@ namespace engine { TimeVar real; int a,b; - Invocation(JobParameter param) - : nominal(TimeValue(param.nominalTime)) - , real(RealClock::now()) - , a(param.invoKey.metaInfo.a) - , b(param.invoKey.metaInfo.b) + Invocation (JobParameter param) + : nominal{TimeValue(param.nominalTime)} + , real{RealClock::now()} + , a{param.invoKey.part.a} + , b{param.invoKey.part.b} { } Invocation() - : nominal(Time::ANYTIME) - , real(Time::NEVER) - , a(MAX_PARAM_A), b(0) + : nominal{Time::ANYTIME} + , real{Time::NEVER} + , a{MAX_PARAM_A}, b{0} { } }; @@ -165,8 +180,8 @@ namespace engine { DummyJob::build() { InvocationInstanceID invoKey; - invoKey.metaInfo.a = rand() % MAX_PARAM_A; - invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + invoKey.part.a = rand() % MAX_PARAM_A; + invoKey.part.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; Time nominalTime = lib::test::randTime(); @@ -178,8 +193,8 @@ namespace engine { DummyJob::build (Time nominalTime, int additionalKey) { InvocationInstanceID invoKey; - invoKey.metaInfo.a = additionalKey; - invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + invoKey.part.a = additionalKey; + invoKey.part.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; return Job(dummyClosure, invoKey, nominalTime); } diff --git a/tests/core/vault/engine/job-hash-test.cpp b/tests/core/vault/engine/job-hash-test.cpp index 0a4a19860..26b75a637 100644 --- a/tests/core/vault/engine/job-hash-test.cpp +++ b/tests/core/vault/engine/job-hash-test.cpp @@ -99,7 +99,7 @@ namespace test { CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the concrete nominal job time copy = job1; - copy.parameter.invoKey.metaInfo.a++; + copy.parameter.invoKey.part.a++; CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the internal interpretation of the invocation key @@ -115,7 +115,7 @@ namespace test { size_t hashOfInstance(InvocationInstanceID invoKey) const { - return boost::hash_value (invoKey.metaInfo.a); + return boost::hash_value (invoKey.part.a); } }; OtherClosure someOtherClosure; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 9b72ee3ae..38291bfe4 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -69824,6 +69824,42 @@ + + + + + + + + +

+ Verwende eine halb-Rotation über size_t ⟹ +

+
    +
  • + die beiden Hälften des Bitstring der µ-ticks werden vertauscht +
  • +
  • + damit erheblicher Abstand zwischen konsekutiven Werten +
  • +
  • + hash² ≡ id +
  • +
  • + impl sollte i.d.R. nach inlining eine einzige Assembler-Instruktion sein ⟶ https://stackoverflow.com/a/31488147 +
  • +
+ +
+ +
+ + + + + + +
@@ -70696,6 +70732,11 @@ + + + + +