2012-07-01 03:42:50 +02:00
|
|
|
/*
|
|
|
|
|
JOB-PLANNING.hpp - steps to prepare and build render jobs
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef PROC_ENGINE_JOB_PLANNING_H
|
|
|
|
|
#define PROC_ENGINE_JOB_PLANNING_H
|
|
|
|
|
|
|
|
|
|
#include "proc/common.hpp"
|
|
|
|
|
//#include "proc/state.hpp"
|
|
|
|
|
#include "proc/engine/job.hpp"
|
|
|
|
|
#include "proc/engine/job-ticket.hpp"
|
2012-09-01 17:33:42 +02:00
|
|
|
#include "proc/engine/time-anchor.hpp"
|
2012-07-01 03:42:50 +02:00
|
|
|
//#include "proc/engine/frame-coord.hpp"
|
|
|
|
|
//#include "lib/time/timevalue.hpp"
|
|
|
|
|
//#include "lib/time/timequant.hpp"
|
|
|
|
|
//#include "lib/meta/function.hpp"
|
|
|
|
|
#include "lib/iter-adapter.hpp"
|
|
|
|
|
#include "lib/iter-explorer.hpp"
|
|
|
|
|
//#include "lib/linked-elements.hpp"
|
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
|
|
|
|
//#include <boost/noncopyable.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace proc {
|
|
|
|
|
namespace engine {
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
//using lib::time::TimeSpan;
|
|
|
|
|
//using lib::time::Duration;
|
|
|
|
|
//using lib::time::FSecs;
|
|
|
|
|
//using lib::time::Time;
|
|
|
|
|
//using lib::LinkedElements;
|
|
|
|
|
using util::isnil;
|
2012-07-21 20:27:52 +02:00
|
|
|
using util::unConst;
|
2012-07-01 03:42:50 +02:00
|
|
|
//
|
|
|
|
|
//class ExitNode;
|
|
|
|
|
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
/**
|
|
|
|
|
*/
|
|
|
|
|
class JobPlanning
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-08-22 10:43:55 +02:00
|
|
|
JobTicket::ExplorationState plannedOperations_;
|
|
|
|
|
FrameCoord point_to_calculate_;
|
2012-07-22 03:11:01 +02:00
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
public:
|
2012-07-21 20:27:52 +02:00
|
|
|
/** by default create the bottom element of job planning,
|
|
|
|
|
* which happens to to plan no job at all. It is represented
|
|
|
|
|
* using an inactive state core (default constructed)
|
|
|
|
|
*/
|
|
|
|
|
JobPlanning()
|
|
|
|
|
{ }
|
|
|
|
|
|
2012-08-22 10:43:55 +02:00
|
|
|
/** further job planning can be initiated by continuing off a given previous planning state.
|
|
|
|
|
* This is how the forks are created, expanding into a multitude of prerequisites for
|
|
|
|
|
* the job in question.
|
2012-07-21 20:27:52 +02:00
|
|
|
*/
|
2012-08-22 10:43:55 +02:00
|
|
|
JobPlanning (JobTicket::ExplorationState const& startingPoint, FrameCoord requestedFrame)
|
2012-07-21 20:27:52 +02:00
|
|
|
: plannedOperations_(startingPoint)
|
|
|
|
|
, point_to_calculate_(requestedFrame)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
// using the standard copy operations
|
2012-07-01 03:42:50 +02:00
|
|
|
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
/** cast and explicate this job planning information
|
|
|
|
|
* to create a frame job descriptor, ready to be scheduled
|
|
|
|
|
*/
|
|
|
|
|
operator Job()
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
if (isnil (plannedOperations_))
|
|
|
|
|
throw error::Logic("Attempt to plan a frame-Job based on a missing, "
|
|
|
|
|
"unspecified, exhausted or superseded job description"
|
|
|
|
|
,error::LUMIERA_ERROR_BOTTOM_VALUE);
|
|
|
|
|
|
|
|
|
|
return plannedOperations_->createJobFor (point_to_calculate_);
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
/** build a new JobPlanning object,
|
|
|
|
|
* set to explore the prerequisites
|
|
|
|
|
* at the given planning situation
|
|
|
|
|
*/
|
|
|
|
|
JobPlanning
|
|
|
|
|
discoverPrerequisites() const
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
if (isnil (plannedOperations_))
|
|
|
|
|
return JobPlanning();
|
|
|
|
|
else
|
2012-09-03 01:49:14 +02:00
|
|
|
return JobPlanning (plannedOperations_->discoverPrerequisites (point_to_calculate_.channelNr)
|
2012-07-21 20:27:52 +02:00
|
|
|
,this->point_to_calculate_);
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
2012-07-21 20:27:52 +02:00
|
|
|
|
2012-08-22 10:43:55 +02:00
|
|
|
/** integrate another chain of prerequisites into the current evaluation line.
|
|
|
|
|
* Further evaluation will start to visit prerequisites from the new starting point,
|
2012-09-04 01:55:11 +02:00
|
|
|
* 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 */
|
2012-07-22 03:11:01 +02:00
|
|
|
friend void
|
2012-09-03 01:49:14 +02:00
|
|
|
integrate (JobPlanning const& newStartingPoint, JobPlanning& existingPlan)
|
2012-07-22 03:11:01 +02:00
|
|
|
{
|
2012-09-04 01:55:11 +02:00
|
|
|
if (isnil (existingPlan.plannedOperations_))
|
|
|
|
|
{ // current evaluation is exhausted: switch to new starting point
|
|
|
|
|
existingPlan.point_to_calculate_ = newStartingPoint.point_to_calculate_;
|
|
|
|
|
}
|
2012-07-22 03:11:01 +02:00
|
|
|
existingPlan.plannedOperations_.push (newStartingPoint.plannedOperations_);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
|
|
|
|
|
/* === Iteration control API for IterStateWrapper== */
|
|
|
|
|
|
|
|
|
|
friend bool
|
2012-07-21 20:27:52 +02:00
|
|
|
checkPoint (JobPlanning const& plan)
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
return !isnil (plan.plannedOperations_);
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend JobPlanning&
|
2012-07-21 20:27:52 +02:00
|
|
|
yield (JobPlanning const& plan)
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
REQUIRE (checkPoint (plan));
|
|
|
|
|
return unConst(plan);
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend void
|
2012-07-21 20:27:52 +02:00
|
|
|
iterNext (JobPlanning & plan)
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-08-22 10:43:55 +02:00
|
|
|
plan.plannedOperations_.pullNext();
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
};
|
2012-07-22 03:11:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
class PlanningState
|
|
|
|
|
: public lib::IterStateWrapper<JobPlanning>
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
typedef lib::IterStateWrapper<JobPlanning> _Iter;
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
|
|
|
|
|
public:
|
2012-07-21 20:27:52 +02:00
|
|
|
/** inactive evaluation */
|
|
|
|
|
PlanningState()
|
|
|
|
|
: _Iter()
|
|
|
|
|
{ }
|
2012-07-01 03:42:50 +02:00
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
explicit
|
|
|
|
|
PlanningState (JobPlanning const& startingPoint)
|
|
|
|
|
: _Iter(startingPoint) // note: invoking copy ctor on state core
|
2012-07-01 03:42:50 +02:00
|
|
|
{ }
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
// using the standard copy operations
|
2012-07-01 03:42:50 +02:00
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
|
2012-07-22 03:11:01 +02:00
|
|
|
|
|
|
|
|
/* === API for JobPlanningSequence to expand the tree of prerequisites === */
|
|
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
/** attach and integrate the given planning details into this planning state.
|
2012-07-22 03:11:01 +02:00
|
|
|
* Actually the evaluation proceeds depth-first with the other state,
|
|
|
|
|
* returning later on to the current position for further evaluation */
|
2012-07-21 20:27:52 +02:00
|
|
|
PlanningState &
|
|
|
|
|
wrapping (JobPlanning const& startingPoint)
|
|
|
|
|
{
|
2012-07-22 03:11:01 +02:00
|
|
|
integrate (startingPoint, this->stateCore());
|
2012-07-21 20:27:52 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlanningState &
|
|
|
|
|
usingSequence (PlanningState const& prerequisites)
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
2012-07-21 20:27:52 +02:00
|
|
|
if (isnil (prerequisites))
|
|
|
|
|
return *this;
|
|
|
|
|
else
|
|
|
|
|
return this->wrapping(*prerequisites);
|
2012-07-22 03:11:01 +02:00
|
|
|
// explanation: PlanningState represents a sequence of successive planning points.
|
|
|
|
|
// actually this is implemented by switching an embedded JobPlanning element
|
2012-09-01 17:33:42 +02:00
|
|
|
// through a sequence of states. Thus the initial state of an investigation
|
2012-07-22 03:11:01 +02:00
|
|
|
// (which is a JobPlanning) can stand-in for the sequence of prerequisites
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
|
}
|
2012-07-01 03:42:50 +02:00
|
|
|
};
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
inline PlanningState
|
2012-09-01 17:33:42 +02:00
|
|
|
expandPrerequisites (JobPlanning const& calculationStep)
|
2012-07-21 20:27:52 +02:00
|
|
|
{
|
|
|
|
|
PlanningState newSubEvaluation(
|
2012-09-01 17:33:42 +02:00
|
|
|
calculationStep.discoverPrerequisites());
|
2012-07-21 20:27:52 +02:00
|
|
|
return newSubEvaluation;
|
|
|
|
|
}
|
2012-07-01 03:42:50 +02:00
|
|
|
|
2012-07-21 20:27:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
{
|
2012-10-06 02:29:19 +02:00
|
|
|
engine::FrameSequencer* locationGenerator_;
|
|
|
|
|
engine::FrameCoord current_;
|
2012-09-01 17:33:42 +02:00
|
|
|
|
2012-07-22 03:11:01 +02:00
|
|
|
public:
|
|
|
|
|
typedef JobPlanning value_type;
|
|
|
|
|
typedef JobPlanning& reference;
|
|
|
|
|
typedef JobPlanning * pointer;
|
|
|
|
|
|
2012-09-01 17:33:42 +02:00
|
|
|
// PlanningStepGenerator() { }
|
|
|
|
|
|
|
|
|
|
PlanningStepGenerator(engine::TimeAnchor startPoint)
|
|
|
|
|
: anchor_(startPoint)
|
|
|
|
|
{ }
|
2012-10-06 02:29:19 +02:00
|
|
|
//////////////////////////////////////////////////////////////TODO actually we need two distinct services
|
|
|
|
|
//////////////////////////////////////////////////////////////TODO - getting the next FrameCoord
|
|
|
|
|
//////////////////////////////////////////////////////////////TODO - getting the JobTicket for this location
|
|
|
|
|
//////////////////////////////////////////////////////////////TODO Actually, the Dispatcher would provide exactly those services,
|
|
|
|
|
//////////////////////////////////////////////////////////////TODO but depending on the Dispatcher constitutes a cyclic dependency.
|
|
|
|
|
//////////////////////////////////////////////////////////////TODO There seems to be a problem hidden somewhere in this design.
|
2012-07-22 03:11:01 +02:00
|
|
|
|
2012-07-01 03:42:50 +02:00
|
|
|
/* === Iteration control API for IterStateWrapper== */
|
|
|
|
|
|
|
|
|
|
friend bool
|
|
|
|
|
checkPoint (PlanningStepGenerator const& gen)
|
|
|
|
|
{
|
2012-07-22 03:11:01 +02:00
|
|
|
UNIMPLEMENTED ("determine planing chunk size"); /// return bool(seq.feed());
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend JobPlanning&
|
|
|
|
|
yield (PlanningStepGenerator const& gen)
|
|
|
|
|
{
|
2012-07-22 03:11:01 +02:00
|
|
|
UNIMPLEMENTED ("generate a single frame job planning tree"); ///return *(seq.feed());
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend void
|
|
|
|
|
iterNext (PlanningStepGenerator & gen)
|
|
|
|
|
{
|
2012-07-22 03:11:01 +02:00
|
|
|
UNIMPLEMENTED ("proceed to next frame"); ///////seq.iterate();
|
2012-07-01 03:42:50 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2012-09-01 17:33:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef PlanningState (*SIG_expandPrerequisites) (JobPlanning const&);
|
|
|
|
|
|
|
|
|
|
typedef lib::IterExplorer<PlanningStepGenerator
|
|
|
|
|
,lib::iter_explorer::RecursiveSelfIntegration> JobPlanningChunkStartPoint;
|
|
|
|
|
|
|
|
|
|
typedef JobPlanningChunkStartPoint::FlatMapped<SIG_expandPrerequisites>::Type ExpandedPlanningSequence;
|
2012-07-01 03:42:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2012-10-06 02:29:19 +02:00
|
|
|
* This iterator represents a pipeline to pull planned jobs from.
|
|
|
|
|
* For dispatching individual frame jobs for rendering, this pipeline is
|
|
|
|
|
* generated and internally wired 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.
|
|
|
|
|
*
|
|
|
|
|
*
|
2012-07-01 03:42:50 +02:00
|
|
|
* @todo 6/12 WIP-WIP-WIP how to prepare jobs for scheduling
|
|
|
|
|
*/
|
|
|
|
|
class JobPlanningSequence
|
2012-09-01 17:33:42 +02:00
|
|
|
: public ExpandedPlanningSequence
|
2012-07-01 03:42:50 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public:
|
2012-09-01 17:33:42 +02:00
|
|
|
// JobPlanningSequence() { }
|
|
|
|
|
|
|
|
|
|
JobPlanningSequence(engine::TimeAnchor startPoint)
|
|
|
|
|
: ExpandedPlanningSequence(
|
|
|
|
|
JobPlanningChunkStartPoint(
|
|
|
|
|
PlanningStepGenerator(startPoint))
|
|
|
|
|
>>= expandPrerequisites)
|
|
|
|
|
{ }
|
2012-07-01 03:42:50 +02:00
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace proc::engine
|
|
|
|
|
#endif
|