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...
This commit is contained in:
Fischlurch 2023-04-30 02:18:56 +02:00
parent 685b5beba6
commit 8aa0c258ba
13 changed files with 233 additions and 52 deletions

View file

@ -71,6 +71,8 @@
#endif
#include "lib/hash-value.h"
#include <cstddef>
#include <utility>

View file

@ -70,6 +70,12 @@ namespace engine {
}
InvocationInstanceID
CalcPlanContinuation::buildInstanceID (HashVal seed) const
{
UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time");
}
size_t
CalcPlanContinuation::hashOfInstance (InvocationInstanceID invoKey) const
{

View file

@ -55,6 +55,7 @@ namespace engine {
// using lib::time::FSecs;
// using lib::time::Time;
using lib::time::FrameCnt;
using lib::HashVal;
/**
@ -80,16 +81,16 @@ namespace engine {
/* === JobClosure Interface === */
JobKind
getJobKind() const
getJobKind() const override
{
return META_JOB;
}
bool verify (Time, InvocationInstanceID) const;
size_t hashOfInstance (InvocationInstanceID) const;
void invokeJobOperation (JobParameter);
void signalFailure (JobParameter, JobFailureReason);
bool verify (Time, InvocationInstanceID) const override;
size_t hashOfInstance (InvocationInstanceID) const override;
InvocationInstanceID buildInstanceID (HashVal) const override;
void invokeJobOperation (JobParameter) override;
void signalFailure (JobParameter, JobFailureReason) override;
@ -102,7 +103,7 @@ namespace engine {
,Dispatcher& dispatcher
,ModelPort modelPort
,uint channel)
: timings_(timings)
: timings_(timings)
, dispatcher_(dispatcher)
, modelPort_(modelPort)
, channel_(channel)

View file

@ -21,7 +21,7 @@
* *****************************************************/
/** @file job-ticket.cpp
/** @file job-ticket.cpp
** Implementation details of preparing and performing job invocations.
**
** @todo WIP-WIP-WIP 3/2012
@ -46,7 +46,7 @@ namespace engine {
using vault::engine::JobParameter;
using vault::engine::JobClosure;
using lib::HashVal;
class FrameJobClosure
@ -69,21 +69,27 @@ using vault::engine::JobClosure;
return false;
}
InvocationInstanceID
buildInstanceID (HashVal seed) const override
{
UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time");
}
size_t
hashOfInstance (InvocationInstanceID invoKey) const
hashOfInstance (InvocationInstanceID invoKey) const override
{
UNIMPLEMENTED ("interpret the invoKey and create a suitable hash");
}
void
invokeJobOperation (JobParameter parameter)
invokeJobOperation (JobParameter parameter) override
{
UNIMPLEMENTED ("representation of the job functor");
}
void
signalFailure (JobParameter parameter, JobFailureReason reason)
signalFailure (JobParameter parameter, JobFailureReason reason) override
{
UNIMPLEMENTED ("what needs to be done when a job cant be invoked?");
}
@ -107,5 +113,22 @@ using vault::engine::JobClosure;
}
/**
* Helper for tests: verify the given invocation parameters match this JobTicket.
*/
bool
JobTicket::verifyInstance (JobFunctor& functor, Time nominalTime) const
{
for (Provision const& p : provision_)
if (util::isSameObject (p.jobFunctor, functor))
{
TODO ("actually re-compute the invocation ID !!!!!!!!!!!!");
return true;
}
return false;
}
}} // namespace steam::engine

View file

@ -64,6 +64,7 @@ using vault::engine::JobFunctor;
using lib::LinkedElements;
using lib::OrientationIndicator;
using util::isnil;
using lib::HashVal;
//
//class ExitNode;
@ -108,10 +109,12 @@ using util::isnil;
{
Provision* next{nullptr};
JobFunctor& jobFunctor;
HashVal invocationSeed;
Prerequisites requirements{};
////////////////////TODO some channel or format descriptor here
Provision (JobFunctor& func)
Provision (JobFunctor& func, HashVal seed =0)
: jobFunctor{func}
, invocationSeed{seed}
{ }
};
@ -154,6 +157,10 @@ using util::isnil;
TODO ("validity self check");
return true;
}
protected:
bool verifyInstance (JobFunctor&, Time) const;
};
@ -276,9 +283,11 @@ using util::isnil;
using Spec = typename IT::value_type;
static_assert (lib::meta::is_Tuple<Spec>()); // -- payload of iterator is a tuple
using Func = typename std::tuple_element<0, Spec>::type;
using Preq = typename std::tuple_element<1, Spec>::type;
using Seed = typename std::tuple_element<1, Spec>::type;
using Preq = typename std::tuple_element<2, Spec>::type;
static_assert (lib::meta::is_basically<Func, JobFunctor>()); // -- first component specifies the JobFunctor to use
static_assert (lib::meta::can_IterForEach<Preq>::value); // -- second component is again an iterable sequence
static_assert (lib::meta::is_basically<Seed, HashVal>()); // -- second component provides a specific seed for Invocation-IDs
static_assert (lib::meta::can_IterForEach<Preq>::value); // -- third component is again an iterable sequence
static_assert (std::is_same<typename Preq::value_type, JobTicket>()); // -- which yields JobTicket prerequisites
REQUIRE (not isnil (featureSpec)
,"require at least specification for one channel");
@ -287,8 +296,9 @@ using util::isnil;
for ( ; featureSpec; ++featureSpec)
{
JobFunctor& func = std::get<0> (*featureSpec);
auto& provision = provisionSpec.emplace<Provision> (func);
for (Preq pre = std::get<1> (*featureSpec); pre; ++pre)
HashVal invoSeed = std::get<1> (*featureSpec);
auto& provision = provisionSpec.emplace<Provision> (func, invoSeed);
for (Preq pre = std::get<2> (*featureSpec); pre; ++pre)
provision.requirements.emplace<Prerequisite> (*pre);
}
provisionSpec.reverse(); // retain order of given definitions per channel

View file

@ -71,6 +71,12 @@ namespace engine {
}
InvocationInstanceID
RenderDrive::buildInstanceID (HashVal seed) const
{
UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time");
}
size_t
RenderDrive::hashOfInstance (InvocationInstanceID invoKey) const
{

View file

@ -30,7 +30,7 @@
** calculation process, the CalcStream, maintains this closure instance, as parametrised
** with the appropriate configuration for the specific playback/render process underway.
** Enclosed into this instance lives the actual job planning pipeline, connected at the
** rear to the dispatcher and thus to the fixture and the low-level model
** rear to the dispatcher and thus to the fixture and the low-level model
**
** @todo 4/2023 »Playback Vertical Slice« -- effort towards first integration of render process ////////////TICKET #1221
*/
@ -59,6 +59,7 @@ namespace engine {
// using lib::time::FSecs;
// using lib::time::Time;
using lib::time::FrameCnt;
using lib::HashVal;
/**
@ -68,7 +69,7 @@ namespace engine {
* runtime behaviour of the render activity, like e.g.
* re-scheduling with modified playback speed. Since the
* CalcStream is an conceptual representation of "the rendering",
* the actual engine implementation is kept opaque this way.
* the actual engine implementation is kept opaque this way.
*/
class RenderEnvironment
{
@ -106,11 +107,12 @@ namespace engine {
return META_JOB;
}
bool verify (Time, InvocationInstanceID) const;
size_t hashOfInstance (InvocationInstanceID) const;
bool verify (Time, InvocationInstanceID) const override;
size_t hashOfInstance (InvocationInstanceID) const override;
InvocationInstanceID buildInstanceID(HashVal) const override;
void invokeJobOperation (JobParameter);
void signalFailure (JobParameter, JobFailureReason);
void invokeJobOperation (JobParameter) override;
void signalFailure (JobParameter, JobFailureReason) override;
@ -121,7 +123,7 @@ namespace engine {
*/
RenderDrive (RenderEnvironment& renderEnvironment
,ModelPort port, uint chan)
: engine_{renderEnvironment}
: engine_{renderEnvironment}
{ }

View file

@ -111,10 +111,11 @@ typedef struct lumiera_jobClosure* LumieraJobClosure;
/** opaque ID attached to each individual job invocation.
* Used by the implementation of the Jobs (i.e. the JobClosure)
* for internal organisation; will be fed back on job activation.
* @todo 4/2023 this is meant as marker or placeholder; the idea is
* to generate a unique reproducible hash key for each invocation,
* which can be used for caching; obviously this hash need to be
* built from the JobTicket (based on ProcNode structure + Time)
* @todo 4/2023 for the time being, this is a mere marker for test code;
* for the real engine it is planned to generate a unique reproducible
* hash key for each invocation, which can be used for caching; obviously
* this hash need to be built from the JobTicket, based on ProcNode structure
* and the nominal Time. ///////////////////////////////////TICKET #1293
*/
union InvocationInstanceID
{
@ -134,9 +135,10 @@ union InvocationInstanceID
*/
struct lumiera_jobParameter_struct
{
gavl_time_t nominalTime;
gavl_time_t nominalTime; /////////////////////////////////////////////////////////////////////////TICKET #1295 job invocation parameter: framework to interpret this time
InvocationInstanceID invoKey;
//////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique?
//////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique?
////////////////////////////////////////////////////////////////////////////////////TICKET #1293 job invocation identity
};
typedef struct lumiera_jobParameter_struct lumiera_jobParameter;
typedef lumiera_jobParameter* LumieraJobParameter;
@ -200,6 +202,7 @@ namespace engine {
using lib::time::TimeValue;
using lib::time::Time;
using lib::HashVal;
typedef lumiera_jobParameter const& JobParameter;
@ -237,9 +240,10 @@ namespace engine {
virtual void invokeJobOperation (JobParameter parameter) =0;
virtual void signalFailure (JobParameter,JobFailureReason) =0;
virtual JobKind getJobKind() const =0;
virtual bool verify (Time, InvocationInstanceID) const =0;
virtual size_t hashOfInstance(InvocationInstanceID) const =0;
virtual JobKind getJobKind() const =0;
virtual bool verify (Time, InvocationInstanceID) const =0;
virtual HashVal hashOfInstance(InvocationInstanceID) const =0;
virtual InvocationInstanceID buildInstanceID(HashVal)const =0;
lib::HashVal hash_value (JobParameter) const;
};

View file

@ -47,7 +47,7 @@
#include "lib/test/test-helper.hpp"
#include "vault/engine/job.h"
#include "vault/engine/dummy-job.hpp"
//#include "lib/util.hpp"
#include "lib/util.hpp"
//#include <functional>
//#include <vector>
@ -79,6 +79,7 @@ namespace test {
// using lib::time::Time;
// using mobject::ModelPort;
// using play::Timings;
using lib::HashVal;
using std::make_tuple;
using std::deque;
///////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1294 : organisation of namespaces / includes??
@ -144,10 +145,9 @@ namespace test {
{
auto emptyPrereq = lib::nilIterator<JobTicket&>();
using Iter = decltype(emptyPrereq);
using SpecTuple = std::tuple<JobFunctor&, HashVal, Iter>;
return lib::singleValIterator(
std::tuple<JobFunctor&, Iter>(DummyJob::getFunctor()
,emptyPrereq
));
SpecTuple(DummyJob::getFunctor(), 0, emptyPrereq));
}
}//(End)internal test helpers....
@ -171,6 +171,7 @@ namespace test {
: JobTicket{defineBottomSpec()}
{ };
bool verify_associated (Job const&) const;
private:
};
@ -195,7 +196,19 @@ namespace test {
/** */
/**
* verify the given job instance was actually generated from this JobTicket.
* @remark this test support function actually relies on some specific rigging,
* which typically is prepared by setup of a MockJobTicket.
*/
inline bool
MockJobTicket::verify_associated (Job const& job) const
{
JobFunctor& functor = dynamic_cast<JobFunctor&> (static_cast<JobClosure&> (*job.jobClosure));
Time nominalTime {TimeValue{job.parameter.nominalTime}};
return this->isValid()
and this->verifyInstance(functor, nominalTime);
}

View file

@ -98,12 +98,12 @@ namespace test {
CHECK (not DummyJob::was_invoked (mockJob));
mockJob.triggerJob();
CHECK ( DummyJob::was_invoked (mockJob));
CHECK (DummyJob::was_invoked (mockJob));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (mockJob)));
CHECK (nominalTime == DummyJob::invocationNominalTime (mockJob) );
CHECK (additionalKey == DummyJob::invocationAdditionalKey(mockJob));
Time prevInvocation = DummyJob::invocationTime (mockJob);
Time prevInvocation = DummyJob::invocationTime (mockJob);
mockJob.triggerJob();
CHECK (prevInvocation < DummyJob::invocationTime (mockJob)); // invoked again, recorded new invocation time
CHECK (nominalTime == DummyJob::invocationNominalTime (mockJob) ); // all other Job parameter recorded again unaltered
@ -115,8 +115,10 @@ namespace test {
void
verify_MockJobTicket()
{
MockJobTicket mockTick;
CHECK (mockTick.discoverPrerequisites().empty());
MockJobTicket mockTicket;
CHECK (mockTicket.discoverPrerequisites().empty());
Job mockJob = mockTicket.createJobFor (FrameCoord{});
CHECK (mockTicket.verify_associated (mockJob));
TODO ("cover details of MockJobTicket");
}

View file

@ -66,25 +66,25 @@ namespace engine {
: public JobClosure
{
void
invokeJobOperation (JobParameter parameter)
invokeJobOperation (JobParameter parameter) override
{
invocationLog_[hash_value (parameter)] = Invocation(parameter);
}
void
signalFailure (JobParameter,JobFailureReason)
signalFailure (JobParameter,JobFailureReason) override
{
NOTREACHED ("Job failure is not subject of this test");
}
JobKind
getJobKind() const
getJobKind() const override
{
return META_JOB;
}
bool
verify (Time nominalJobTime, InvocationInstanceID invoKey) const
verify (Time nominalJobTime, InvocationInstanceID invoKey) const override
{
return Time::ANYTIME < nominalJobTime
&& 0 <= invoKey.metaInfo.a
@ -93,8 +93,14 @@ namespace engine {
&& invoKey.metaInfo.b < MAX_PARAM_B;
}
InvocationInstanceID
buildInstanceID (HashVal seed) const override
{
UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time");
}
size_t
hashOfInstance(InvocationInstanceID invoKey) const
hashOfInstance(InvocationInstanceID invoKey) const override
{
return std::hash<int>{} (invoKey.metaInfo.a);
} ////////////////////////////////////////////////////////////////////////TICKET #1293 : this is dangerous and could lead to clashes
@ -133,6 +139,12 @@ namespace engine {
return access_or_default (invocationLog_, hash_value(param)
,NullValue<Invocation>::get());
}
void
clearLog()
{
invocationLog_.clear();
}
};

View file

@ -106,10 +106,11 @@ namespace test {
class OtherClosure
: public JobClosure
{
void invokeJobOperation (JobParameter) { /* irrelevant */ }
void signalFailure (JobParameter,JobFailureReason) { /* irrelevant */ }
JobKind getJobKind() const { return META_JOB; }
bool verify (Time, InvocationInstanceID) const { return false; }
void invokeJobOperation (JobParameter) { /* irrelevant */ }
void signalFailure (JobParameter,JobFailureReason) { /* irrelevant */ }
JobKind getJobKind() const { return META_JOB; }
bool verify (Time, InvocationInstanceID) const { return false; }
InvocationInstanceID buildInstanceID(HashVal) const { return InvocationInstanceID(); }
size_t
hashOfInstance(InvocationInstanceID invoKey) const

View file

@ -69824,6 +69824,90 @@
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682204997554" ID="ID_599701197" MODIFIED="1682205023588" TEXT="Prerequisites hinzuf&#xfc;gen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682627654491" ID="ID_1371147624" MODIFIED="1682627673234" TEXT="Marker definieren und sichtbar machen">
<linktarget COLOR="#48417c" DESTINATION="ID_1371147624" ENDARROW="Default" ENDINCLINATION="-48;56;" ID="Arrow_ID_1753796050" SOURCE="ID_1110039315" STARTARROW="None" STARTINCLINATION="-284;-8;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1682641810117" ID="ID_596391717" MODIFIED="1682641823784" TEXT="Problem: wie integieren?">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1682641827594" ID="ID_361324923" MODIFIED="1682641841154" TEXT="JobTicket selber hat keine VTable">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...und das ist gut so
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1682641932932" ID="ID_554797446" MODIFIED="1682642480303" TEXT="das geh&#xf6;rt damit eigentlich in den JobFunktor">
<node CREATED="1682641950985" ID="ID_775736069" MODIFIED="1682641959993" TEXT="denn der wird vermutlich ein generischer Baustein"/>
<node CREATED="1682641960512" ID="ID_1052805185" MODIFIED="1682641969181" TEXT="und k&#xf6;nnte dann auf die ProcNode delegieren"/>
</node>
<node CREATED="1682642490281" ID="ID_1787652965" MODIFIED="1682642992889">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
<font color="#e62222">&#228;rgerliche Konsequenz</font>: bekomme <b>viele Instanzen</b>&#160;vom JobFunktor
</p>
</body>
</html></richcontent>
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1682691820105" ID="ID_405339335" MODIFIED="1682691853870" TEXT="stattdessen: vom JobFunktor den konstanten Teil berechnen lassen">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1682718443540" ID="ID_116215580" MODIFIED="1682718589879" TEXT="es gen&#xfc;gt, den Marker/Seed zu best&#xe4;tigen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...ich darf nicht daran h&#228;ngenbleiben, da&#223; der Marker literal in der InvocationInstanceID steckt; das mit der Uniton ist ohnehin nur ein tempor&#228;rer Trick und kann nicht dauerhaft so bleiben &#8212; vielmehr ist die L&#246;sung, den chained-Hash-Mechanismus f&#252;r den Test so umzufunktionieren, da&#223; man mit ihm beweisen kann, da&#223; ein ganz bestimmter Job auch aufgerufen wurde.
</p>
</body>
</html></richcontent>
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1682805859751" ID="ID_1020214107" MODIFIED="1682805886125" TEXT="es gibt zwei Implementierungen">
<icon BUILTIN="broken-line"/>
<node CREATED="1682805888320" ID="ID_1779572309" MODIFIED="1682805905593" TEXT="und es ist derzeit unklar, ob das so bleibt...."/>
<node CREATED="1682805906307" ID="ID_308580099" MODIFIED="1682806098603" TEXT="bisher gibt es schon den DummyJob">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...mit dem man zuf&#228;llige pseudo-Invocations erzeugen kann und diese auch sp&#228;ter mithilfe eines statischen Invocation-Log verifizieren.
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1682807066972" ID="ID_1766437793" MODIFIED="1682808824748" TEXT="nun kommt die Konstruktion mit dem MockTicket dazu">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...und die setzt auf die f&#252;r sp&#228;ter tats&#228;chlich vorgesehene Hash-Verkn&#252;pfung auf, welche <i>in diesem Fall auch die nominelle Zeit </i>in die InvocationInstanceID mit einrechnet &#8212; nicht jedoch die reale Deadline (die in der Job-Instanz explizit vermerkt ist)
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1682808327555" ID="ID_782813898" MODIFIED="1682808633170" TEXT="Job-Invocation ist derzeit noch weitgehend ungekl&#xe4;rt">
<arrowlink COLOR="#b75269" DESTINATION="ID_1713196934" ENDARROW="Default" ENDINCLINATION="-871;-27;" ID="Arrow_ID_865176706" STARTARROW="None" STARTINCLINATION="-1263;70;"/>
<icon BUILTIN="stop-sign"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1682205189643" ID="ID_897564380" MODIFIED="1682205211467" TEXT="Problem: Umbau in JobTicket selber blockt">
<icon BUILTIN="stop-sign"/>
<node CREATED="1682205214637" ID="ID_451342844" MODIFIED="1682205353004" TEXT="kann nicht klar sehen, wie ich einen Marker im MockJob hinterlegen kann">
@ -69833,7 +69917,7 @@
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1682205280447" ID="ID_1890320289" MODIFIED="1682205280447" TEXT="derzeit reflektiert das JobTicket noch die Monaden-Struktur"/>
<node CREATED="1682205282092" ID="ID_577681512" MODIFIED="1682205305621" TEXT="und die Abl&#xe4;ufe sind daher ziemlich undurchsichtig"/>
<node CREATED="1682205306451" ID="ID_936014701" MODIFIED="1682205328540" TEXT="die eigentliche Ergebnis/Job-Erzeugung ist das halb mit eingewoben"/>
<node CREATED="1682205306451" ID="ID_936014701" MODIFIED="1682715333366" TEXT="die eigentliche Ergebnis/Job-Erzeugung ist dabei halb mit eingewoben"/>
<node CREATED="1682205329192" ID="ID_1591053369" MODIFIED="1682205341296" TEXT="schwer f&#xfc;r einen Test/Mock zu isolieren"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1682205419719" ID="ID_806137309" MODIFIED="1682212328035" TEXT="Job createJobFor (FrameCoord) ist noch gar nicht implementiert">
@ -69871,6 +69955,10 @@
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682627621082" ID="ID_1110039315" MODIFIED="1682627678946" TEXT="ein Segment hinzuf&#xfc;gen">
<arrowlink COLOR="#48417c" DESTINATION="ID_1371147624" ENDARROW="Default" ENDINCLINATION="-48;56;" ID="Arrow_ID_1753796050" STARTARROW="None" STARTINCLINATION="-284;-8;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682385895644" ID="ID_89969390" MODIFIED="1682385902557" TEXT="Verifikation im Test unterst&#xfc;tzen">
<icon BUILTIN="flag-yellow"/>
@ -70668,6 +70756,13 @@
<icon BUILTIN="help"/>
<node CREATED="1682040544603" ID="ID_1221736313" MODIFIED="1682040594240" TEXT="was steht tats&#xe4;chlich in der PriQueue? Job-Deskriptor oder nur eine Activity?"/>
<node CREATED="1682040603211" ID="ID_1088917301" MODIFIED="1682040621149" TEXT="wenn nicht in der Queue, wo werden Jobs dann gespeichert? und wieder freigegeben?"/>
<node CREATED="1682808411785" ID="ID_1713196934" MODIFIED="1682808633170" TEXT="welche Parameter m&#xfc;ssen einer Invocation im allgemeinen mitgegeben werden">
<linktarget COLOR="#b75269" DESTINATION="ID_1713196934" ENDARROW="Default" ENDINCLINATION="-871;-27;" ID="Arrow_ID_865176706" SOURCE="ID_782813898" STARTARROW="None" STARTINCLINATION="-1263;70;"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682810738487" ID="ID_1493396541" MODIFIED="1682810875535" TEXT="#1295 draft job invocation parameter representation">
<linktarget COLOR="#b22e63" DESTINATION="ID_1493396541" ENDARROW="Default" ENDINCLINATION="-1383;-51;" ID="Arrow_ID_847435526" SOURCE="ID_48632766" STARTARROW="None" STARTINCLINATION="-753;40;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1682040285134" ID="ID_1653553289" MODIFIED="1682040299541" TEXT="Performance-Belange">
<icon BUILTIN="hourglass"/>
@ -76687,6 +76782,10 @@
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680196018888" ID="ID_874815999" LINK="https://issues.lumiera.org/ticket/1287" MODIFIED="1680391903482" TEXT="#1287 invoke simple render job">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1682810738487" ID="ID_48632766" MODIFIED="1682810875535" TEXT="#1295 draft job invocation parameter representation">
<arrowlink COLOR="#b22e63" DESTINATION="ID_1493396541" ENDARROW="Default" ENDINCLINATION="-1383;-51;" ID="Arrow_ID_847435526" STARTARROW="None" STARTINCLINATION="-753;40;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680196129567" ID="ID_988036389" LINK="https://issues.lumiera.org/ticket/1288" MODIFIED="1680392639594" TEXT="#1288 simple video display widget">
<icon BUILTIN="flag-yellow"/>