/* DISPATCHER.hpp - translating calculation streams into frame jobs Copyright (C) Lumiera.org 2011, Hermann Vosseler 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_DISPATCHER_H #define PROC_ENGINE_DISPATCHER_H #include "proc/common.hpp" #include "proc/mobject/model-port.hpp" #include "proc/engine/time-anchor.hpp" #include "proc/engine/frame-coord.hpp" #include "proc/engine/job-ticket.hpp" #include "proc/engine/job-planning.hpp" #include "lib/time/timevalue.hpp" #include #include namespace proc { namespace engine { using std::function; using mobject::ModelPort; using lib::time::FrameCnt; using lib::time::TimeSpan; using lib::time::FSecs; using lib::time::Time; /** * Internal abstraction: a service within the engine * for translating a logical calculation stream (corresponding to a PlayProcess) * into a sequence of individual RenderJob entries for calculations and data access. * The actual implementation of this service is tied to the low-level-model, i.e. * the render nodes network. The Dispatcher service is used to implement the CalcStreams * during playback and rendering; there will be a continuous, chunk-wise proceeding * evaluation and planning of new jobs, which can then be handed over to the Scheduler * for time-bound activation. * * \par usage considerations * the asynchronous and ongoing nature of the render process mandates to avoid a central * instance for operating this planning process. Instead, together with each chunk of * planned jobs we generate a continuation job, which -- on activation -- will pick up * the planning of the next chunk. The Dispatcher interface was shaped especially to * support this process, with a local JobBuilder for use within the continuation job, * and a TimeAnchor to represent the continuation point. All the complexities of * actually planning the jobs are hidden within the JobPlanningSequence, * which, for the purpose of dispatching a series of jobs just looks * like a sequence of job descriptors * * @todo 10/12 still WIP, but conceptually settled by now */ class Dispatcher : public FrameLocator { struct JobBuilder { Dispatcher* dispatcher_; ModelPort modelPort_; uint channel_; FrameCoord relativeFrameLocation (TimeAnchor& refPoint, FrameCnt frameCountOffset =0); JobPlanningSequence establishNextJobs (TimeAnchor& refPoint) { return JobPlanningSequence( relativeFrameLocation(refPoint), *dispatcher_); } }; public: virtual ~Dispatcher(); ///< this is an interface JobBuilder onCalcStream (ModelPort modelPort, uint channel); protected: /** 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& virtual JobTicket& accessJobTicket (ModelPort, TimeValue nominalTime) =0; }; }} // namespace proc::engine #endif