LUMIERA.clone/src/proc/engine/job-ticket.hpp
Ichthyostega 082822fde8 relocate the JobClosure interface to be defined alongside of Job
This is necessary since the implementation of the job functions
calls through the VTable of the interface JobClosure. Thus this
interface (and the VTable definition) needs to reside within
some compilation unit linked together with the basic job class.

TODO: move class Job entirely into the Backend
2013-05-31 02:59:32 +02:00

253 lines
7.9 KiB
C++

/*
JOB-TICKET.hpp - execution plan for 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_TICKET_H
#define PROC_ENGINE_JOB_TICKET_H
#include "proc/common.hpp"
//#include "proc/state.hpp"
#include "proc/engine/job.hpp"
#include "proc/engine/frame-coord.hpp"
//#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
#include "lib/hierarchy-orientation-indicator.hpp"
#include "lib/linked-elements.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <stack>
namespace proc {
namespace engine {
//using lib::time::TimeSpan;
//using lib::time::Duration;
//using lib::time::FSecs;
//using lib::time::Time;
using lib::LinkedElements;
using lib::OrientationIndicator;
using util::isnil;
//
//class ExitNode;
/**
* execution plan for pulling a specific exit node.
* Usable as blue print for generating actual render jobs.
* Job tickets are created on demand, specialised for each segment
* of the low-level model, and for each individual feed (corresponding
* to a single model port). Once created, they are final for this segment,
* stored together with the other descriptor objects (ProcNode and WiringDescriptor)
* and finally discarded in bulk, in case that segment of the low-level model becomes
* obsolete and is replaced by a newly built new version of this model segment.
*
* Job tickets are created by a classical recursive descent call on the exit node,
* which figures out everything to be done for generating data from this node.
* To turn a JobTicket into an actual job, we need the additional information
* regarding the precise frame number (=nominal time) and the channel number
* to calculate (in case the actual feed is multichannel, which is the default).
* This way, the JobTicket acts as <i>higher order function:</i> a function
* generating on invocation another, specific function (= the job).
*
* @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
*/
class JobTicket
: boost::noncopyable
{
struct Provision
{
Provision* next;
////////////////////TODO some channel or format descriptor here
};
struct Prerequisite
{
Prerequisite* next;
JobTicket* descriptor;
};
struct Prerequisites ///< per channel
{
Prerequisites* next;
LinkedElements<Prerequisite> requiredJobs_;
};
LinkedElements<Provision> channelConfig_;
LinkedElements<Prerequisites> requirement_;
public:
class ExplorationState;
friend class ExplorationState;
JobTicket()
{
UNIMPLEMENTED ("job representation, planning and scheduling");
}
ExplorationState startExploration() const;
ExplorationState discoverPrerequisites (uint channelNr) const;
Job createJobFor (FrameCoord coordinates);
bool
isValid() const
{
if (channelConfig_.size() != requirement_.size())
return false;
UNIMPLEMENTED ("validity self check");
}
};
class JobTicket::ExplorationState
{
typedef LinkedElements<Prerequisite>::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 (!isnil (prerequisites.requiredJobs_))
toExplore_.push (prerequisites.requiredJobs_.begin());
}
// using default copy operations
bool
empty() const
{
return toExplore_.empty();
}
void
markTreeLocation()
{
UNIMPLEMENTED ("establish tree relation to previous point");
}
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*
operator->() const
{
REQUIRE (!empty() && toExplore_.top().isValid());
REQUIRE (toExplore_.top()->descriptor);
REQUIRE (toExplore_.top()->descriptor->isValid());
return toExplore_.top()->descriptor;
}
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);
}
}
};
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 an 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 an 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
{
REQUIRE (channelNr < requirement_.size());
return ExplorationState (requirement_[channelNr]);
}
}} // namespace proc::engine
#endif