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.
This commit is contained in:
parent
42f4e403ac
commit
d109f5e1fb
26 changed files with 185 additions and 3446 deletions
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
HIERARCHY-ORIENTATION-INDICATOR.hpp - helper to mark level on tree navigation
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/** @file hierarchy-orientation-indicator.hpp
|
||||
** Helper to support navigating a tree structure.
|
||||
** The OrientationIndicator records reference levels (depth into the tree)
|
||||
** and can then be used to determine the relative orientation between the
|
||||
** previously marked reference level and the current reference level.
|
||||
** This simple state capturing mechanism can be used to track the path
|
||||
** of a tree visitation, or to sync an external stack with a currently
|
||||
** investigated tree level.
|
||||
**
|
||||
** The relative orientation value can be retrieved through an int conversion;
|
||||
** to ease recursive programming, this statefull value can be incremented and
|
||||
** decremented without influencing the captured reference level.
|
||||
**
|
||||
** @see job-ticket.hpp usage example
|
||||
** @see HierarchyOrientationIndicator_test
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HIERARCHY_ORIENTATION_INDICATOR_H
|
||||
#define HIERARCHY_ORIENTATION_INDICATOR_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
namespace lib {
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
class OrientationIndicator
|
||||
{
|
||||
size_t refLevel_;
|
||||
ptrdiff_t offset_;
|
||||
|
||||
public:
|
||||
OrientationIndicator()
|
||||
: refLevel_(0)
|
||||
, offset_(0)
|
||||
{ }
|
||||
|
||||
// using default copy/assignment
|
||||
|
||||
operator ptrdiff_t() const
|
||||
{
|
||||
return offset_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
markRefLevel (size_t newRefLevel)
|
||||
{
|
||||
REQUIRE (newRefLevel < size_t(std::numeric_limits<ptrdiff_t>::max()) );
|
||||
|
||||
offset_ -= newRefLevel - refLevel_;
|
||||
refLevel_ = newRefLevel;
|
||||
}
|
||||
|
||||
/** define the current offset as new reference point */
|
||||
OrientationIndicator&
|
||||
markRefLevel ()
|
||||
{
|
||||
REQUIRE (0 < ptrdiff_t(refLevel_) + offset_);
|
||||
|
||||
markRefLevel (refLevel_ + offset_);
|
||||
ENSURE (offset_ == 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OrientationIndicator&
|
||||
resetToRef ()
|
||||
{
|
||||
offset_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OrientationIndicator&
|
||||
operator+= (int adj)
|
||||
{
|
||||
offset_ += adj;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OrientationIndicator&
|
||||
operator-= (int adj)
|
||||
{
|
||||
offset_ -= adj;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OrientationIndicator&
|
||||
operator++ ()
|
||||
{
|
||||
this->operator +=(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OrientationIndicator&
|
||||
operator-- ()
|
||||
{
|
||||
this->operator -=(1);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*HIERARCHY_ORIENTATION_INDICATOR_HPP_*/
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
CalcPlanContinuation - closure for planning a chunk of jobs
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
/** @file calc-plan-continuation.cpp
|
||||
** Implementation elements of render process planning.
|
||||
** @deprecated 4/2023 »Playback Vertical Slice« -- reworked into the RenderDrive /////////////////////////TICKET #1221
|
||||
*/
|
||||
|
||||
|
||||
#include "steam/engine/calc-plan-continuation.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/engine/job-ticket.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
//#include "lib/frameid.hpp"
|
||||
//#include "steam/state.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
||||
|
||||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
|
||||
/** entry point (interface JobClosure): invoke the concrete job operation.
|
||||
* In this case, the job operation is responsible for planning a chunk of actual render jobs.
|
||||
*/
|
||||
void
|
||||
CalcPlanContinuation::invokeJobOperation (JobParameter parameter)
|
||||
{
|
||||
ASSERT (parameter.nominalTime == timings_.getFrameStartAt (parameter.invoKey.frameNumber));
|
||||
|
||||
this->performJobPlanningChunk (parameter.invoKey.frameNumber);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CalcPlanContinuation::signalFailure (JobParameter parameter, JobFailureReason reason)
|
||||
{
|
||||
UNIMPLEMENTED ("what needs to be done when a planning continuation cant be invoked?");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CalcPlanContinuation::verify (Time nominalTime, InvocationInstanceID invoKey) const
|
||||
{
|
||||
return timings_.isValid()
|
||||
&& Time::MIN < nominalTime && nominalTime < Time::MAX
|
||||
&& nominalTime == timings_.getFrameStartAt (invoKey.frameNumber);
|
||||
}
|
||||
|
||||
|
||||
InvocationInstanceID
|
||||
CalcPlanContinuation::buildInstanceID (HashVal seed) const
|
||||
{
|
||||
UNIMPLEMENTED ("systematically generate an invoKey, distinct for the nominal time");
|
||||
}
|
||||
|
||||
size_t
|
||||
CalcPlanContinuation::hashOfInstance (InvocationInstanceID invoKey) const
|
||||
{
|
||||
return boost::hash_value (invoKey.frameNumber);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Job
|
||||
CalcPlanContinuation::prepareRenderPlanningFrom (FrameCnt startFrame)
|
||||
{
|
||||
InvocationInstanceID invoKey;
|
||||
invoKey.frameNumber = startFrame;
|
||||
Time nominalPlanningStartTime = timings_.getFrameStartAt (startFrame);
|
||||
|
||||
return Job(*this, invoKey, nominalPlanningStartTime);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CalcPlanContinuation::performJobPlanningChunk(FrameCnt nextStartFrame)
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
|
||||
JobPlanningSequence jobs = dispatcher_.onCalcStream(modelPort_, channel_)
|
||||
.establishNextJobs(refPoint);
|
||||
|
||||
Job nextChunkOfPlanning = buildFollowUpJobFrom (refPoint);
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301 TimeAnchor refPoint(timings_, nextStartFrame);
|
||||
|
||||
UNIMPLEMENTED ("the actual meat: access the scheduler and fed those jobs");
|
||||
}
|
||||
|
||||
|
||||
Job
|
||||
CalcPlanContinuation::buildFollowUpJobFrom (TimeAnchor const& refPoint)
|
||||
{
|
||||
return this->prepareRenderPlanningFrom(
|
||||
refPoint.getNextAnchorPoint());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace engine
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
CALC-PLAN-CONTINUATION.hpp - closure for planning a chunk of jobs
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file calc-plan-continuation.hpp
|
||||
** A specialised render job to care for the planning of the calculation process itself.
|
||||
** Rendering is seen as an open-ended, ongoing process, and thus the management and planning
|
||||
** of the render process itself is performed chunk wise and embedded into the other rendering
|
||||
** calculations. The _"rendering-as-it-is-planned-right-now"_ can be represented as a closure
|
||||
** to the jobs, which perform and update this plan on the go. And in fact, the head of the
|
||||
** calculation process, the CalcStream, holds onto such a closure to access current planning.
|
||||
**
|
||||
** @deprecated 4/2023 »Playback Vertical Slice« -- reworked into the RenderDrive /////////////////////////TICKET #1221
|
||||
*/
|
||||
|
||||
|
||||
#ifndef STEAM_ENGINE_CALC_PLAN_CONTINUATION_H
|
||||
#define STEAM_ENGINE_CALC_PLAN_CONTINUATION_H
|
||||
|
||||
#include "steam/common.hpp"
|
||||
#include "steam/mobject/model-port.hpp"
|
||||
#include "steam/engine/time-anchor.hpp"
|
||||
#include "steam/engine/dispatcher.hpp"
|
||||
#include "steam/play/timings.hpp"
|
||||
#include "vault/engine/job.h"
|
||||
|
||||
|
||||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
// using std::function;
|
||||
using vault::engine::JobParameter;
|
||||
using vault::engine::JobClosure;
|
||||
using mobject::ModelPort;
|
||||
// using lib::time::TimeSpan;
|
||||
// using lib::time::FSecs;
|
||||
// using lib::time::Time;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::HashVal;
|
||||
|
||||
|
||||
/**
|
||||
* Special job to perform the job planning.
|
||||
* This closure extends the existing planning of frame jobs to add a chunk
|
||||
* of additional future jobs. Included with this chunk will be a recursive
|
||||
* self re-invocation to trigger planning of the next chunk. Overall, this
|
||||
* planning process is determined and controlled by the CalcStream owning
|
||||
* this closure.
|
||||
*
|
||||
* @deprecated 4/2023 »Playback Vertical Slice« -- reworked into the RenderDrive //////////////////////////TICKET #1221
|
||||
*/
|
||||
class CalcPlanContinuation
|
||||
: public JobClosure
|
||||
{
|
||||
|
||||
play::Timings const& timings_;
|
||||
Dispatcher& dispatcher_;
|
||||
const ModelPort modelPort_;
|
||||
const uint channel_;
|
||||
|
||||
|
||||
/* === JobClosure Interface === */
|
||||
|
||||
JobKind
|
||||
getJobKind() const override
|
||||
{
|
||||
return META_JOB;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
CalcPlanContinuation(play::Timings const& timings
|
||||
,Dispatcher& dispatcher
|
||||
,ModelPort modelPort
|
||||
,uint channel)
|
||||
: timings_(timings)
|
||||
, dispatcher_(dispatcher)
|
||||
, modelPort_(modelPort)
|
||||
, channel_(channel)
|
||||
{ }
|
||||
|
||||
/** create the "start trigger job"
|
||||
* Scheduling this job will effectively get a calculation stream
|
||||
* into active processing, since it causes the first chunk of job planning
|
||||
* plus the automated scheduling of follow-up planning jobs. The relation
|
||||
* to real (wall clock) time will be established when the returned job
|
||||
* is actually invoked
|
||||
* @param startFrame where to begin rendering, relative to the nominal
|
||||
* time grid implicitly given by the ModelPort to be pulled
|
||||
*/
|
||||
Job prepareRenderPlanningFrom (FrameCnt startFrame);
|
||||
|
||||
|
||||
private:
|
||||
void performJobPlanningChunk(FrameCnt nextStartFrame);
|
||||
Job buildFollowUpJobFrom (TimeAnchor const& refPoint);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace steam::engine
|
||||
#endif
|
||||
|
|
@ -24,33 +24,17 @@
|
|||
/** @file dispatch-table.cpp
|
||||
** Implementation details of render job generation.
|
||||
** @todo draft from 2011, stalled, relevance not yet clear
|
||||
** @todo 6/2023 still unimplemented, as is the Fixture, which needs to back the Dispatcher
|
||||
*/
|
||||
|
||||
|
||||
#include "steam/engine/dispatch-table.hpp"
|
||||
//#include "lib/frameid.hpp"
|
||||
//#include "steam/state.hpp"
|
||||
|
||||
|
||||
|
||||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
/** */
|
||||
FrameCoord
|
||||
DispatchTable::locateRelative (FrameCoord const&, FrameCnt frameOffset)
|
||||
{
|
||||
UNIMPLEMENTED ("real implementation of the core dispatch operation");
|
||||
}
|
||||
|
||||
bool
|
||||
DispatchTable::isEndOfChunk (FrameCnt, ModelPort port)
|
||||
{
|
||||
UNIMPLEMENTED ("determine when to finish a planning chunk");
|
||||
}
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
|
||||
JobTicket&
|
||||
DispatchTable::getJobTicketFor (size_t, TimeValue nominalTime)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
/** @file dispatch-table.hpp
|
||||
** Implementation details of render job generation.
|
||||
** @todo draft from 2011, stalled, relevance not yet clear
|
||||
** @todo 6/2023 »PlaybackVerticalSlice« : completing and integrating the core engine step by step
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ namespace engine {
|
|||
// class ExitNode;
|
||||
|
||||
/**
|
||||
* @todo 11/11 extremely fuzzy at the moment
|
||||
* @todo 6/2023 gradually building up the core engine components...
|
||||
*/
|
||||
class DispatchTable
|
||||
: public Dispatcher
|
||||
|
|
@ -53,10 +53,6 @@ namespace engine {
|
|||
|
||||
/* ==== Dispatcher interface ==== */
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
FrameCoord locateRelative (FrameCoord const&, FrameCnt frameOffset) override;
|
||||
bool isEndOfChunk (FrameCnt, ModelPort port) override;
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
size_t resolveModelPort (ModelPort) override;
|
||||
JobTicket& getJobTicketFor (size_t, TimeValue nominalTime) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@
|
|||
|
||||
/** @file dispatcher.cpp
|
||||
** Implementation parts of job generation within an ongoing render process
|
||||
** @todo valid draft, unfortunately stalled in 2013
|
||||
** @todo 2023 do we actually need a separate translation unit for this?
|
||||
*/
|
||||
|
||||
|
||||
#include "steam/engine/dispatcher.hpp"
|
||||
//#include "lib/frameid.hpp"
|
||||
//#include "steam/state.hpp"
|
||||
|
||||
|
||||
|
||||
|
|
@ -39,26 +37,6 @@ namespace engine {
|
|||
Dispatcher::~Dispatcher() { } // emit VTables and Typeinfo here....
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
/** @todo WIP */
|
||||
FrameCoord
|
||||
Dispatcher::JobBuilder::relativeFrameLocation (TimeAnchor& refPoint, FrameCnt frameOffset)
|
||||
{
|
||||
FrameCoord frame;
|
||||
frame.absoluteNominalTime = refPoint;
|
||||
frame.absoluteFrameNumber = refPoint.getStartFrame();
|
||||
frame.realTimeDeadline = refPoint.establishDeadlineFor (frameOffset);
|
||||
// frame.modelPort = this->modelPort_; ////////////////////////////TICKET #1301 : translation to model-port-ID now when Dispatcher-Pipeline is built
|
||||
// frame.channelNr = this->channel_;
|
||||
|
||||
ENSURE (frame.isDefined());
|
||||
return dispatcher_->locateRelative (frame, frameOffset);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
** the Dispatcher is responsible for transforming the generic setup of such a calculation stream
|
||||
** into a sequence of concrete jobs, anchored at some distinct point in time.
|
||||
**
|
||||
** @todo valid draft, unfortunately stalled in 2013
|
||||
** @todo as of 4/2023 a complete rework of the Dispatcher is underway //////////////////////////////////////TICKET #1275
|
||||
*/
|
||||
|
||||
|
|
@ -38,12 +37,11 @@
|
|||
|
||||
#include "steam/common.hpp"
|
||||
#include "steam/mobject/model-port.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/engine/job-ticket.hpp"
|
||||
#include "steam/engine/job-planning.hpp"
|
||||
#include "steam/play/timings.hpp"
|
||||
#include "steam/play/output-slot.hpp"
|
||||
#include "lib/iter-tree-explorer.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
|
||||
|
|
@ -60,8 +58,9 @@ namespace engine {
|
|||
using mobject::ModelPort;
|
||||
using play::Timings;
|
||||
using play::DataSink;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::time::TimeValue;
|
||||
using lib::time::TimeSpan;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::time::FSecs;
|
||||
using lib::time::Time;
|
||||
|
||||
|
|
@ -91,33 +90,13 @@ namespace engine {
|
|||
class Dispatcher
|
||||
: util::NonCopyable
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsolete
|
||||
struct JobBuilder
|
||||
{
|
||||
Dispatcher* dispatcher_;
|
||||
ModelPort modelPort_;
|
||||
uint channel_;
|
||||
|
||||
FrameCoord relativeFrameLocation (TimeAnchor& refPoint, FrameCnt frameCountOffset =0);
|
||||
|
||||
JobPlanningSequence
|
||||
establishNextJobs (TimeAnchor& refPoint)
|
||||
{
|
||||
return JobPlanningSequence(
|
||||
relativeFrameLocation(refPoint),
|
||||
*dispatcher_);
|
||||
}
|
||||
};
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsolete
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
struct PipeFrameTick;
|
||||
|
||||
template<class IT>
|
||||
struct PipelineBuilder;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
virtual ~Dispatcher(); ///< this is an interface
|
||||
|
||||
|
|
@ -157,36 +136,6 @@ namespace engine {
|
|||
*/
|
||||
virtual size_t resolveModelPort (ModelPort) =0;
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/** core dispatcher operation: based on the coordinates of a reference point,
|
||||
* establish binding frame number, nominal time and real (wall clock) deadline.
|
||||
* @return new FrameCoord record (copy), with the nominal time, frame number
|
||||
* and deadline adjusted in accordance to the given frame offset.
|
||||
*/
|
||||
virtual FrameCoord locateRelative (FrameCoord const&, FrameCnt frameOffset) =0;
|
||||
|
||||
virtual bool isEndOfChunk (FrameCnt, ModelPort port) =0;
|
||||
|
||||
////////TODO: API-1 = just get next frame, without limitations .... CHECK
|
||||
////////TODO: API-2 = query limitation of planning chunk .... CHECK
|
||||
////////TODO: API-3 = establish next chunk .... still WIP
|
||||
|
||||
////////TODO: Question: why not embedding the time anchor directly within the location generator??
|
||||
//////// Answer: no this would lead to a huge blob called "the dispatcher"
|
||||
|
||||
////////TODO: immediate point to consider: the time anchor is responsible for the real timing calculations. But how to introduce the play strategy *here* ?
|
||||
|
||||
////////////TODO: the solution is simple: get rid of the additional job placed magically into the chunk
|
||||
//////////// instead, provide a dedicated API function to create exactly that job
|
||||
//////////// and *enclosed* into a specialised JobClosure subclass, embody the code for the follow-up
|
||||
//////////// As a corollary: the scheduling deadline should be defined right *within* the job!
|
||||
|
||||
////////////TODO: remaining issues
|
||||
//////////// - the TimeAnchor needs to be created directly from the JobParameter. No mutable state!
|
||||
//////////// - but this leads to a lot of duplicated Timings records, unless we rewrite the TimeAnchor to be noncopyable and use a Timings const&
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
/**
|
||||
* Core Dispatcher operation: locate the appropriate Segment and
|
||||
* retrieve/derive a »blueprint« for render job generation.
|
||||
|
|
@ -272,7 +221,7 @@ namespace engine {
|
|||
timeRange (Time start, Time after)
|
||||
{
|
||||
SRC::activate (start,after);
|
||||
return buildPipeline (lib::treeExplore (move(*this)));
|
||||
return buildPipeline (lib::explore (move(*this)));
|
||||
} // expected next to invoke pullFrom(port,sink)
|
||||
|
||||
|
||||
|
|
@ -340,9 +289,9 @@ namespace engine {
|
|||
protected:
|
||||
/** @internal type rebinding helper to move the given tree-Explorer pipeline
|
||||
* and layer a new PipelineBuilder subclass on top.
|
||||
* @note TreeExplorer itself is defined in a way to always strip away any existing
|
||||
* top-level TreeExplorer, then add a new processing layer and finally
|
||||
* place a new TreeExplorer layer on top. Taken together, this setup will
|
||||
* @note IterExplorer itself is defined in a way to always strip away any existing
|
||||
* top-level IterExplorer, then add a new processing layer and finally
|
||||
* place a new IterExplorer layer on top. Taken together, this setup will
|
||||
* *slice away* the actual PipelineBuilder layer, move the resulting pipeline
|
||||
* into the next building step and finally produce a cleanly linked processing
|
||||
* pipeline without any interspersed builders. Yet still, partially constructed
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
FRAME-COORD.hpp - unique distinct coordinates of a frame to be calculated
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file frame-coord.hpp
|
||||
** Tuple data type to address distinct frames within the render engine calculations.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef STEAM_ENGINE_FRAME_COORD_H
|
||||
#define STEAM_ENGINE_FRAME_COORD_H
|
||||
|
||||
#include "steam/common.hpp"
|
||||
#include "steam/mobject/model-port.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
using mobject::ModelPort;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::time::TimeValue;
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::Time;
|
||||
|
||||
|
||||
/**
|
||||
* effective coordinates of a frame to be calculated.
|
||||
* Frame coordinates are produced as result of the Dispatcher call,
|
||||
* thus forming the foundation of an actual ProcNode invocation
|
||||
* A frame render job can be characterised by
|
||||
* - the nominal (timeline) time of the frame
|
||||
* - the corresponding frame-number
|
||||
* - a real wall-clock time deadline for delivery
|
||||
* - the actual node to pull data from, defined indirectly through
|
||||
* ModelPort and channel number (as used within the Segmentation)
|
||||
*
|
||||
* @remarks consider frame coordinates being "boiled down" to the actual values.
|
||||
* There is no reference to any kind of time grid (or similar session internals).
|
||||
*
|
||||
* @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
|
||||
* @todo 4/23 WIP-WIP-WIP recast the dispatch- and job invocation sequence
|
||||
*/
|
||||
struct FrameCoord
|
||||
{
|
||||
|
||||
TimeVar absoluteNominalTime;
|
||||
FrameCnt absoluteFrameNumber;
|
||||
|
||||
size_t modelPortIDX;
|
||||
|
||||
|
||||
/** build an \em undefined frame location */
|
||||
FrameCoord()
|
||||
: absoluteNominalTime{Time::NEVER}
|
||||
, absoluteFrameNumber{std::numeric_limits<FrameCnt>::max()}
|
||||
, modelPortIDX{0}
|
||||
{ }
|
||||
|
||||
explicit
|
||||
FrameCoord (TimeValue nominalTime, size_t portIDX =0)
|
||||
: absoluteNominalTime{nominalTime}
|
||||
, absoluteFrameNumber{std::numeric_limits<FrameCnt>::max()}
|
||||
, modelPortIDX{portIDX}
|
||||
{ }
|
||||
|
||||
FrameCoord (TimeValue nominalTime, FrameCnt frameNr, size_t portIDX =0)
|
||||
: absoluteNominalTime{nominalTime}
|
||||
, absoluteFrameNumber{frameNr}
|
||||
, modelPortIDX{portIDX}
|
||||
{ }
|
||||
|
||||
// using default copy operations
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
/**
|
||||
* Facility for producing a sequence of FrameCoord.
|
||||
* This interface describes the essence of generating
|
||||
* a series of frame locations, which is necessary for
|
||||
* planning render jobs. To implement it, actually some
|
||||
* kind of [frame grid](\ref lib::time::Quantiser) is
|
||||
* necessary -- in practice we use a Dispatcher, which is
|
||||
* backed by the Segmentation (i.e. the render nodes network).
|
||||
* @deprecated 6/2023 obsolete and replaced by the pipeline builder API in Dispatcher
|
||||
*/
|
||||
class FrameSequencer
|
||||
: util::NonCopyable
|
||||
{
|
||||
|
||||
public:
|
||||
virtual ~FrameSequencer(); ///< this is an interface
|
||||
|
||||
FrameCoord
|
||||
getNextFrame (FrameCoord refPoint)
|
||||
{
|
||||
return locateRelative (refPoint, +1 );
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual FrameCoord locateRelative (FrameCoord const&, FrameCnt frameOffset) =0;
|
||||
};
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
|
||||
|
||||
|
||||
}} // namespace steam::engine
|
||||
#endif
|
||||
|
|
@ -70,7 +70,6 @@
|
|||
#include "steam/common.hpp"
|
||||
#include "vault/engine/job.h"
|
||||
#include "steam/engine/job-ticket.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/play/output-slot.hpp"
|
||||
#include "steam/play/timings.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
|
@ -87,6 +86,7 @@ namespace engine {
|
|||
using play::DataSink;
|
||||
using play::Timings;
|
||||
using lib::time::Time;
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::Duration;
|
||||
|
||||
|
||||
|
|
@ -212,7 +212,6 @@ namespace engine {
|
|||
{ }
|
||||
|
||||
|
||||
|
||||
Time
|
||||
doCalcDeadline(Timings const& timings)
|
||||
{
|
||||
|
|
@ -226,325 +225,9 @@ namespace engine {
|
|||
- jobTicket_.getExpectedRuntime()
|
||||
- timings.engineLatency;
|
||||
}
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
/** build a new JobPlanning object,
|
||||
* set to explore the prerequisites
|
||||
* at the given planning situation
|
||||
*/
|
||||
JobPlanning
|
||||
discoverPrerequisites() const
|
||||
{
|
||||
if (isnil (plannedOperations_))
|
||||
return JobPlanning();
|
||||
else
|
||||
return JobPlanning (plannedOperations_->discoverPrerequisites (0) //////////////////////////////TICKET #1301 : was: point_to_calculate_.channelNr)
|
||||
,this->point_to_calculate_);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
|
||||
/** integrate another chain of prerequisites into the current evaluation line.
|
||||
* Further evaluation will start to visit prerequisites from the new starting point,
|
||||
* and return to the current evaluation chain later on exhaustion of the side chain.
|
||||
* Especially in case the current evaluation is empty or already exhausted, the
|
||||
* new starting point effectively replaces the current evaluation point */
|
||||
friend void
|
||||
integrate (JobPlanning const& newStartingPoint, JobPlanning& existingPlan)
|
||||
{
|
||||
if (isnil (existingPlan.plannedOperations_))
|
||||
{ // current evaluation is exhausted: switch to new starting point
|
||||
existingPlan.point_to_calculate_ = newStartingPoint.point_to_calculate_;
|
||||
}
|
||||
existingPlan.plannedOperations_.push (newStartingPoint.plannedOperations_);
|
||||
existingPlan.plannedOperations_.markTreeLocation();
|
||||
}
|
||||
|
||||
|
||||
/* === Iteration control API for IterStateWrapper== */
|
||||
|
||||
bool
|
||||
checkPoint () const
|
||||
{
|
||||
return not isnil (plannedOperations_);
|
||||
}
|
||||
|
||||
JobPlanning&
|
||||
yield () const
|
||||
{
|
||||
REQUIRE (checkPoint());
|
||||
return unConst(*this);
|
||||
}
|
||||
|
||||
void
|
||||
iterNext ()
|
||||
{
|
||||
plannedOperations_.pullNext();
|
||||
plannedOperations_.markTreeLocation();
|
||||
}
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/**
|
||||
* iterator, exposing a sequence of JobPlanning elements
|
||||
*/
|
||||
class PlanningState
|
||||
: public lib::IterStateWrapper<JobPlanning>
|
||||
{
|
||||
typedef lib::IterStateWrapper<JobPlanning> _Iter;
|
||||
|
||||
|
||||
public:
|
||||
/** inactive evaluation */
|
||||
PlanningState()
|
||||
: _Iter()
|
||||
{ }
|
||||
|
||||
explicit
|
||||
PlanningState (JobPlanning const& startingPoint)
|
||||
: _Iter(startingPoint) // note: invoking copy ctor on state core
|
||||
{ }
|
||||
|
||||
// using the standard copy operations
|
||||
|
||||
|
||||
|
||||
|
||||
/* === API for JobPlanningSequence to expand the tree of prerequisites === */
|
||||
|
||||
/** attach and integrate the given planning details into this planning state.
|
||||
* Actually the evaluation proceeds depth-first with the other state,
|
||||
* returning to the current position later for further evaluation */
|
||||
PlanningState &
|
||||
wrapping (JobPlanning const& startingPoint)
|
||||
{
|
||||
integrate (startingPoint, this->stateCore());
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlanningState &
|
||||
usingSequence (PlanningState const& prerequisites)
|
||||
{
|
||||
if (isnil (prerequisites))
|
||||
return *this;
|
||||
else
|
||||
return this->wrapping(*prerequisites);
|
||||
// explanation: PlanningState represents a sequence of successive planning points.
|
||||
// actually this is implemented by switching an embedded JobPlanning element
|
||||
// through a sequence of states. Thus the initial state of an investigation
|
||||
// (which is a JobPlanning) can stand-in for the sequence of prerequisites
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Extension point to be picked up by ADL.
|
||||
* Provides access for the JobPlanningSequence
|
||||
* for combining and expanding partial results.
|
||||
*/
|
||||
friend PlanningState&
|
||||
build (PlanningState& attachmentPoint)
|
||||
{
|
||||
return attachmentPoint;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** this is the core operation to drive planning ahead:
|
||||
* discover the prerequisites of some operation -- here
|
||||
* "prerequisites" are those operations to be performed
|
||||
* within separate Jobs beforehand.
|
||||
* @note this function is intended to be flat-mapped (">>=")
|
||||
* onto a tree-like monad representing the evaluation process.
|
||||
*/
|
||||
inline PlanningState
|
||||
expandPrerequisites (JobPlanning const& calculationStep)
|
||||
{
|
||||
PlanningState newSubEvaluation(
|
||||
calculationStep.discoverPrerequisites());
|
||||
return newSubEvaluation;
|
||||
}
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
/**
|
||||
* Abstraction: a Facility to establish frame coordinates
|
||||
* and identify and access the execution plan for this frame.
|
||||
* @see Dispatcher the service interface actually used
|
||||
*/
|
||||
class FrameLocator
|
||||
: public FrameSequencer
|
||||
{
|
||||
public:
|
||||
|
||||
JobTicket&
|
||||
getJobTicketFor (FrameCoord const& location)
|
||||
{
|
||||
return accessJobTicket (location.modelPortIDX, location.absoluteNominalTime);
|
||||
}
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
bool canContinue (FrameCoord const& location)
|
||||
{
|
||||
// return not isEndOfChunk (location.absoluteFrameNumber,
|
||||
// location.modelPort);
|
||||
}
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : likely to become obsolete
|
||||
|
||||
protected:
|
||||
virtual JobTicket& accessJobTicket (size_t, TimeValue nominalTime) =0;
|
||||
virtual bool isEndOfChunk (FrameCnt, ModelPort port) =0;
|
||||
};
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/**
|
||||
* Generate a sequence of starting points for Job planning,
|
||||
* based on the underlying frame grid. This sequence will be
|
||||
* used to seed a JobPlanningSequence for generating a chunk
|
||||
* of frame render jobs within a given CalcStream in the player.
|
||||
* Evaluation of that seed will then expand each starting point,
|
||||
* until all prerequisites for those frames are discovered,
|
||||
* resulting in a sequence of Jobs ready to be handed over
|
||||
* to the scheduler for time-bound activation.
|
||||
*/
|
||||
class PlanningStepGenerator
|
||||
{
|
||||
FrameLocator* locationGenerator_;
|
||||
FrameCoord currentLocation_;
|
||||
|
||||
//////////////////////////////////////////TODO duplicated storage of a FrameCoord record
|
||||
//////////////////////////////////////////TODO nextEvaluation_ is only needed to initialise the "current" sequence
|
||||
//////////////////////////////////////////TODO within the RecursiveSelfIntegration strategy. Maybe this storage could be collapsed?
|
||||
JobPlanning nextEvaluation_;
|
||||
|
||||
void
|
||||
use_current_location_as_starting_point_for_planning()
|
||||
{
|
||||
JobTicket& processingPlan = locationGenerator_->getJobTicketFor (currentLocation_);
|
||||
|
||||
nextEvaluation_ = JobPlanning(processingPlan.startExploration()
|
||||
,currentLocation_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
typedef JobPlanning value_type;
|
||||
typedef JobPlanning& reference;
|
||||
typedef JobPlanning * pointer;
|
||||
|
||||
|
||||
PlanningStepGenerator(FrameCoord startPoint, FrameLocator& locator)
|
||||
: locationGenerator_(&locator)
|
||||
, currentLocation_(startPoint)
|
||||
{
|
||||
REQUIRE (startPoint.isDefined());
|
||||
use_current_location_as_starting_point_for_planning();
|
||||
}
|
||||
|
||||
// default copyable
|
||||
|
||||
|
||||
/* === Iteration control API for IterStateWrapper== */
|
||||
|
||||
bool
|
||||
checkPoint () const
|
||||
{
|
||||
return currentLocation_.isDefined();
|
||||
} // might indicate end of this planning chunk (or of playback altogether)
|
||||
|
||||
|
||||
JobPlanning&
|
||||
yield () const
|
||||
{
|
||||
ENSURE (checkPoint());
|
||||
return unConst(this)->nextEvaluation_;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
iterNext ()
|
||||
{
|
||||
if (locationGenerator_->canContinue (currentLocation_))
|
||||
{
|
||||
currentLocation_ = locationGenerator_->getNextFrame (currentLocation_);
|
||||
this->use_current_location_as_starting_point_for_planning();
|
||||
ENSURE (this->checkPoint());
|
||||
}
|
||||
else
|
||||
{ // indicate end-of playback or a jump to another playback position
|
||||
currentLocation_ = FrameCoord();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* type definitions for building the JobPlaningSequence */
|
||||
|
||||
typedef PlanningState (*SIG_expandPrerequisites) (JobPlanning const&);
|
||||
|
||||
typedef lib::IterExplorer<PlanningStepGenerator
|
||||
,lib::iter_explorer::RecursiveSelfIntegration> JobPlanningChunkStartPoint;
|
||||
typedef JobPlanningChunkStartPoint::FlatMapped<SIG_expandPrerequisites>::Type ExpandedPlanningSequence;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This iterator represents a pipeline to pull planned jobs from.
|
||||
* To dispatch individual frame jobs for rendering, this pipeline is generated
|
||||
* and wired internally such as to interpret the render node definitions.
|
||||
*
|
||||
* \par Explanation of the structure
|
||||
*
|
||||
* The JobPlanningSequence is constructed from several nested layers of functionality
|
||||
* - for the client, it is an iterator, exposing a sequence of JobPlanning elements
|
||||
* - a JobPlanning element allows to add a frame render job to the scheduler
|
||||
* - actually such an element can even be \em converted directly into a Job (descriptor)
|
||||
* - the sequence of such JobPlanning elements (that is, the iterator) is called a PlanningState,
|
||||
* since evaluating this iterator effectively drives the process of job planning ahead
|
||||
* - this planning process is \em implemented as a recursive evaluation and exploration of
|
||||
* a tree of prerequisites; these prerequisites are defined in the JobTicket datastructure
|
||||
* - there is an underlying grid of evaluation starting points, each corresponding to a
|
||||
* single frame. Typically, each frame generates at least two jobs, one for fetching
|
||||
* data, and one for the actual calculations. Depending on the actual render network,
|
||||
* a lot of additional jobs might be necessary
|
||||
* - this basic frame grid is generated by the PlanningStepGenerator, which is
|
||||
* effectively backed by the Dispatcher and thus the render node model.
|
||||
*
|
||||
* @remarks JobPlanningSequence is a monad, and the operation to explore the prerequisites
|
||||
* is applied by the \c >>= (monad flat map operation). This approach allows us
|
||||
* to separate the technicalities of exhausting tree exploration from the actual
|
||||
* "business code" to deal with frame job dependencies
|
||||
*/
|
||||
class JobPlanningSequence
|
||||
: public ExpandedPlanningSequence
|
||||
{
|
||||
|
||||
public:
|
||||
JobPlanningSequence(engine::FrameCoord startPoint, FrameLocator& locator)
|
||||
: ExpandedPlanningSequence(
|
||||
JobPlanningChunkStartPoint(
|
||||
PlanningStepGenerator(startPoint,locator))
|
||||
|
||||
>>= expandPrerequisites) // "flat map" (monad operation)
|
||||
{ }
|
||||
};
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
|
||||
|
||||
}} // namespace steam::engine
|
||||
#endif
|
||||
}}// namespace steam::engine
|
||||
#endif /*STEAM_ENGINE_JOB_PLANNING_H*/
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
|
||||
#include "steam/common.hpp"
|
||||
#include "vault/engine/job.h"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/engine/exit-node.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/linked-elements.hpp"
|
||||
|
|
@ -56,8 +55,9 @@ namespace engine {
|
|||
using vault::engine::Job;
|
||||
using vault::engine::JobFunctor;
|
||||
using vault::engine::JobClosure; /////////////////////////////////////////////////////////////////////TICKET #1287 : fix actual interface down to JobFunctor (after removing C structs)
|
||||
using lib::time::Duration;
|
||||
using lib::LinkedElements;
|
||||
using lib::time::Duration;
|
||||
using lib::time::Time;
|
||||
using util::isnil;
|
||||
using lib::HashVal;
|
||||
using lib::LUID;
|
||||
|
|
@ -82,11 +82,12 @@ using lib::LUID;
|
|||
* on invocation another, specific function (= the job).
|
||||
*
|
||||
* @note JobTicket is effectively immutable after construction
|
||||
* @todo 4/23 WIP-WIP-WIP defining the invocation sequence and render jobs
|
||||
* @todo 6/23 WIP rework and integration for »PlaybackVerticalSlice«
|
||||
*/
|
||||
class JobTicket
|
||||
: util::NonCopyable
|
||||
{
|
||||
/** @internal management of prerequisites */
|
||||
struct Prerequisite
|
||||
{
|
||||
Prerequisite* next{nullptr}; // for intrusive list
|
||||
|
|
@ -100,7 +101,7 @@ using lib::LUID;
|
|||
using Prerequisites = LinkedElements<Prerequisite>;
|
||||
|
||||
|
||||
/** what handling this task entails */
|
||||
/** @internal what handling this task entails */
|
||||
struct Provision
|
||||
{
|
||||
JobFunctor& jobFunctor;
|
||||
|
|
@ -124,6 +125,8 @@ using lib::LUID;
|
|||
template<class ALO>
|
||||
static Provision buildProvisionSpec (ExitNode const&, ALO&);
|
||||
|
||||
|
||||
|
||||
public:
|
||||
template<class ALO>
|
||||
JobTicket (ExitNode const& exitNode, ALO& allocator)
|
||||
|
|
@ -133,31 +136,6 @@ using lib::LUID;
|
|||
static JobTicket NOP;
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
class ExplorationState;
|
||||
friend class ExplorationState;
|
||||
ExplorationState startExploration() const; ////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
ExplorationState discoverPrerequisites (uint channelNr =0) const; ////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
Job createJobFor (Time nominalTime);
|
||||
|
||||
Duration getExpectedRuntime();
|
||||
|
||||
auto
|
||||
getPrerequisites ()
|
||||
{
|
||||
return lib::transformIterator (this->empty()? Prerequisites::iterator()
|
||||
: provision_.prerequisites.begin()
|
||||
,[](Prerequisite& prq) -> JobTicket&
|
||||
{
|
||||
return prq.prereqTicket;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
|
|
@ -176,6 +154,34 @@ using lib::LUID;
|
|||
,[](auto& pq){ return pq.prereqTicket.isValid(); });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Core operation: iterate over the prerequisites,
|
||||
* required to carry out a render operation based on this blueprint
|
||||
* @return iterator exposing the prerequisites as JobTicket&
|
||||
*/
|
||||
auto
|
||||
getPrerequisites ()
|
||||
{
|
||||
return lib::transformIterator (this->empty()? Prerequisites::iterator()
|
||||
: provision_.prerequisites.begin()
|
||||
,[](Prerequisite& prq) -> JobTicket&
|
||||
{
|
||||
return prq.prereqTicket;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Core operation: build a concrete render job based on this blueprint
|
||||
*/
|
||||
Job createJobFor (Time nominalTime);
|
||||
|
||||
/**
|
||||
* Core operation: guess expected runtime for rendering
|
||||
*/
|
||||
Duration getExpectedRuntime();
|
||||
|
||||
|
||||
protected:
|
||||
static InvocationInstanceID timeHash (Time, InvocationInstanceID const&);
|
||||
bool verifyInstance (JobFunctor&, InvocationInstanceID const&, Time) const;
|
||||
|
|
@ -183,104 +189,6 @@ using lib::LUID;
|
|||
};
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
class JobTicket::ExplorationState
|
||||
{
|
||||
typedef Prerequisites::iterator SubTicketSeq;
|
||||
typedef std::stack<SubTicketSeq> SubTicketStack; //////////////////////////TODO use a custom container to avoid heap allocations
|
||||
|
||||
SubTicketStack toExplore_;
|
||||
OrientationIndicator orientation_;
|
||||
|
||||
public:
|
||||
ExplorationState() { }
|
||||
|
||||
ExplorationState (Prerequisites& prerequisites)
|
||||
{
|
||||
if (not isnil (prerequisites))
|
||||
toExplore_.push (prerequisites.begin());
|
||||
}
|
||||
|
||||
// using default copy operations
|
||||
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return toExplore_.empty();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
markTreeLocation()
|
||||
{
|
||||
orientation_.markRefLevel (toExplore_.size());
|
||||
orientation_.resetToRef();
|
||||
ENSURE (0 == orientation_);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
pullNext()
|
||||
{
|
||||
if (empty())
|
||||
throw lumiera::error::Logic ("Exploration of Job prerequisites floundered. "
|
||||
"Attempt to iterate beyond the end of prerequisite list"
|
||||
,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
|
||||
ASSERT (toExplore_.top().isValid());
|
||||
|
||||
++(toExplore_.top());
|
||||
while ( !toExplore_.empty()
|
||||
&& toExplore_.top().empty())
|
||||
toExplore_.pop();
|
||||
|
||||
ENSURE (empty() || toExplore_.top().isValid());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
push (ExplorationState subExploration) // note: passing deliberately by value
|
||||
{
|
||||
if (subExploration.empty()) return;
|
||||
|
||||
pushAllPrerequisites (subExploration.toExplore_);
|
||||
}
|
||||
|
||||
|
||||
JobTicket const *
|
||||
operator->() const
|
||||
{
|
||||
REQUIRE (!empty() && toExplore_.top().isValid());
|
||||
REQUIRE (toExplore_.top()->prereqTicket.isValid());
|
||||
|
||||
return & toExplore_.top()->prereqTicket;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
void
|
||||
pushAllPrerequisites (SubTicketStack& furtherPrerequisites)
|
||||
{
|
||||
REQUIRE (!isnil (furtherPrerequisites));
|
||||
|
||||
if (1 == furtherPrerequisites.size())
|
||||
{
|
||||
this->toExplore_.push (furtherPrerequisites.top());
|
||||
}
|
||||
else
|
||||
{ // pathological case: several levels of prerequisites
|
||||
// --> push recursively to retain level ordering
|
||||
SubTicketSeq deepestLevel (furtherPrerequisites.top());
|
||||
furtherPrerequisites.pop();
|
||||
pushAllPrerequisites (furtherPrerequisites);
|
||||
this->toExplore_.push (deepestLevel);
|
||||
}
|
||||
}
|
||||
};
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -314,33 +222,6 @@ using lib::LUID;
|
|||
}
|
||||
|
||||
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
/// @deprecated : could be expendable ... likely incurred solely by the use of Monads as design pattern
|
||||
inline JobTicket::ExplorationState
|
||||
JobTicket::startExploration() const
|
||||
{
|
||||
UNIMPLEMENTED ("somehow build a self-referential pseudo-prerequisite, and seed an ExplorationState with that");
|
||||
/////////////////////TODO problem is: we need a JobTicket::Prerequisite instance, where the descriptor points to "self" (this JobTicket)
|
||||
/////////////////////TODO : but this instance needs to reside somewhere at a safe location, since we want to embed a LinkedElements-iterator
|
||||
/////////////////////TODO : into the ExplorationState. And obviously we do not want that instance in each JobTicket, only in the top level ones
|
||||
|
||||
/////////////////////TODO : on second thought -- better have a top-level entry point to the evaluation of a frame
|
||||
/////////////////////TODO basically this inherits from Prerequisite and lives somehow in the dispatcher-table or segment
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline JobTicket::ExplorationState
|
||||
JobTicket::discoverPrerequisites (uint channelNr) const
|
||||
{
|
||||
return empty()? ExplorationState()
|
||||
: ExplorationState (util::unConst(provision_).prerequisites);
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 :: to be refactored...
|
||||
|
||||
|
||||
}} // namespace steam::engine
|
||||
#endif
|
||||
}}// namespace steam::engine
|
||||
#endif /*STEAM_ENGINE_JOB_TICKET_H*/
|
||||
|
|
|
|||
|
|
@ -28,11 +28,8 @@
|
|||
|
||||
|
||||
#include "steam/engine/render-drive.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/engine/job-ticket.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
//#include "lib/frameid.hpp"
|
||||
//#include "steam/state.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
|
|
@ -102,16 +99,14 @@ namespace engine {
|
|||
void
|
||||
RenderDrive::performJobPlanningChunk(FrameCnt nextStartFrame)
|
||||
{
|
||||
TimeAnchor refPoint(getTimings(), nextStartFrame);
|
||||
UNIMPLEMENTED ("the actual meat: advance the render process");
|
||||
}
|
||||
|
||||
|
||||
Job
|
||||
RenderDrive::buildFollowUpJobFrom (TimeAnchor const& refPoint)
|
||||
RenderDrive::buildFollowUpJobFrom (Time refPoint)
|
||||
{
|
||||
return this->prepareRenderPlanningFrom(
|
||||
refPoint.getNextAnchorPoint());
|
||||
UNIMPLEMENTED ("create a follow-up job to pick up job-planning at or after the refPoint");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
|
||||
#include "steam/common.hpp"
|
||||
#include "steam/mobject/model-port.hpp"
|
||||
#include "steam/engine/time-anchor.hpp"
|
||||
#include "steam/engine/dispatcher.hpp"
|
||||
#include "steam/play/timings.hpp"
|
||||
#include "vault/engine/job.h"
|
||||
|
|
@ -147,7 +146,7 @@ namespace engine {
|
|||
|
||||
private:
|
||||
void performJobPlanningChunk(FrameCnt nextStartFrame);
|
||||
Job buildFollowUpJobFrom (TimeAnchor const& refPoint);
|
||||
Job buildFollowUpJobFrom (Time refPoint);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
TIME-ANCHOR.hpp - current render evaluation time point closure
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file time-anchor.hpp
|
||||
** Representation of a _continuation point_ for planning the render process.
|
||||
** In the Lumiera engine, render and playback processes are modelled as infinite streams,
|
||||
** which are evaluated chunk wise. The TimeAnchor is used to mark a point, where the
|
||||
** _planning_ of further render jobs will be picked up and continued later on
|
||||
**
|
||||
** @todo this is part of an implementation draft from 2013,
|
||||
** to create a complete outline of player and render job generation.
|
||||
** @todo as of 2016 this effort is stalled, but remains valid
|
||||
**
|
||||
** @deprecated 5/23 during rework of Playback / Rendering the concept TimeAnchor lost its substance
|
||||
*/
|
||||
|
||||
|
||||
#ifndef STEAM_ENGINE_TIME_ANCHOR_H
|
||||
#define STEAM_ENGINE_TIME_ANCHOR_H
|
||||
|
||||
#include "steam/common.hpp"
|
||||
#include "vault/real-clock.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "steam/play/timings.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
|
||||
|
||||
|
||||
namespace steam {
|
||||
namespace engine {
|
||||
|
||||
using vault::RealClock;
|
||||
using lib::time::Offset;
|
||||
using lib::time::Duration;
|
||||
using lib::time::FrameCnt;
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::Time;
|
||||
|
||||
|
||||
/**
|
||||
* The process of playback or rendering is a continued series of exploration and evaluation.
|
||||
* The outline of what needs to be calculated is determined continuously, proceeding in
|
||||
* chunks of evaluation. Each of these continued partial evaluations establishes a distinct
|
||||
* anchor or breaking point in time: everything before this point can be considered settled
|
||||
* and planned thus far. Effectively, this time point acts as a <i>evaluation closure</i>,
|
||||
* to be picked up for the next partial evaluation. Each time anchor defines a span of the
|
||||
* timeline, which will be covered with the next round of job planning; the successive next
|
||||
* TimeAnchor will be located at the first frame \em after this time span, resulting in
|
||||
* seamless coverage of the whole timeline. Whenever a TimeAnchor is created, a relation
|
||||
* between nominal time, current engine latency and wall clock time is established, This way,
|
||||
* the TimeAnchor closure is the definitive binding between the abstract logical time of the
|
||||
* session timeline, and the real wall-clock time forming the deadline for rendering.
|
||||
*
|
||||
* # internals
|
||||
* The time anchor associates a nominal time, defined on the implicit time grid
|
||||
* of some given Timings, with an actual wall clock time. Due to the usage situation,
|
||||
* the TimeAnchor takes on the secondary meaning of a breaking point; everything \em before
|
||||
* this anchor point has been handled during the preceding invocations of an ongoing chunk wise
|
||||
* partial evaluation of the timeline to be "performed" within this play process.
|
||||
* - the #timings_ serve as an abstracted grid (actually, the implementation
|
||||
* does refer to a grid defined somewhere within the session)
|
||||
* - the actual #anchorPoint_ is defined as frame number relative to this grid
|
||||
* - this anchor point is scheduled to happen at a #relatedRealTime_, based on
|
||||
* system's real time clock scale (typically milliseconds since 1970).
|
||||
* This schedule contains a compensation for engine and output latency.
|
||||
*
|
||||
* @remarks please note that time anchors are set per CalcStream.
|
||||
* Since different streams might use different frame grids, the rhythm
|
||||
* of these planning operations is likely to be specific for a given stream.
|
||||
* The relation to real time is established anew at each time anchor, so any
|
||||
* adjustments to the engine latency will be reflected in the planned job's
|
||||
* deadlines. Actually, the embedded Timings record is responsible for this
|
||||
* timing calculation and for fetching the current EngineConfig.
|
||||
*
|
||||
* @deprecated 5/23 during rework of Playback / Rendering it turns out that TimeAnchor
|
||||
* is completely redundant and void of substantial meaning
|
||||
*
|
||||
* @see Dispatcher
|
||||
* @see DispatcherInterface_test
|
||||
* @see Timings
|
||||
*/
|
||||
class TimeAnchor
|
||||
{
|
||||
play::Timings timings_;
|
||||
FrameCnt anchorPoint_;
|
||||
Time relatedRealTime_;
|
||||
|
||||
static Time
|
||||
expectedTimeofArival (play::Timings const& timings, FrameCnt startFrame, Offset startDelay)
|
||||
{
|
||||
TimeVar deadline = timings.isTimebound()? timings.getTimeDue(startFrame)
|
||||
: RealClock::now();
|
||||
return deadline + startDelay;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
TimeAnchor (play::Timings timings, FrameCnt startFrame, Offset startDelay =Offset::ZERO)
|
||||
: timings_(timings)
|
||||
, anchorPoint_(startFrame)
|
||||
, relatedRealTime_(expectedTimeofArival(timings,startFrame,startDelay))
|
||||
{ }
|
||||
|
||||
// using default copy operations
|
||||
|
||||
|
||||
|
||||
/** set a follow-up TimeAnchor point.
|
||||
* After planning a chunk of jobs, the dispatcher uses
|
||||
* this function to set up a new breaking point (TimeAnchor)
|
||||
* and places a continuation job to resume the planning activity.
|
||||
* @note precisely satisfies the <i>planning chunk duration</i>
|
||||
* @return a frame number suitable to build the next TimeAnchor
|
||||
* based on the current play::Timings. This new start point
|
||||
* will be anchored at the grid point following the end of
|
||||
* the previous planning chunk, resulting in a seamless
|
||||
* coverage of the timeline
|
||||
*/
|
||||
FrameCnt
|
||||
getNextAnchorPoint() const
|
||||
{
|
||||
return timings_.establishNextPlanningChunkStart (this->anchorPoint_);
|
||||
}
|
||||
|
||||
|
||||
/** @internal for debugging and diagnostics:
|
||||
* explicitly cast this TimeAnchor onto the underlying
|
||||
* nominal time scale (as defined by the Timings of this
|
||||
* playback or render process). */
|
||||
operator lib::time::TimeValue() const
|
||||
{
|
||||
return timings_.getFrameStartAt (anchorPoint_);
|
||||
}
|
||||
|
||||
|
||||
/** @return the frame at which any job planning
|
||||
* for this planning chunk will start */
|
||||
FrameCnt getStartFrame() const
|
||||
{
|
||||
return anchorPoint_;
|
||||
}
|
||||
|
||||
|
||||
/** define the deadline for a grid point relative to this reference point.
|
||||
* Since a TimeAnchor represents the definitive link between nominal time
|
||||
* and ongoing wall clock time, and since all of the current output stream
|
||||
* related timing information is available -- including the engine and the
|
||||
* output latency -- this is the place to do the final decision.
|
||||
* @param frameOffset frame count offset relative to this TimeAnchor point
|
||||
* @return the latest real absolute wall clock time at which this frame
|
||||
* has to be delivered to the OutputSlot. This deadline is exclusive,
|
||||
* i.e. time < deadline is required.
|
||||
*/
|
||||
Time
|
||||
establishDeadlineFor (FrameCnt frameOffset)
|
||||
{
|
||||
return this->relatedRealTime_
|
||||
+ timings_.getRealOffset(frameOffset);
|
||||
}
|
||||
|
||||
|
||||
/** convenience shortcut, employing the deadline calculation in relation
|
||||
* to current wall clock time */
|
||||
Offset
|
||||
remainingRealTimeFor (FrameCoord plannedFrame)
|
||||
{
|
||||
FrameCnt frameOffset = plannedFrame.absoluteFrameNumber - anchorPoint_;
|
||||
return Offset(RealClock::now()
|
||||
,establishDeadlineFor(frameOffset));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace steam::engine
|
||||
#endif
|
||||
|
|
@ -419,11 +419,6 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "Hierarchy rebuilding" HierarchyOrientationIndicator_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "IOS restore format" IosSavepoint_test <<END
|
||||
out-lit: 0x2a
|
||||
out-lit: 42
|
||||
|
|
|
|||
|
|
@ -76,5 +76,5 @@ PLANNED "scheduler interface" SchedulerInterface_test <<END
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Step timing basics" Timings_test <<END
|
||||
TEST "Step timing basics" Timings_test <<END
|
||||
END
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ namespace test {
|
|||
* locate here since this is a dedicated translation unit
|
||||
* @return `true` iff the job was defined in the typical way used by
|
||||
* JobTicket to generate fill jobs for empty segments.
|
||||
* @see JobTicket::JobTicket::createJobFor(FrameCoord)
|
||||
* @see JobTicket::JobTicket::createJobFor(nominalTime)
|
||||
*/
|
||||
bool
|
||||
MockJob::isNopJob (Job const& job)
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ namespace test {
|
|||
* and is able to probe some otherwise opaque internals of JobTicket.
|
||||
* Beyond that, MockJobTicket has the same storage size; and behaves
|
||||
* like the regular JobTicket after construction -- but any Job
|
||||
* created by JobTicket::createJobFor(FrameCoord) will be wired
|
||||
* created by JobTicket::createJobFor(nominalTime) will be wired
|
||||
* with the MockJob functor and can thus be related back to
|
||||
* the test specification setup.
|
||||
* @see JobPlanningPipeline_test
|
||||
|
|
@ -348,20 +348,6 @@ namespace test {
|
|||
public:
|
||||
/* == mock implementation of the Dispatcher interface == */
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
FrameCoord
|
||||
locateRelative (FrameCoord const&, FrameCnt frameOffset) override
|
||||
{
|
||||
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
|
||||
}
|
||||
|
||||
bool
|
||||
isEndOfChunk (FrameCnt, ModelPort port) override
|
||||
{
|
||||
UNIMPLEMENTED ("determine when to finish a planning chunk");
|
||||
}
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1301 : obsoleted by rework of Dispatcher-Pipeline
|
||||
|
||||
size_t
|
||||
resolveModelPort (ModelPort modelPort) override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
|
||||
/** @file scheduler-interface-test.cpp
|
||||
** unit test \ref SchedulerInterface_test
|
||||
**
|
||||
** @deprecated 6/2023 the Scheduler interface is about to be defined anew,
|
||||
** and will likely be totally different than envisioned here...
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -144,7 +147,8 @@ namespace test {
|
|||
* driven by the exploration of a tree-like structure (the JobTicket).
|
||||
* For the purpose of this interface demonstration test this recursive
|
||||
* invocation structure is just emulated by a simple tail recursion.
|
||||
* @see HierarchyOrientationIndicator_test#demonstrate_tree_rebuilding
|
||||
* @deprecated 6/2023 STOP we do it quite different now, and better,
|
||||
* in the Job-Planning-Pipeline
|
||||
*/
|
||||
void
|
||||
demonstrate_nested_job_specification (SchedulerFrontend& scheduler)
|
||||
|
|
|
|||
|
|
@ -65,9 +65,6 @@ namespace test {
|
|||
|
||||
const uint START_FRAME(10);
|
||||
|
||||
const uint DUMMY_CHANNEL(0); /////////////////////////////////////////////////////////////TICKET #1297 : get rid of the channels (use different ModelPort)
|
||||
|
||||
|
||||
|
||||
|
||||
FSecs
|
||||
|
|
@ -78,11 +75,6 @@ namespace test {
|
|||
|
||||
} // (End) Test fixture
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -115,8 +107,6 @@ namespace test {
|
|||
ENSURE (START_FRAME == 10);
|
||||
CHECK (timings.getFrameStartAt(START_FRAME) == Time::ZERO + Duration(10, FrameRate::PAL));
|
||||
CHECK (timings.getFrameStartAt(START_FRAME+1) == Time::ZERO + Duration(11, FrameRate::PAL));
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1301
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#include "lib/test/run.hpp"
|
||||
#include "steam/fixture/node-graph-attachment.hpp"
|
||||
#include "steam/engine/mock-dispatcher.hpp"
|
||||
#include "steam/engine/frame-coord.hpp"
|
||||
#include "steam/engine/exit-node.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,416 +0,0 @@
|
|||
/*
|
||||
HierarchyOrientationIndicator(Test) - mechanism for reproducing a tree (of jobs)
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2013, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
/** @file hierarchy-orientation-indicator-test.cpp
|
||||
** unit test \ref HierarchyOrientationIndicator_test
|
||||
*/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
|
||||
#include "lib/hierarchy-orientation-indicator.hpp"
|
||||
#include "lib/iter-adapter-stl.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/itertools.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/operators.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
namespace { // test fixture: a random Tree to navigate...
|
||||
|
||||
using std::rand;
|
||||
using std::function;
|
||||
using lib::transformIterator;
|
||||
using lib::iter_stl::eachAddress;
|
||||
using util::contains;
|
||||
using util::max;
|
||||
|
||||
/* -- size of the test tree ---- */
|
||||
const uint MAX_CHILDREN_CNT(5); // children per Node (5 means 0 to 4 children)
|
||||
const double CHILD_PROBABILITY(0.45); // probability for a Node to have any children
|
||||
const uint TEST_SEQUENCE_LENGTH(50); // test uses a sequence of Node trees
|
||||
// 5 - 45% - 50 produce roughly 1000 Nodes and tree depths of about 12
|
||||
uint nextChildID(1);
|
||||
|
||||
|
||||
/**
|
||||
* pick a random child count below #MAX_CHILDREN_CNT
|
||||
* with a probability to get any count above zero
|
||||
* as defined by CHILD_PROBABILITY
|
||||
*/
|
||||
inline uint
|
||||
pick_random_count()
|
||||
{
|
||||
uint bottom((1.0/CHILD_PROBABILITY - 1) * MAX_CHILDREN_CNT);
|
||||
uint limit = bottom + MAX_CHILDREN_CNT;
|
||||
ASSERT (0 < bottom);
|
||||
ASSERT (bottom < limit);
|
||||
|
||||
int cnt = (rand() % limit) - bottom;
|
||||
return max(0, cnt);
|
||||
}
|
||||
|
||||
/** (sub)tree of test data */
|
||||
struct Node
|
||||
: boost::equality_comparable<Node>
|
||||
{
|
||||
typedef std::vector<Node> Children;
|
||||
|
||||
int id_;
|
||||
Children children_;
|
||||
|
||||
|
||||
Node(int i) ///< build node explicitly without children
|
||||
: id_(i)
|
||||
{ }
|
||||
|
||||
|
||||
Node() ///< build a random test subtree
|
||||
: id_(nextChildID++)
|
||||
{
|
||||
uint c = pick_random_count();
|
||||
for (uint j=0; j<c; ++j) // populate with c random children
|
||||
children_.push_back(Node());
|
||||
}
|
||||
|
||||
Node const&
|
||||
child (uint i) const
|
||||
{
|
||||
REQUIRE (i < children_.size());
|
||||
return children_[i];
|
||||
}
|
||||
|
||||
bool
|
||||
hasChild (Node const& o)
|
||||
{
|
||||
return util::contains (children_, o);
|
||||
}
|
||||
|
||||
Node&
|
||||
makeChild (int childID)
|
||||
{
|
||||
children_.push_back (Node(childID));
|
||||
return children_.back();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline bool
|
||||
have_equivalent_children (Node const& l, Node const& r)
|
||||
{
|
||||
if (l.children_.size() != r.children_.size()) return false;
|
||||
for (uint i=0; i<l.children_.size(); ++i)
|
||||
if (l.child(i) != r.child(i)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator== (Node const& l, Node const& r)
|
||||
{
|
||||
return l.id_ == r.id_
|
||||
&& have_equivalent_children(l,r);
|
||||
}
|
||||
|
||||
|
||||
typedef lib::IterQueue<Node*> NodeSeq;
|
||||
|
||||
|
||||
/**
|
||||
* Function to generate a depth-first tree visitation
|
||||
*/
|
||||
NodeSeq
|
||||
exploreChildren (Node* node)
|
||||
{
|
||||
NodeSeq children_to_visit;
|
||||
build(children_to_visit).usingSequence (eachAddress (node->children_));
|
||||
return children_to_visit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct VisitationData
|
||||
{
|
||||
int id;
|
||||
int orientation;
|
||||
|
||||
VisitationData(int refID,
|
||||
int direction =0)
|
||||
: id(refID)
|
||||
, orientation(direction)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* This functor visits the nodes to produce the actual test data.
|
||||
* The intention is to describe a visitation path through a tree structure
|
||||
* by a sequence of "up", "down", and "level" orientations. The test we're
|
||||
* preparing here will attempt to re-create a given tree based on these
|
||||
* directional information. The actual visitation path is created by
|
||||
* a depth-first exploration of the source tree.
|
||||
*/
|
||||
class NodeVisitor
|
||||
{
|
||||
typedef std::deque<Node*> NodePath;
|
||||
typedef NodePath::reverse_iterator PathIter;
|
||||
|
||||
NodePath path_;
|
||||
|
||||
public:
|
||||
// using default ctor and copy operations
|
||||
|
||||
static function<VisitationData(Node*)>
|
||||
create () { return NodeVisitor(); }
|
||||
|
||||
|
||||
VisitationData
|
||||
operator() (Node* node)
|
||||
{
|
||||
int direction = establishRelation (node);
|
||||
return VisitationData(node->id_, direction);
|
||||
}
|
||||
|
||||
private:
|
||||
/** Helper for this test only: find out about the hierarchical relation.
|
||||
* In the real usage situation, the key point is that we \em record
|
||||
* this relation on-the-fly, when visiting the tree, instead of
|
||||
* determining it after the fact. */
|
||||
int
|
||||
establishRelation (Node* nextNode)
|
||||
{
|
||||
REQUIRE (nextNode);
|
||||
uint level = path_.size();
|
||||
uint refLevel = level;
|
||||
for (PathIter p = path_.rbegin();
|
||||
0 < level ; --level, ++p )
|
||||
{
|
||||
Node* parent = *p;
|
||||
if (parent->hasChild (*nextNode))
|
||||
{
|
||||
// visitation continues with children below this level
|
||||
path_.resize(level);
|
||||
path_.push_back(nextNode);
|
||||
return (level - refLevel) + 1;
|
||||
}
|
||||
}
|
||||
ASSERT (0 == level);
|
||||
|
||||
// nextNode not found as child (i.e. fork) within current tree path
|
||||
// --> start new tree path at root
|
||||
path_.clear();
|
||||
path_.push_back(nextNode);
|
||||
return (0 - refLevel) + 1;
|
||||
} // by convention, root is an implicitly pre-existing context at level 0
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* the core of this test: rebuilding a tree
|
||||
* based on visitation data, including the \em orientation
|
||||
* of the visitation path (up, down, siblings). After construction,
|
||||
* the embedded #children_ will reflect the original sequence as
|
||||
* described by the given treeTraversal.
|
||||
* @remarks this is a blueprint for the scheduler interface,
|
||||
* which accepts a sequence of jobs with dependencies.
|
||||
*/
|
||||
struct TreeRebuilder
|
||||
: Node
|
||||
{
|
||||
template<class IT>
|
||||
TreeRebuilder (IT treeTraversal)
|
||||
: Node(0)
|
||||
{
|
||||
populate (transformIterator (treeTraversal,
|
||||
NodeVisitor::create()));
|
||||
}
|
||||
|
||||
private:
|
||||
template<class IT>
|
||||
void
|
||||
populate (IT treeVisitation)
|
||||
{
|
||||
struct Builder
|
||||
{
|
||||
Builder (Node& startPoint)
|
||||
: parent_(NULL)
|
||||
, current_(&startPoint)
|
||||
{ }
|
||||
|
||||
void
|
||||
populateBy (IT& treeVisitation)
|
||||
{
|
||||
while (treeVisitation)
|
||||
{
|
||||
int direction = treeVisitation->orientation;
|
||||
if (direction < 0)
|
||||
{
|
||||
treeVisitation->orientation += 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
if (direction > 0)
|
||||
{
|
||||
treeVisitation->orientation -= 1;
|
||||
Node* refPoint = startChildTransaction();
|
||||
populateBy (treeVisitation);
|
||||
commitChildTransaction(refPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
addNode (treeVisitation->id);
|
||||
++treeVisitation;
|
||||
}}}
|
||||
|
||||
private:
|
||||
Node* parent_;
|
||||
Node* current_;
|
||||
|
||||
void
|
||||
addNode (int id)
|
||||
{
|
||||
current_ = & parent_->makeChild(id);
|
||||
}
|
||||
|
||||
Node*
|
||||
startChildTransaction()
|
||||
{
|
||||
Node* oldRefPoint = parent_;
|
||||
ASSERT (current_);
|
||||
parent_ = current_; // set new ref point
|
||||
return oldRefPoint;
|
||||
}
|
||||
|
||||
void
|
||||
commitChildTransaction(Node* refPoint)
|
||||
{
|
||||
parent_ = refPoint;
|
||||
current_ = parent_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Builder builder(*this); // pre-existing implicit root context
|
||||
builder.populateBy (treeVisitation);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} //(End) test fixture
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************************************************************//**
|
||||
* @test describing and rebuilding a tree structure
|
||||
* while visiting the tree in depth first order.
|
||||
* To keep track of the level changes during that navigation,
|
||||
* we use an indicator to represent the relative level difference
|
||||
* compared to the previously visited node in the tree.
|
||||
*
|
||||
* @see HierarchyOrientationIndicator
|
||||
* @see DispatcherInterface_test
|
||||
*/
|
||||
class HierarchyOrientationIndicator_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
demonstrate_tree_rebuilding();
|
||||
verify_OrientationIndicator();
|
||||
}
|
||||
|
||||
/** @test demonstrate how a Node tree structure can be rebuilt
|
||||
* just based on the visitation sequence of an original tree.
|
||||
* This visitation captures the local data of the Node (here the ID)
|
||||
* and the orientation of the visitation path (down, next sibling, up)
|
||||
*
|
||||
* This is a demonstration and blueprint for constructing the scheduler interface.
|
||||
* The Scheduler accepts a series of new jobs, but jobs may depend on each other,
|
||||
* and the jobs are created while exploring the dependencies in the render engine's
|
||||
* node graph (low-level-model).
|
||||
*/
|
||||
void
|
||||
demonstrate_tree_rebuilding ()
|
||||
{
|
||||
Node::Children testWood;
|
||||
for (uint i=0; i < TEST_SEQUENCE_LENGTH; ++i)
|
||||
testWood.push_back(Node());
|
||||
|
||||
TreeRebuilder reconstructed (depthFirst (eachAddress(testWood)) >>= exploreChildren);
|
||||
|
||||
CHECK (reconstructed.children_ == testWood);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_OrientationIndicator ()
|
||||
{
|
||||
OrientationIndicator orient;
|
||||
|
||||
CHECK (0 == orient);
|
||||
++++orient;
|
||||
CHECK (+2 == orient);
|
||||
orient.markRefLevel();
|
||||
CHECK ( 0 == orient);
|
||||
--orient;
|
||||
CHECK (-1 == orient);
|
||||
|
||||
orient.markRefLevel (4);
|
||||
CHECK (-3 == orient);
|
||||
|
||||
orient.markRefLevel (4);
|
||||
CHECK (-3 == orient);
|
||||
|
||||
orient -= orient;
|
||||
CHECK ( 0 == orient);
|
||||
|
||||
++orient;
|
||||
CHECK (+1 == orient);
|
||||
orient.resetToRef(); // now at level == 4
|
||||
orient.markRefLevel (7);
|
||||
CHECK (-3 == orient);
|
||||
|
||||
orient += 200;
|
||||
orient -= 190;
|
||||
CHECK (+7 == orient);
|
||||
|
||||
OrientationIndicator o2(orient);
|
||||
o2.markRefLevel(16);
|
||||
CHECK (-2 == o2);
|
||||
CHECK (+7 == orient);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER(HierarchyOrientationIndicator_test, "unit common");
|
||||
|
||||
}} // namespace lib::test
|
||||
|
|
@ -1,582 +0,0 @@
|
|||
/*
|
||||
IterExplorer(Test) - verify evaluation patterns built using iterators
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
/** @file iter-explorer-test.cpp
|
||||
** The \ref IterExplorer_test covers and demonstrates several usage scenarios and
|
||||
** extensions built on top of the \ref lib::IterExplorer template. These introduce some
|
||||
** elements from Functional Programming, especially the _Monad Pattern_ to encapsulate
|
||||
** and isolate intricacies of evolving embedded state. At usage site, only a _state
|
||||
** transition function_ need to be provided, thereby focusing at the problem domain
|
||||
** and thus reducing complexity.
|
||||
**
|
||||
** The setup for this test relies on a demonstration example of encapsulated state:
|
||||
** a counter with start and end value, embedded into an iterator. Basically, this
|
||||
** running counter, when iterated, generates a sequence of numbers start ... end.
|
||||
** So -- conceptually -- this counting iterator can be thought to represent this
|
||||
** sequence of numbers. Note that this is a kind of abstract or conceptual
|
||||
** representation, not a factual representation of the sequence in memory.
|
||||
** The whole point is _not to represent_ this sequence in runtime state at once,
|
||||
** rather to pull and expand it on demand. Thus, all the examples demonstrate in
|
||||
** this case "build" on this sequence, they expand it into various tree-like
|
||||
** structures, without actually performing these structural operations in memory.
|
||||
**
|
||||
** All these tests work by first defining these _functional structures_, which just
|
||||
** yields an iterator entity. We get the whole structure it conceptually defines
|
||||
** only if we "pull" this iterator until exhaustion -- which is precisely what
|
||||
** the test does to verify proper operation. Real world code of course would
|
||||
** just not proceed in this way, like pulling everything from such an iterator.
|
||||
** Often, the very reason we're using such a setup is the ability to represent
|
||||
** infinite structures. Like e.g. the evaluation graph of video passed through
|
||||
** a complex processing pipeline.
|
||||
**
|
||||
** @todo as of 2017, this framework is deemed incomplete and requires more design work. ////////////////////TICKET #1116
|
||||
** @deprecated Monads considered harmful -- as of 4/2023 this framework is about to be abandoned
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/iter-adapter-stl.hpp"
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/format-util.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
using ::Test;
|
||||
using util::isnil;
|
||||
using util::isSameObject;
|
||||
using lib::iter_stl::eachElm;
|
||||
using lib::iter_explorer::ChainedIters;
|
||||
using lumiera::error::LERR_(ITER_EXHAUST);
|
||||
using std::string;
|
||||
|
||||
|
||||
namespace { // test substrate: simple number sequence iterator
|
||||
|
||||
/**
|
||||
* This iteration _"state core" type_ describes
|
||||
* a sequence of numbers yet to be delivered.
|
||||
*/
|
||||
class State
|
||||
{
|
||||
uint p,e;
|
||||
|
||||
public:
|
||||
State(uint start, uint end)
|
||||
: p(start)
|
||||
, e(end)
|
||||
{ }
|
||||
|
||||
bool
|
||||
checkPoint () const
|
||||
{
|
||||
return p < e;
|
||||
}
|
||||
|
||||
uint&
|
||||
yield () const
|
||||
{
|
||||
return util::unConst (checkPoint()? p : e);
|
||||
}
|
||||
|
||||
void
|
||||
iterNext ()
|
||||
{
|
||||
if (not checkPoint()) return;
|
||||
++p;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A straight ascending number sequence as basic test iterator.
|
||||
* The tests will dress up this source sequence in various ways.
|
||||
*/
|
||||
class NumberSequence
|
||||
: public IterStateWrapper<uint, State>
|
||||
{
|
||||
|
||||
public:
|
||||
explicit
|
||||
NumberSequence(uint end = 0)
|
||||
: IterStateWrapper<uint,State> (State(0,end))
|
||||
{ }
|
||||
NumberSequence(uint start, uint end)
|
||||
: IterStateWrapper<uint,State> (State(start,end))
|
||||
{ }
|
||||
|
||||
/** allow using NumberSequence in LinkedElements
|
||||
* (intrusive single linked list) */
|
||||
NumberSequence* next =nullptr;
|
||||
};
|
||||
|
||||
inline NumberSequence
|
||||
seq (uint end)
|
||||
{
|
||||
return NumberSequence(end);
|
||||
}
|
||||
|
||||
inline NumberSequence
|
||||
seq (uint start, uint end)
|
||||
{
|
||||
return NumberSequence(start, end);
|
||||
}
|
||||
|
||||
NumberSequence NIL_Sequence;
|
||||
|
||||
|
||||
/**
|
||||
* an arbitrary series of numbers
|
||||
* @note deliberately this is another type
|
||||
* and not equivalent to a NumberSequence,
|
||||
* while both do share the same value type
|
||||
*/
|
||||
typedef IterQueue<int> NumberSeries;
|
||||
|
||||
|
||||
/**
|
||||
* _"exploration function"_ to generate a functional datastructure.
|
||||
* Divide the given number by 5, 3 and 2, if possible. Repeatedly
|
||||
* applying this function yields a tree of decimation sequences,
|
||||
* each leading down to 1
|
||||
*/
|
||||
inline NumberSeries
|
||||
exploreChildren (uint node)
|
||||
{
|
||||
NumberSeries results;
|
||||
if (0 == node % 5 && node/5 > 0) results.feed (node/5);
|
||||
if (0 == node % 3 && node/3 > 0) results.feed (node/3);
|
||||
if (0 == node % 2 && node/2 > 0) results.feed (node/2);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
/** Diagnostic helper: "squeeze out" the given iterator
|
||||
* and join all the elements yielded into a string
|
||||
*/
|
||||
template<class II>
|
||||
inline string
|
||||
materialise (II&& ii)
|
||||
{
|
||||
return util::join (std::forward<II> (ii), "-");
|
||||
}
|
||||
|
||||
template<class II>
|
||||
inline void
|
||||
pullOut (II & ii)
|
||||
{
|
||||
while (ii)
|
||||
{
|
||||
cout << *ii;
|
||||
if (++ii) cout << "-";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
} // (END) test helpers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************//**
|
||||
* @test use a simple source iterator yielding numbers
|
||||
* to build various functional evaluation structures,
|
||||
* based on the \ref IterExplorer template.
|
||||
* - the [state adapter](\ref verifyStateAdapter() )
|
||||
* iterator construction pattern
|
||||
* - helper to [chain iterators](\ref verifyChainedIterators() )
|
||||
* - building [tree exploring structures](\ref verifyDepthFirstExploration())
|
||||
* - the [monadic nature](\ref verifyMonadOperator()) of IterExplorer
|
||||
* - a [recursively self-integrating](\ref verifyRecrusiveSelfIntegration())
|
||||
* evaluation pattern
|
||||
*
|
||||
* # Explanation
|
||||
*
|
||||
* Both this test and the IterExplorer template might be bewildering
|
||||
* and cryptic, unless you know the *Monad design pattern*. Monads are
|
||||
* heavily used in functional programming, actually they originate
|
||||
* from Category Theory. Basically, Monad is a pattern where we
|
||||
* combine several computation steps in a specific way; but instead
|
||||
* of intermingling the individual computation steps and their
|
||||
* combination, the goal is to isolate and separate the _mechanics
|
||||
* of combination_, so we can focus on the actual _computation steps:_
|
||||
* The mechanics of combination are embedded into the Monad type,
|
||||
* which acts as a kind of container, holding some entities
|
||||
* to be processed. The actual processing steps are then
|
||||
* fed to the monad as "function object" parameters.
|
||||
*
|
||||
* Using the monad pattern is well suited when both the mechanics of
|
||||
* combination and the individual computation steps tend to be complex.
|
||||
* In such a situation, it is beneficial to develop and test both
|
||||
* in isolation. The IterExplorer template applies this pattern
|
||||
* to the task of processing a source sequence. Typically we use
|
||||
* this in situations where we can't afford building elaborate
|
||||
* data structures in (global) memory, but rather strive at
|
||||
* doing everything on-the-fly. A typical example is the
|
||||
* processing of a variably sized data set without
|
||||
* using heap memory for intermediary results.
|
||||
*
|
||||
* @see IterExplorer
|
||||
* @see IterAdapter
|
||||
*/
|
||||
class IterExplorer_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
verifyStateAdapter();
|
||||
|
||||
verifyMonadOperator ();
|
||||
verifyChainedIterators();
|
||||
verifyRawChainedIterators();
|
||||
|
||||
verifyDepthFirstExploration();
|
||||
verifyBreadthFirstExploration();
|
||||
verifyRecursiveSelfIntegration();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test demonstrate the underlying solution approach of IterExplorer.
|
||||
* All of the following IterExplorer flavours are built on top of a
|
||||
* special iterator adapter, centred at the notion of an iterable state
|
||||
* element type. The actual iterator just embodies one element of this
|
||||
* state representation, and typically this element alone holds all the
|
||||
* relevant state and information. Essentially this means the iterator is
|
||||
* _self contained_. Contrast this to the more conventional approach of
|
||||
* iterator implementation, where the iterator entity actually maintains
|
||||
* a hidden back-link to some kind of container, which in turn is the one
|
||||
* in charge of the elements yielded by the iterator.
|
||||
*/
|
||||
void
|
||||
verifyStateAdapter ()
|
||||
{
|
||||
NumberSequence ii = seq(9);
|
||||
CHECK (!isnil (ii));
|
||||
CHECK (0 == *ii);
|
||||
++ii;
|
||||
CHECK (1 == *ii);
|
||||
pullOut(ii);
|
||||
CHECK ( isnil (ii));
|
||||
CHECK (!ii);
|
||||
|
||||
VERIFY_ERROR (ITER_EXHAUST, *ii );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++ii );
|
||||
|
||||
ii = seq(5);
|
||||
CHECK (materialise(ii) == "0-1-2-3-4");
|
||||
ii = seq(5,8);
|
||||
CHECK (materialise(ii) == "5-6-7");
|
||||
|
||||
ii = NIL_Sequence;
|
||||
CHECK ( isnil (ii));
|
||||
CHECK (!ii);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test verify a helper to chain a series of iterators into a "flat" result sequence.
|
||||
* This convenience helper is built using IterExplorer building blocks. The resulting
|
||||
* iterator _combines_ and _flattens_ a sequence of source iterators, resulting in a
|
||||
* simple sequence accessible as iterator again. Here we verify the convenience
|
||||
* implementation; this uses a STL container (actually std:deque) behind the scenes
|
||||
* to keep track of all added source iterators.
|
||||
*/
|
||||
void
|
||||
verifyChainedIterators ()
|
||||
{
|
||||
typedef ChainedIters<NumberSequence> Chain;
|
||||
|
||||
Chain ci = iterChain (seq(5),seq(7),seq(9));
|
||||
|
||||
CHECK (!isnil (ci));
|
||||
pullOut (ci);
|
||||
CHECK ( isnil (ci));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *ci );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++ci );
|
||||
|
||||
CHECK (isnil(Chain()));
|
||||
CHECK (!iterChain (NIL_Sequence));
|
||||
|
||||
// Iterator chaining "flattens" one level of packaging
|
||||
NumberSequence s9 = seq(9);
|
||||
ci = iterChain (s9);
|
||||
|
||||
for ( ; s9 && ci; ++s9, ++ci )
|
||||
CHECK (*s9 == *ci);
|
||||
|
||||
CHECK (isnil(s9));
|
||||
CHECK (isnil(ci));
|
||||
|
||||
// Note: Iterator chain is created based on (shallow) copy
|
||||
// of the source sequences. In case these have an independent
|
||||
// per-instance state (like e.g. NumberSequence used for this test),
|
||||
// then the created chain is independent from the source iterators.
|
||||
s9 = seq(9);
|
||||
ci = iterChain (s9);
|
||||
CHECK (0 == *s9);
|
||||
CHECK (0 == *ci);
|
||||
|
||||
pullOut (ci);
|
||||
CHECK (isnil(ci));
|
||||
CHECK (0 == *s9);
|
||||
pullOut (s9);
|
||||
CHECK (isnil(s9));
|
||||
}
|
||||
|
||||
|
||||
/** @test variation of the iterator chaining facility.
|
||||
* This is the "raw" version without any convenience shortcuts.
|
||||
* The source iterators are provided as iterator yielding other iterators.
|
||||
*/
|
||||
void
|
||||
verifyRawChainedIterators ()
|
||||
{
|
||||
typedef std::vector<NumberSequence> IterContainer;
|
||||
typedef RangeIter<IterContainer::iterator> IterIter;
|
||||
|
||||
typedef ChainedIters<IterIter> Chain;
|
||||
|
||||
NumberSequence s5 (1,5);
|
||||
NumberSequence s7 (5,8);
|
||||
NumberSequence s9 (8,10);
|
||||
|
||||
CHECK (1 == *s5);
|
||||
CHECK (5 == *s7);
|
||||
CHECK (8 == *s9);
|
||||
|
||||
IterContainer srcIters;
|
||||
srcIters.push_back (s5);
|
||||
srcIters.push_back (s7);
|
||||
srcIters.push_back (s9);
|
||||
|
||||
IterIter iti = eachElm(srcIters);
|
||||
CHECK (!isnil (iti));
|
||||
|
||||
// note: iterator has been copied
|
||||
CHECK ( isSameObject (srcIters[0], *iti));
|
||||
CHECK (!isSameObject (s5, *iti));
|
||||
|
||||
Chain chain(iti);
|
||||
CHECK (!isnil (iti));
|
||||
CHECK (1 == *chain);
|
||||
|
||||
++chain;
|
||||
CHECK (2 == *chain);
|
||||
|
||||
CHECK (1 == *s5); // unaffected of course...
|
||||
CHECK (5 == *s7);
|
||||
CHECK (8 == *s9);
|
||||
|
||||
++++chain;
|
||||
CHECK (4 == *chain);
|
||||
++chain;
|
||||
CHECK (5 == *chain); // switch over to contents of 2nd iterator
|
||||
++++++++chain;
|
||||
CHECK (9 == *chain);
|
||||
|
||||
++chain;
|
||||
CHECK (isnil(chain));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *chain );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++chain );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test a depth-first visiting and exploration scheme of a tree like system,
|
||||
* built on top of the IterExplorer monad.
|
||||
*
|
||||
* # Test data structure
|
||||
* We build a functional datastructure here, on the fly, while exploring it.
|
||||
* The `exploreChildren(m)` function generates this tree like datastructure:
|
||||
* For a given number, it tries to divide by 5, 3 and 2 respectively, possibly
|
||||
* generating multiple decimation sequences.
|
||||
*
|
||||
* If we start such a tree structure e.g. with a root node 30, this scheme yields:
|
||||
* \code
|
||||
* ( 30 )
|
||||
* ( 6 10 15 )
|
||||
* ( 2 3 2 5 3 5 )
|
||||
* ( 1 1 1 1 1 1 )
|
||||
* \endcode
|
||||
* This tree has no meaning in itself, beyond being an easy testbed for tree exploration schemes.
|
||||
*
|
||||
* # How the exploration works
|
||||
* We use a pre defined Template \ref DepthFirstExplorer, which is built on top of IterExplorer.
|
||||
* It contains the depth-first exploration strategy in a hardwired fashion. Actually this effect is
|
||||
* achieved by defining a specific way how to _combine the results of an exploration_ -- the latter
|
||||
* being the function which generates the data structure. To yield a depth-first exploration, all we
|
||||
* have to do is to delve down immediately into the children, right after visiting the node itself.
|
||||
*
|
||||
* Now, when creating such a DepthFirstExplorer by wrapping a given source iterator, the result is again
|
||||
* an iterator, but a specific iterator which at the same time is a Monad: It supports the `>>=` operation
|
||||
* (also known as _bind_ operator or _flatMap_ operator). This operator takes as second argument a function,
|
||||
* which in our case is the function to generate or explore the data structure.
|
||||
*
|
||||
* The result of applying this monadic `>>=` operation is a _transformed_ version of the source iterator,
|
||||
* i.e. it is again an iterator, which yields the results of the exploration function, combined together
|
||||
* in the order as defined by the built-in exploration strategy (here: depth first)
|
||||
*
|
||||
* @note technical detail: the result type of the exploration function (here `exploreChildren()`) determines
|
||||
* the iterator type used within IterExplorer and to drive the evaluation. The source sequence used to
|
||||
* seed the evaluation process can actually be any iterator yielding assignment compatible values: The
|
||||
* second example uses a NumberSequence with unsigned int values 0..6, while the actual expansion and
|
||||
* evaluation is based on NumberSeries using signed int values.
|
||||
*/
|
||||
void
|
||||
verifyDepthFirstExploration ()
|
||||
{
|
||||
NumberSeries root = elements(30);
|
||||
string explorationResult = materialise (depthFirst(root) >>= exploreChildren);
|
||||
CHECK (explorationResult == "30-6-2-1-3-1-10-2-1-5-1-15-3-1-5-1");
|
||||
|
||||
NumberSequence to7 = seq(7);
|
||||
explorationResult = materialise (depthFirst(to7) >>= exploreChildren);
|
||||
CHECK (explorationResult == "0-1-2-1-3-1-4-2-1-5-1-6-2-1-3-1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** @test a breadth-first visiting and exploration scheme of a tree like system,
|
||||
* built on top of the IterExplorer monad.
|
||||
* Here, an internal queue is used to explore the hierarchy in layers.
|
||||
* The (functional) datastructure is the same, just we're visiting it
|
||||
* in a different way here, namely in rows or layers.
|
||||
*/
|
||||
void
|
||||
verifyBreadthFirstExploration ()
|
||||
{
|
||||
NumberSeries root = elements(30);
|
||||
string explorationResult = materialise (breadthFirst(root) >>= exploreChildren);
|
||||
CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test verify a variation of recursive exploration, this time to rely
|
||||
* directly on the result set iterator type to provide the re-integration
|
||||
* of intermediary results. Since our `exploreChildren()` function returns
|
||||
* a NumberSeries, which basically is a IterQueue, the re-integration of expanded
|
||||
* elements will happen at the end, resulting in breadth-first visitation order --
|
||||
* but contrary to the dedicated `breadthFirst(..)` explorer, this expansion is done
|
||||
* separately for each element in the initial seed sequence. Note for example how the
|
||||
* expansion series for number 30, which is also generated in verifyBreadthFirstExploration(),
|
||||
* appears here at the end of the explorationResult sequence
|
||||
* @remarks this "combinator strategy" is really intended for use with custom sequences,
|
||||
* where the "Explorer" function works together with a specific implementation
|
||||
* and exploits knowledge about specifically tailored additional properties of
|
||||
* the input sequence elements, in order to yield the desired overall effect.
|
||||
* Actually this is what we use in the steam::engine::Dispatcher to generate
|
||||
* a series of frame render jobs, including all prerequisite jobs
|
||||
*/
|
||||
void
|
||||
verifyRecursiveSelfIntegration ()
|
||||
{
|
||||
typedef IterExplorer<iter_explorer::WrappedSequence<NumberSeries>
|
||||
,iter_explorer::RecursiveSelfIntegration> SelfIntegratingExploration;
|
||||
|
||||
NumberSeries root = elements(10,20,30);
|
||||
SelfIntegratingExploration exploration(root);
|
||||
string explorationResult = materialise (exploration >>= exploreChildren);
|
||||
CHECK (explorationResult == "10-2-5-1-1-20-4-10-2-2-5-1-1-1-30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test cover the basic monad bind operator,
|
||||
* which is used to build all the specialised Iterator flavours.
|
||||
* The default implementation ("Combinator strategy") just joins and flattens the result sequences
|
||||
* created by the functor bound into the monad. For this test, we use a functor `explode(top)`,
|
||||
* which returns the sequence 0...top.
|
||||
*/
|
||||
void
|
||||
verifyMonadOperator ()
|
||||
{
|
||||
auto explode = [](uint top) { return seq(0,top); };
|
||||
|
||||
// IterExplorer as such is an iterator wrapping the source sequence
|
||||
string result = materialise (exploreIter(seq(5)));
|
||||
CHECK (result == "0-1-2-3-4");
|
||||
|
||||
// now, if the source sequence yields exactly one element 5...
|
||||
result = materialise (exploreIter(seq(5,6)));
|
||||
CHECK (result == "5");
|
||||
|
||||
// then binding the explode()-Function yields just the result of invoking explode(5)
|
||||
result = materialise (exploreIter(seq(5,6)) >>= explode);
|
||||
CHECK (result == "0-1-2-3-4");
|
||||
|
||||
// binding anything into an empty sequence still results in an empty sequence
|
||||
result = materialise (exploreIter(seq(0)) >>= explode);
|
||||
CHECK (result == "");
|
||||
|
||||
// also, in case the bound function yields an empty sequence, the result remains empty
|
||||
result = materialise (exploreIter(seq(1)) >>= explode);
|
||||
CHECK (result == "");
|
||||
|
||||
// combining an empty sequence and the one element sequence (seq(0,1)) results in just one element
|
||||
result = materialise (exploreIter(seq(2)) >>= explode);
|
||||
CHECK (result == "0");
|
||||
|
||||
// multiple result sequences will be joined (flattened) into one sequence
|
||||
result = materialise (exploreIter(seq(5)) >>= explode);
|
||||
CHECK (result == "0-0-1-0-1-2-0-1-2-3");
|
||||
|
||||
// since the result is a monad, we can again bind yet another function
|
||||
result = materialise((exploreIter(seq(5)) >>= explode) >>= explode);
|
||||
CHECK (result == "0-0-0-1-0-0-1-0-1-2");
|
||||
|
||||
// Explanation:
|
||||
// 0 -> empty sequence, gets dropped
|
||||
// 1 -> 1-element sequence {0}
|
||||
// 2 -> {0,1}
|
||||
// 3 -> {0,1,2}
|
||||
|
||||
// Note: when cascading multiple >>= the parentheses are necessary, since in C++ unfortunately
|
||||
// the ">>=" associates to the right, while the proper monad bind operator should associate to the left
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
LAUNCHER (IterExplorer_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
|
|
@ -20,39 +20,32 @@
|
|||
|
||||
* *****************************************************/
|
||||
|
||||
/** @file iter-tree-explorer-test.cpp
|
||||
** The \ref IterTreeExplorer_test covers and demonstrates a generic mechanism
|
||||
** to expand and evaluate tree like structures. In its current shape (as of 2017),
|
||||
** it can be seen as an preliminary step towards retrofitting IterExplorer into
|
||||
** a framework of building blocks for tree expanding and backtracking evaluations.
|
||||
/** @file iter-explorer-test.cpp
|
||||
** The \ref IterExplorer_test covers and demonstrates a generic mechanism
|
||||
** to expand and evaluate tree like structures. It was created in response to
|
||||
** a recurring need for configurable tree expanding and backtracking evaluations.
|
||||
** Due to the nature of Lumiera's design, we repeatedly encounter this kind of
|
||||
** algorithms, when it comes to matching configuration and parametrisation against
|
||||
** a likewise hierarchical and rules based model. To keep the code base maintainable,
|
||||
** we deem it crucial to reduce the inherent complexity in such algorithms by clearly
|
||||
** separate the _mechanics of evaluation_ from the actual logic of the target domain.
|
||||
**
|
||||
** Similar to IterExplorer_test, this test relies on a demonstration setup featuring
|
||||
** a custom encapsulated state type: we rely on a counter with start and end value,
|
||||
** embedded into an iterator. Basically, this running counter, when iterated, generates
|
||||
** a descending sequence of numbers start ... end.
|
||||
** So -- conceptually -- this counting iterator can be thought to represent this
|
||||
** sequence of numbers. Note that this is a kind of abstract or conceptual
|
||||
** representation, not a factual representation of the sequence in memory.
|
||||
** The whole point is _not to represent_ this sequence in runtime state at once,
|
||||
** rather to pull and expand it on demand.
|
||||
** This test relies on a demonstration setup featuring a custom encapsulated state type:
|
||||
** we rely on a counter with start and end value, embedded into an iterator as »state core«.
|
||||
** This running counter, when iterated, generates a descending sequence of numbers start ... end.
|
||||
** So -- conceptually -- this counting iterator can be conceived as _representing_ this sequence
|
||||
** of numbers, while not actually representing all these numbers as data in memory. And this is
|
||||
** the whole point of the exercise: _not to represent_ this sequence in runtime state at once,
|
||||
** rather to __pull and expand it on demand._
|
||||
**
|
||||
** All these tests work by first defining these _functional structures_, which just
|
||||
** yields an iterator entity. We get the whole structure it conceptually defines
|
||||
** only if we "pull" this iterator until exhaustion -- which is precisely what
|
||||
** the test does to verify proper operation. Real world code of course would
|
||||
** just not proceed in this way, like pulling everything from such an iterator.
|
||||
** Often, the very reason we're using such a setup is the ability to represent
|
||||
** infinite structures. Like e.g. the evaluation graph of video passed through
|
||||
** a complex processing pipeline.
|
||||
**
|
||||
** @warning as of 4/2023 the alternative Monad-style iterator framework "iter-explorer" will be retracted
|
||||
** and replaced by this design here, which will then be renamed into IterExplorer //////////////////////////////TICKET #1276
|
||||
**
|
||||
** only if we »pull« and »materialise« this iterator until exhaustion — which essentially
|
||||
** is what the test does to verify proper operation. In contrast, _Real World Code_ of course
|
||||
** would not proceed in this way, like pulling everything from such an iterator. Since often
|
||||
** the very reason we're using such a setup is the ability to represent infinite structures.
|
||||
** Like e.g. the evaluation graph of video passed through a complex processing pipeline.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print previ
|
|||
</div>
|
||||
<!--POST-SHADOWAREA-->
|
||||
<div id="storeArea">
|
||||
<div title="AboutMonads" creator="Ichthyostega" modifier="Ichthyostega" created="201712101925" modified="202304132305" tags="Concepts discuss" changecount="72">
|
||||
<div title="AboutMonads" creator="Ichthyostega" modifier="Ichthyostega" created="201712101925" modified="202306221801" tags="Concepts discuss" changecount="74">
|
||||
<pre>//Monads are of questionable usefulness//
|
||||
|
||||
Monads are a concept and theoretical framework from category theory.
|
||||
|
|
@ -536,9 +536,7 @@ What remains is a set of clever technicalities. Such can be applied and executed
|
|||
When we care for complexity, we do so while we care for matters tangible to humans. Related to software, this is the need to maintain it, to adjust it, to adapt and remould it, to keep it relevant. At the bottom of all of these lies the need to understand software. And this Understanding mandates to use terms and notions, even patterns, which evoke meaning -- even to the uninitiated. How can „Monads“ even be helpful with that?
|
||||
|
||||
!To make sensible usage of Monads
|
||||
Foremost, they should be kept as what they are: technicalities. For the understanding, they must be subordinate to a real concept or pattern. One with the power to reorient our view of matters at hand.
|
||||
Thus we ask: what can be said about Monads?
|
||||
|
||||
Foremost, they should be kept as what they are: technicalities. For the understanding, they must be subordinate to a real concept or pattern. One with the power to reorient our view of matters at hand. Thus we ask: //what can be said about Monads?//
|
||||
!!!Formalism
|
||||
Regarding the formalism, it should be mentioned
|
||||
* that a Monad is a //type constructor// -- it takes a type parameter and generates a new instant of monadic kind.
|
||||
|
|
@ -4271,9 +4269,9 @@ Most calculations are not monolithic and can not be performed in-memory in a sin
|
|||
Pre-planning this chain of schedules is only necessary for {{{TIMEBOUND}}} playback -- in the other cases, for //best effort// or //background// computations, just the sequence of dependencies must be observed, starting the next step only after all prerequisites are ready. And while the [[Scheduler]] actually has the capability to react on such logical dependencies, for precise timing the schedule deadlines will be prepared in the JobPlanningPipeline. Working backwards from the »top-level« frame job, prerequisites will be discovered incrementally by a recursive depth-first »tree exploration«. Defined by a //exploration function// within an iterator-pipeline construct, further layers of child dependencies are discovered and pushed onto a stack. At some point, a »leaf prerequisite« is discovered -- and at that point the complete chain of dependencies resides within this stack, each represented as a {{{JobPlanning}}} data record. The computation is arranges in a way to link these dependent planning contexts together, allowing to determine the dependent scheduling deadlines by recurring to the parent {{{JobPlanning}}} record, all the way up to the root record, which aligns to an time gird point externally set by the playback timings.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="JobPlanningPipeline" creator="Ichthyostega" modifier="Ichthyostega" created="202305260209" modified="202306202218" tags="operational Rendering draft" changecount="10">
|
||||
<div title="JobPlanningPipeline" creator="Ichthyostega" modifier="Ichthyostega" created="202305260209" modified="202306221803" tags="operational Rendering draft" changecount="11">
|
||||
<pre>//Depth-first evaluation pipeline used in the FrameDispatcher to generate the next chunk of [[render jobs|RenderJob]]//
|
||||
This is an implementation structure backed and established by the [[Dispatcher|FrameDispatcher]] and operated by the RenderDrive core of each CalcStream, where it is assembled by a //builder notation// -- backed by the {{{TreeExplorer}}} iterator pipeline framework; besides the typical filter and transform operations, the latter offers an »expansion« mechanism to integrate a //monadic exhaustive depth-first search,// allowing to pick up all prerequisites of a given calculation, proceeding step by step within a local planning context.
|
||||
This is an implementation structure backed and established by the [[Dispatcher|FrameDispatcher]] and operated by the RenderDrive core of each CalcStream, where it is assembled by a //builder notation// -- backed by the {{{IterExplorer}}} iterator pipeline framework; besides the typical filter and transform operations, the latter offers an »expansion« mechanism to integrate a //monadic exhaustive depth-first search,// allowing to pick up all prerequisites of a given calculation, proceeding step by step within a local planning context.
|
||||
|
||||
!Stages of the Job-planning pipeline
|
||||
;frame-tick
|
||||
|
|
|
|||
|
|
@ -54535,7 +54535,7 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1535891065189" ID="ID_1233342893" MODIFIED="1576282357969" TEXT="Iterator-Monade">
|
||||
<node COLOR="#338800" CREATED="1535891065189" FOLDED="true" ID="ID_1233342893" MODIFIED="1687456645679" TEXT="Iterator-Monade">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
|
|
@ -54557,8 +54557,8 @@
|
|||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#435e98" CREATED="1512925265295" HGAP="103" ID="ID_1502143527" MODIFIED="1538869212828" VSHIFT="-66">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1512925265295" HGAP="103" ID="ID_1502143527" MODIFIED="1687456622837" VSHIFT="-66">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
|
|
@ -54685,21 +54685,24 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1535891101400" ID="ID_673846521" MODIFIED="1535891107371" TEXT="IterExplorer">
|
||||
<node CREATED="1535891108199" ID="ID_1302968576" MODIFIED="1535891111234" TEXT="erster Anlauf">
|
||||
<node COLOR="#5b280f" CREATED="1535891101400" FOLDED="true" ID="ID_673846521" MODIFIED="1687456617298" TEXT="IterExplorer (monade)">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node COLOR="#435e98" CREATED="1535891108199" ID="ID_1302968576" MODIFIED="1687456510053" TEXT="erster Anlauf">
|
||||
<icon BUILTIN="licq"/>
|
||||
<node CREATED="1535891111919" ID="ID_1088778723" MODIFIED="1535891123604" TEXT="funktioniert">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
<node CREATED="1535891114310" ID="ID_799781858" MODIFIED="1535891120950" TEXT="ist häßlich">
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
<node CREATED="1535891125877" ID="ID_131112709" MODIFIED="1535891129432" TEXT="nur einmal verwendet">
|
||||
<node COLOR="#5b280f" CREATED="1535891125877" ID="ID_131112709" MODIFIED="1687456491997" TEXT="nur einmal verwendet">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1535891130164" ID="ID_1396398506" MODIFIED="1535891135892" TEXT="für das Job-Planning">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1512925334501" ID="ID_409327286" MODIFIED="1573229967374" TEXT="IterExplorer-Design">
|
||||
<node COLOR="#5b280f" CREATED="1512925334501" FOLDED="true" ID="ID_409327286" MODIFIED="1687456500552" TEXT="IterExplorer-Design">
|
||||
<linktarget COLOR="#625975" DESTINATION="ID_409327286" ENDARROW="Default" ENDINCLINATION="-843;1771;" ID="Arrow_ID_86323098" SOURCE="ID_185594200" STARTARROW="None" STARTINCLINATION="-493;-1123;"/>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
<node CREATED="1512925397741" ID="ID_1352590559" MODIFIED="1518487921098" TEXT="Design">
|
||||
|
|
@ -54772,24 +54775,26 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1512925864774" ID="ID_1543264108" MODIFIED="1535891440192" TEXT="verwendet für JobPlanning">
|
||||
<node COLOR="#5b280f" CREATED="1512925864774" ID="ID_1543264108" MODIFIED="1687456480773" TEXT="verwendet für JobPlanning">
|
||||
<arrowlink COLOR="#5379b5" DESTINATION="ID_181262071" ENDARROW="Default" ENDINCLINATION="-150;-386;" ID="Arrow_ID_1786254694" STARTARROW="Default" STARTINCLINATION="-1298;0;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1512925959074" ID="ID_30857289" MODIFIED="1518487921098" TEXT="TODO: ablösen durch IterTreeExplorer">
|
||||
<node COLOR="#338800" CREATED="1512925959074" ID="ID_30857289" MODIFIED="1687456358119" TEXT="komplett abgelöst durch IterTreeExplorer">
|
||||
<arrowlink COLOR="#83dfc7" DESTINATION="ID_845080696" ENDARROW="Default" ENDINCLINATION="-1109;3887;" ID="Arrow_ID_1645152965" STARTARROW="None" STARTINCLINATION="2051;-176;"/>
|
||||
<arrowlink COLOR="#bf3473" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1310;-138;" ID="Arrow_ID_1052952035" STARTARROW="None" STARTINCLINATION="-424;31;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680568272530" ID="ID_265531291" LINK="https://issues.lumiera.org/ticket/1276" MODIFIED="1680569227880" TEXT="#1276: Wiederaufnahme des Themas im Rahmen von »PlaybackVerticalSlice«">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<arrowlink COLOR="#2c9fa6" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1310;-138;" ID="Arrow_ID_1052952035" STARTARROW="None" STARTINCLINATION="-424;31;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1680568272530" ID="ID_265531291" LINK="https://issues.lumiera.org/ticket/1276" MODIFIED="1687456383425" TEXT="#1276: Wiederaufnahme des Themas im Rahmen von »PlaybackVerticalSlice«">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1680568313565" ID="ID_1547861521" MODIFIED="1680568337979" TEXT="Ziel: komplett ersetzen durch das neue TreeExplorer-Design">
|
||||
<node COLOR="#435e98" CREATED="1680568313565" ID="ID_1547861521" MODIFIED="1687456380339" TEXT="Ziel: stattdessen das neue TreeExplorer-Design ausbauen">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1535891140563" ID="ID_1503014369" MODIFIED="1535891144430" TEXT="TreeExplorer">
|
||||
<node COLOR="#435e98" CREATED="1535891140563" ID="ID_1503014369" MODIFIED="1687456594305" TEXT="IterExplorer (pipeline)">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1535891145922" ID="ID_1982906906" MODIFIED="1535891149925" TEXT="zweiter Anlauf">
|
||||
<node CREATED="1535891150825" ID="ID_451523751" MODIFIED="1535891155914" TEXT="funktioniert">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
|
|
@ -54810,7 +54815,7 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1514328717684" ID="ID_137835978" MODIFIED="1573229989029" TEXT="neuer Anlauf IterExplorer II">
|
||||
<node COLOR="#338800" CREATED="1514328717684" FOLDED="true" ID="ID_137835978" MODIFIED="1573229989029" TEXT="neuer Anlauf IterExplorer II">
|
||||
<arrowlink COLOR="#8ad3a5" DESTINATION="ID_845080696" ENDARROW="Default" ENDINCLINATION="-230;134;" ID="Arrow_ID_253437970" STARTARROW="None" STARTINCLINATION="-387;0;"/>
|
||||
<linktarget COLOR="#27998e" DESTINATION="ID_137835978" ENDARROW="Default" ENDINCLINATION="-521;-801;" ID="Arrow_ID_1873000120" SOURCE="ID_1860662881" STARTARROW="None" STARTINCLINATION="-1866;98;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -69110,8 +69115,8 @@
|
|||
<node COLOR="#338800" CREATED="1686873575574" ID="ID_1754560865" MODIFIED="1686873615335" TEXT="Umbau: mit neuer Struktur ohne Monaden nutzbar machen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1686873596600" ID="ID_1924946981" MODIFIED="1686873656415" TEXT="Umbau: nun unter / hinter das JobPlanning legen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1686873596600" ID="ID_1924946981" MODIFIED="1687456456197" TEXT="Umbau: nun unter / hinter das JobPlanning legen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1686873681722" ID="ID_574294902" MODIFIED="1686873692546" TEXT="in neuen Dispatcher + RenderDrive integriert">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
|
|
@ -69124,12 +69129,12 @@
|
|||
<arrowlink COLOR="#6c79a2" DESTINATION="ID_1502143527" ENDARROW="Default" ENDINCLINATION="-606;743;" ID="Arrow_ID_616092581" STARTARROW="None" STARTINCLINATION="229;19;"/>
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
<node CREATED="1535891554587" ID="ID_185594200" MODIFIED="1535891663091" TEXT="entwerfe einen IterExplorer">
|
||||
<node CREATED="1535891554587" ID="ID_185594200" MODIFIED="1687443459355" TEXT="Implementierung beruht auf IterExplorer">
|
||||
<arrowlink COLOR="#625975" DESTINATION="ID_409327286" ENDARROW="Default" ENDINCLINATION="-843;1771;" ID="Arrow_ID_86323098" STARTARROW="None" STARTINCLINATION="-493;-1123;"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1680567733451" ID="ID_896127270" MODIFIED="1686841575588" TEXT="Kritik / Refactoring Monaden">
|
||||
<linktarget COLOR="#bf3473" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1310;-138;" ID="Arrow_ID_1052952035" SOURCE="ID_30857289" STARTARROW="None" STARTINCLINATION="-424;31;"/>
|
||||
<linktarget COLOR="#f42f34" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1096;-80;" ID="Arrow_ID_1764912075" SOURCE="ID_1805461369" STARTARROW="None" STARTINCLINATION="-511;24;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1680567733451" ID="ID_896127270" MODIFIED="1687456300887" TEXT="Kritik / Refactoring Monaden">
|
||||
<linktarget COLOR="#2c9fa6" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1310;-138;" ID="Arrow_ID_1052952035" SOURCE="ID_30857289" STARTARROW="None" STARTINCLINATION="-424;31;"/>
|
||||
<linktarget COLOR="#3598d7" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1096;-80;" ID="Arrow_ID_1764912075" SOURCE="ID_1805461369" STARTARROW="None" STARTINCLINATION="-511;24;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#a51558" CREATED="1680568362230" ID="ID_1855480415" MODIFIED="1680568445542" TEXT="Problem: Code selbst für micht nicht mehr verständlich">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
|
@ -69454,7 +69459,8 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1681179592836" ID="ID_1534441402" MODIFIED="1681179596322" TEXT="Umbau-Planung">
|
||||
<node COLOR="#435e98" CREATED="1681179592836" FOLDED="true" ID="ID_1534441402" MODIFIED="1687456297676" TEXT="Umbau-Planung">
|
||||
<icon BUILTIN="list"/>
|
||||
<node CREATED="1681179609394" ID="ID_655076663" MODIFIED="1681184137895" TEXT="Idee: JobPlanning direkt als StateCore nutzen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1681183957626" ID="ID_1460327799" MODIFIED="1681183981602" TEXT="Layer-1: Startpunkte für Planning-Chunk"/>
|
||||
|
|
@ -69490,7 +69496,7 @@
|
|||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1681181487438" ID="ID_1162267066" MODIFIED="1681250532522" TEXT="Frage: Memory-Footprint?">
|
||||
<node COLOR="#435e98" CREATED="1681181487438" ID="ID_1162267066" MODIFIED="1687444453838" TEXT="Frage: Memory-Footprint?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1681229228809" ID="ID_208607615" MODIFIED="1681229238435" TEXT="auch die bestehende Lösung braucht einen Stack"/>
|
||||
<node CREATED="1681229243414" ID="ID_1553851683" MODIFIED="1681229405236" TEXT="dieser ist aber „pfiffig“ in der Linked-List-Datenstruktur versteckt">
|
||||
|
|
@ -69529,6 +69535,36 @@
|
|||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1687444174519" ID="ID_1547339335" MODIFIED="1687444429968" TEXT="Grundsockel an Speicher stabilisiert sich im Lauf">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Einige Aspekte wachsen noch graduell, möglicherweise jedoch bis zum Ende
|
||||
vom Playback
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
besagter Stack für die child-Prerequisites braucht Speicher gemäß
|
||||
maximaler Rekursionstiefe (normalerweise wenig)
|
||||
</li>
|
||||
<li>
|
||||
in jedem neu berührtten Segment muß einmal das JobTicket aufgebaut
|
||||
werden (wird dann aber von weiteren Play-Vorgängen wiederverwendet)
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<font color="#1eac36">��</font> abgesehen davon ist der Speicher stabil
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1687444439187" ID="ID_1133833184" MODIFIED="1687444451394" TEXT="neue Lösung ist besser (nur eine Pipeline)">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1681250556444" ID="ID_1991482624" MODIFIED="1681511717857" TEXT="Grundlegendes Auswertungs-Schema">
|
||||
<icon BUILTIN="yes"/>
|
||||
|
|
@ -69723,7 +69759,7 @@
|
|||
<edge COLOR="#725330"/>
|
||||
<icon BUILTIN="full-2"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1681424610628" ID="ID_1888344503" MODIFIED="1686841525670" STYLE="bubble" TEXT="Dispatch so umschreiben, daß er vom CalcDrive gesteuert wird">
|
||||
<node COLOR="#435e98" CREATED="1681424610628" ID="ID_1888344503" MODIFIED="1687444497843" STYLE="bubble" TEXT="Dispatch so umschreiben, daß er vom CalcDrive gesteuert wird">
|
||||
<edge COLOR="#725330"/>
|
||||
<arrowlink COLOR="#f4fec9" DESTINATION="ID_1928295133" ENDARROW="Default" ENDINCLINATION="-1068;-69;" ID="Arrow_ID_362199078" STARTARROW="None" STARTINCLINATION="1381;58;"/>
|
||||
<icon BUILTIN="full-3"/>
|
||||
|
|
@ -69740,11 +69776,27 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1681424909356" ID="ID_1912461724" MODIFIED="1681424960719" TEXT="Monaden-basierten IterExplorer zurückbauen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1681424909356" ID="ID_1912461724" MODIFIED="1687449596110" TEXT="Monaden-basierten IterExplorer zurückbauen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1687449357545" ID="ID_404047671" MODIFIED="1687449591906" TEXT="HierarchyOrientationIndicator als Folge">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...auch das erscheint mir heute als eine Folge der <i>problematischen Monaden-Struktur</i>. In dem neueren Pipeline-Design liefert die Expand-Funktion eben ganz bewußt zunächst die Parent-Node, und dann erst die Kinder, bzw. man kann die Navigation sogar steuern (da expandChildren() explizit aufgerufen wird). Damit ist ein solcher Struktur-Marker gar nicht mehr notwendig. Diese These wird dadruch bestätitgt, daß ich seither diverse rekursive Auswertungen implementiert habe, und nie mehr auf den HierarchyOrientationIndicator zurückgekommen bin
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1681424933809" ID="ID_377619970" MODIFIED="1681424958374" TEXT="Umbenennen: TreeExplorer ⟼ IterExplorer">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node COLOR="#338800" CREATED="1687449598029" ID="ID_1969213651" MODIFIED="1687449617015" TEXT="alte Dispatcher und Job-Ticket-Impl stillgelegt">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1681424933809" ID="ID_377619970" MODIFIED="1687456440136" TEXT="Umbenennen: TreeExplorer ⟼ IterExplorer">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1681082793383" ID="ID_1314381934" MODIFIED="1681082807141" TEXT="Kritik Scheduler-Interface">
|
||||
|
|
@ -69951,12 +70003,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1685402933522" ID="ID_517929511" MODIFIED="1685406668551" TEXT="TimeAnchor">
|
||||
<node COLOR="#5b280f" CREATED="1685402933522" FOLDED="true" ID="ID_517929511" MODIFIED="1687456721467" TEXT="TimeAnchor">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node COLOR="#338800" CREATED="1685402939155" ID="ID_895439120" LINK="#ID_260441580" MODIFIED="1685402990260" TEXT="Konzept nochmal überprüft: grundsätzlich OK">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685402993556" ID="ID_1110033319" MODIFIED="1685406661129" TEXT="Aber: sollte keine Latenz-Kompensation machen">
|
||||
<node COLOR="#5b280f" CREATED="1685402993556" ID="ID_1110033319" MODIFIED="1687443336633" TEXT="Aber: sollte keine Latenz-Kompensation machen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1685403027730" ID="ID_1618141786" MODIFIED="1685403315041" TEXT="das verwirrt die Konzepte »realTime« und »Deadline«">
|
||||
|
|
@ -69975,8 +70027,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685403325222" ID="ID_2694625" LINK="#ID_109124269" MODIFIED="1685403350256" TEXT="jedwede Latenz-Kompensation / Deadline-Bestimmung in die Planning-Pipeline verschieben">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1685403325222" ID="ID_2694625" LINK="#ID_109124269" MODIFIED="1687443331744" TEXT="jedwede Latenz-Kompensation / Deadline-Bestimmung in die Planning-Pipeline verschieben">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1685406566342" ID="ID_524698195" MODIFIED="1685406623527" TEXT="dann bleibt aber nix mehr übrig....???">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
|
|
@ -69991,17 +70043,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1685406639037" ID="ID_1524269116" MODIFIED="1685406674080" TEXT="Also: TimeAnchor ist überflüssig">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1685406639037" ID="ID_1524269116" MODIFIED="1687443350817" TEXT="Also: TimeAnchor ist überflüssig">
|
||||
<icon BUILTIN="yes"/>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1686841797580" ID="ID_781227780" MODIFIED="1686841806837" TEXT="Rolle der FrameCoord">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#5b280f" CREATED="1686841797580" FOLDED="true" ID="ID_781227780" MODIFIED="1687456723500" TEXT="Rolle der FrameCoord">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1686841807901" ID="ID_886837998" MODIFIED="1686841818018" TEXT="war ein zentraler Daten-Aggregator im 1.Entwurf"/>
|
||||
<node CREATED="1686841818526" ID="ID_834625454" MODIFIED="1686841830837" TEXT="mit dem Wegfall des TimeAnchor nimmt die Bedeutung sehr ab"/>
|
||||
<node CREATED="1686841831377" ID="ID_1948825773" MODIFIED="1686841838684" TEXT="derzeit noch in der Planning-Pipeline verwendet"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1686867570930" ID="ID_1291057680" MODIFIED="1686867716668" TEXT="könnten komplett aufgelöst werden?">
|
||||
<node COLOR="#435e98" CREATED="1686867570930" ID="ID_1291057680" MODIFIED="1687456686167" TEXT="könnten komplett aufgelöst werden?">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
|
|
@ -70048,7 +70100,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1687047415820" ID="ID_450940415" MODIFIED="1687047453090">
|
||||
<node BACKGROUND_COLOR="#f2dcc5" COLOR="#ff0000" CREATED="1687047415820" ID="ID_450940415" MODIFIED="1687456702082">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
|
|
@ -70116,16 +70168,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<linktarget COLOR="#32bc16" DESTINATION="ID_804638831" ENDARROW="Default" ENDINCLINATION="194;0;" ID="Arrow_ID_1177586994" SOURCE="ID_1765761108" STARTARROW="None" STARTINCLINATION="13;81;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1686696897576" ID="ID_218942560" MODIFIED="1686696913065" TEXT="ExplorationState komplett überflüssig">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node COLOR="#338800" CREATED="1686696897576" ID="ID_218942560" MODIFIED="1687456730985" TEXT="ExplorationState komplett überflüssig">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685227587042" ID="ID_1506519459" MODIFIED="1685227597731" TEXT="EngineConfig">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685227599324" ID="ID_996861061" MODIFIED="1685227641903" TEXT="Timings verwenden einen Singleton-Zugang">
|
||||
<node COLOR="#5b280f" CREATED="1685227599324" ID="ID_996861061" MODIFIED="1687456742695" TEXT="Timings verwenden einen Singleton-Zugang">
|
||||
<linktarget COLOR="#b03e6a" DESTINATION="ID_996861061" ENDARROW="Default" ENDINCLINATION="1914;0;" ID="Arrow_ID_280066938" SOURCE="ID_978050958" STARTARROW="None" STARTINCLINATION="202;-9;"/>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685227615700" ID="ID_233202097" MODIFIED="1685227641889" TEXT="sollte besser in die RenderEnvironmentClosure">
|
||||
<icon BUILTIN="yes"/>
|
||||
|
|
@ -84405,11 +84458,11 @@ class Something
|
|||
<arrowlink COLOR="#963345" DESTINATION="ID_1805461369" ENDARROW="Default" ENDINCLINATION="367;0;" ID="Arrow_ID_271837208" STARTARROW="None" STARTINCLINATION="175;160;"/>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680396470946" ID="ID_970682100" MODIFIED="1680396490033" TEXT="Aufgabe: noch ein letztes Mal verstehen, was das sollte">
|
||||
<node COLOR="#435e98" CREATED="1680396470946" ID="ID_970682100" MODIFIED="1687456404436" TEXT="Aufgabe: noch ein letztes Mal verstehen, was das sollte">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680396490895" ID="ID_1991292419" MODIFIED="1680396509638" TEXT="Umbauen auf das Pipeline / TreeExplorer-Framework">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1680396490895" ID="ID_1991292419" MODIFIED="1687456406048" TEXT="Umbauen auf das Pipeline / TreeExplorer-Framework">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1680396521021" ID="ID_1520286749" MODIFIED="1680396527735" TEXT="das hat sich inzwischen viel besser bewährt"/>
|
||||
<node CREATED="1680396528171" ID="ID_1369183009" MODIFIED="1680396546921">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -84493,11 +84546,11 @@ class Something
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1680305211903" ID="ID_1805461369" LINK="https://issues.lumiera.org/ticket/1276" MODIFIED="1681165797441" TEXT="#1276 reassess design of render job deadline planning">
|
||||
<arrowlink COLOR="#f42f34" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1096;-80;" ID="Arrow_ID_1764912075" STARTARROW="None" STARTINCLINATION="-511;24;"/>
|
||||
<node COLOR="#338800" CREATED="1680305211903" ID="ID_1805461369" LINK="https://issues.lumiera.org/ticket/1276" MODIFIED="1687456413800" TEXT="#1276 reassess design of render job deadline planning">
|
||||
<arrowlink COLOR="#3598d7" DESTINATION="ID_896127270" ENDARROW="Default" ENDINCLINATION="-1096;-80;" ID="Arrow_ID_1764912075" STARTARROW="None" STARTINCLINATION="-511;24;"/>
|
||||
<linktarget COLOR="#963345" DESTINATION="ID_1805461369" ENDARROW="Default" ENDINCLINATION="367;0;" ID="Arrow_ID_271837208" SOURCE="ID_216719314" STARTARROW="None" STARTINCLINATION="175;160;"/>
|
||||
<linktarget COLOR="#6a92a9" DESTINATION="ID_1805461369" ENDARROW="Default" ENDINCLINATION="190;-8;" ID="Arrow_ID_1209058185" SOURCE="ID_699634936" STARTARROW="None" STARTINCLINATION="61;-2;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1684967502141" HGAP="32" ID="ID_1293956051" LINK="https://issues.lumiera.org/ticket/920" MODIFIED="1684975150795" TEXT="#1301 Render-Drive refactoring" VSHIFT="9">
|
||||
<arrowlink COLOR="#942e62" DESTINATION="ID_470129345" ENDARROW="Default" ENDINCLINATION="-1340;103;" ID="Arrow_ID_1563903339" STARTARROW="None" STARTINCLINATION="-1408;109;"/>
|
||||
<linktarget COLOR="#fa2575" DESTINATION="ID_1293956051" ENDARROW="Default" ENDINCLINATION="47;4;" ID="Arrow_ID_652930263" SOURCE="ID_305633266" STARTARROW="None" STARTINCLINATION="-8;28;"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue