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 0213 9 , USA .
*/
2013-01-12 14:36:01 +01:00
/** @file job-planning.hpp
2023-06-15 03:51:07 +02:00
* * Aggregation of planning data to generate actual frame calculation jobs .
* * These render jobs are generated periodically by an ongoing process while rendering is underway .
* * For this purpose , each CalcStream of the play / render process operates a RenderDrive with a
* * _job - planning pipeline_ , rooted at the » master beat « as defined by the frame grid from the
* * Timings spec of the current render process . This pipeline will assemble the specifications
* * for the render jobs and thereby possibly discover prerequisites , which must be calculated first .
* * From a usage point of view , the _job - planning pipeline_ is an _iterator : _ for each independent
* * calculation step a new JobPlanning record appears at the output side of the pipeline , holding
* * all collected data , sufficient to generate the actual job definition , which can then be
2023-06-19 03:56:11 +02:00
* * handed over to the Scheduler .
* *
* * # Implementation of the Job - Planning pipeline
* *
* * JobPlanning acts as _working data aggregator_ within the Job - Planning pipeline ; for this reason
* * all data fields are references , and the optimiser is expected to elide them , since after template
* * instantiation , JobPlanning becomes part of the overall assembled pipeline object , stacked on top
* * of the Dispatcher : : PipeFrameTick , which holds and increments the current frame number . The
* * underlying play : : Timings will provide a _frame grid_ to translate these frame numbers into
* * the _nominal time values_ used throughout the rest of the render calculations .
* *
* * There is one tricky detail to note regarding the handling of calculation prerequisites . The
* * typical example would be the loading and decoding of media data , which is an IO - bound task and
* * must be complete before the main frame job can be started . Since the Job - Planning pipeline is
* * generic , this kind of detail dependency is modelled as _prerequisite JobTicket , _ leading to
* * an possibly extended depth - first tree expansion , starting from the » master frame ticket « at
2023-06-22 20:23:55 +02:00
* * the root . This _tree exploration_ is implemented by the IterExplorer : : Expander building block ,
2023-06-19 03:56:11 +02:00
* * which obviously has to maintain a stack of expanded child dependencies . This leads to the
* * observation , that at any point of this dependency processing , for the complete path from the
* * child prerequisite up to the root tick there is a sequence of JobPlanning instances placed
* * into this stack in the Explorer object ( each level in this stack is actually an iterator
* * and handles one level of child prerequisites ) . The deadline calculation directly exploits
* * this known arrangement , insofar each JobPlanning has a pointer to its parent ( sitting in
2023-06-22 20:23:55 +02:00
* * the stack level above ) . See the [ IterExplorer unit test ] ( \ ref lib : : IterExplorer_test : : verify_expandOperation )
2023-06-19 03:56:11 +02:00
* * to understand this recursive on - demand processing in greater detail .
2013-04-29 01:36:32 +02:00
* *
2023-06-15 03:51:07 +02:00
* * @ see JobPlanning_test
2013-01-12 14:36:01 +01:00
* * @ see JobTicket
* * @ see Dispatcher
* * @ see EngineService
* *
*/
2012-07-01 03:42:50 +02:00
2018-11-15 23:52:02 +01:00
# ifndef STEAM_ENGINE_JOB_PLANNING_H
# define STEAM_ENGINE_JOB_PLANNING_H
2012-07-01 03:42:50 +02:00
2018-11-15 23:42:43 +01:00
# include "steam/common.hpp"
# include "vault/engine/job.h"
# include "steam/engine/job-ticket.hpp"
2023-06-16 01:50:11 +02:00
# include "steam/play/output-slot.hpp"
2023-06-16 04:09:38 +02:00
# include "steam/play/timings.hpp"
2013-08-30 02:00:35 +02:00
# include "lib/time/timevalue.hpp"
2023-06-19 03:56:11 +02:00
# include "lib/itertools.hpp"
2023-06-19 01:51:48 +02:00
# include "lib/nocopy.hpp"
2012-07-01 03:42:50 +02:00
2018-11-15 23:55:13 +01:00
namespace steam {
2012-07-01 03:42:50 +02:00
namespace engine {
2012-07-21 20:27:52 +02:00
namespace error = lumiera : : error ;
2023-06-16 01:50:11 +02:00
using play : : DataSink ;
2023-06-16 04:09:38 +02:00
using play : : Timings ;
using lib : : time : : Time ;
2023-06-22 20:23:55 +02:00
using lib : : time : : TimeVar ;
2023-06-16 04:09:38 +02:00
using lib : : time : : Duration ;
2013-01-12 14:36:01 +01:00
2012-07-01 03:42:50 +02:00
2013-01-11 16:48:28 +01:00
/**
* View on the execution planning for a single calculation step .
* When this view - frontend becomes accessible , behind the scenes all
2015-09-11 03:36:22 +02:00
* the necessary information has been pulled and collected from the
2013-01-11 16:48:28 +01:00
* low - level model and the relevant rendering / playback configuration .
* Typically , clients will materialise this planning into a Job ( descriptor )
* ready to be entered into the scheduler .
*
* JobPlanning is indeed a view ; the represented planning information is not
* persisted ( other then in the job to be created ) . The implementation draws
* on a recursive exploration of the corresponding JobTicket , which acts as
* a general blueprint for creating jobs within this segment of the timeline .
2013-01-12 14:36:01 +01:00
*/
2012-07-21 20:27:52 +02:00
class JobPlanning
2023-06-19 01:51:48 +02:00
: util : : MoveOnly
2012-07-01 03:42:50 +02:00
{
2023-06-19 01:51:48 +02:00
JobTicket & jobTicket_ ;
2023-06-20 01:46:44 +02:00
TimeVar const & nominalTime_ ;
2023-06-19 01:51:48 +02:00
FrameCnt const & frameNr_ ;
2023-06-19 03:56:11 +02:00
2012-07-01 03:42:50 +02:00
public :
2023-06-20 01:46:44 +02:00
JobPlanning ( JobTicket & ticket , TimeVar const & nominalTime , FrameCnt const & frameNr )
2023-06-19 01:51:48 +02:00
: jobTicket_ { ticket }
, nominalTime_ { nominalTime }
, frameNr_ { frameNr }
2012-07-21 20:27:52 +02:00
{ }
2023-06-19 03:56:11 +02:00
// move construction is possible
2012-07-21 20:27:52 +02:00
2023-06-19 03:56:11 +02:00
JobTicket & ticket ( ) { return jobTicket_ ; }
bool isTopLevel ( ) const { return not dependentPlan_ ; }
2012-07-01 03:42:50 +02:00
2023-06-16 01:50:11 +02:00
/**
* Connect and complete the planning information assembled thus far
* to create a frame job descriptor , ready to be scheduled .
2012-07-21 20:27:52 +02:00
*/
2023-06-16 01:50:11 +02:00
Job
buildJob ( )
2012-07-01 03:42:50 +02:00
{
2023-06-19 01:51:48 +02:00
Job job = jobTicket_ . createJobFor ( Time { nominalTime_ } ) ;
2023-06-16 01:50:11 +02:00
//////////////////////////////////////////////////////TICKET #1295 : somehow package and communicate the DataSink info
return job ;
2012-07-01 03:42:50 +02:00
}
2023-06-16 04:09:38 +02:00
/**
* Calculate the latest time point when to _start_ the job ,
* so it can still possibly reach the timing goal .
* @ return time point in wall - clock - time , or Time : : ANYTIME if unconstrained
*/
Time
determineDeadline ( Timings const & timings )
{
switch ( timings . playbackUrgency )
{
case play : : ASAP :
case play : : NICE :
return Time : : ANYTIME ;
case play : : TIMEBOUND :
2023-06-19 18:28:01 +02:00
return doCalcDeadline ( timings ) ;
2023-06-16 04:09:38 +02:00
}
NOTREACHED ( " unexpected playbackUrgency " ) ;
}
/**
* Determine a timing buffer for flexibility to allow starting the job
* already before its deadline ; especially for real - time playback this leeway
* is rather limited , and constrained by the earliest time the target buffer
* is already allotted and ready to receive data .
* @ return tolerance duration
* - Duration : : NIL if deadline has to be matched with maximum precision
* - Duration : : MAX for unlimited leeway to start anytime before the deadline
*/
Duration
determineLeeway ( Timings const & )
{
UNIMPLEMENTED ( " Job planning logic to establish Leeway for scheduling " ) ;
}
2023-06-19 03:56:11 +02:00
/**
* Build a sequence of dependent JobPlanning scopes for all prerequisites
* of this current JobPlanning , and internally linked back to ` * this `
* @ return an iterator which explores the prerequisites of the JobTicket .
* @ remark typical example would be to load data from file , or to require
* the results from some other extended media calculation .
* @ see Dispatcher : : PipelineBuilder : : expandPrerequisites ( )
*/
auto
buildDependencyPlanning ( )
{
return lib : : transformIterator ( jobTicket_ . getPrerequisites ( )
, [ this ] ( JobTicket & prereqTicket )
{
return JobPlanning { * this , prereqTicket } ;
} ) ;
}
2023-06-16 04:09:38 +02:00
private :
2023-06-19 03:56:11 +02:00
/** link to a dependent JobPlanning, for planning of prerequisites */
JobPlanning * dependentPlan_ { nullptr } ;
/**
* @ internal construct a chained prerequisite JobPlanning ,
* attached to the dependent » parent « JobPlanning , using the same
* frame data , but chaining up the deadlines , so that a Job created
* from this JobPlanning needs to be completed before the » parent «
* Job ( which uses the generated data ) can start
* @ see # buildDependencyPlanning ( )
* @ see JobPlanning_test : : setupDependentJob ( )
*/
JobPlanning ( JobPlanning & parent , JobTicket & prerequisite )
: jobTicket_ { prerequisite }
, nominalTime_ { parent . nominalTime_ }
, frameNr_ { parent . frameNr_ }
, dependentPlan_ { & parent }
{ }
2023-06-19 18:28:01 +02:00
Time
doCalcDeadline ( Timings const & timings )
{
2023-06-21 03:55:09 +02:00
if ( isTopLevel ( ) )
return timings . getTimeDue ( frameNr_ ) // anchor at timing grid
- jobTicket_ . getExpectedRuntime ( ) // deduce the presumably runtime
- timings . engineLatency // and the generic engine overhead
- timings . outputLatency ; // Note: output latency only on top-level job
else
return dependentPlan_ - > determineDeadline ( timings ) ////////////////////////////////////////////TICKET #1310 : WARNING - quadratic in the depth of the dependency chain
- jobTicket_ . getExpectedRuntime ( )
- timings . engineLatency ;
2023-06-19 18:28:01 +02:00
}
2012-07-01 03:42:50 +02:00
} ;
2023-06-22 20:23:55 +02:00
} } // namespace steam::engine
# endif /*STEAM_ENGINE_JOB_PLANNING_H*/