/* * Copyright 2011, Blender Foundation. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor: * Jeroen Bakker * Monique Dewanchand */ #ifndef __COM_EXECUTIONGROUP_H__ #define __COM_EXECUTIONGROUP_H__ #include "COM_Node.h" #include "COM_NodeOperation.h" #include #include "BLI_rect.h" #include "COM_MemoryProxy.h" #include "COM_Device.h" #include "COM_CompositorContext.h" using std::vector; class ExecutionSystem; class MemoryProxy; class ReadBufferOperation; class Device; /** * \brief the execution state of a chunk in an ExecutionGroup * \ingroup Execution */ typedef enum ChunkExecutionState { /** * \brief chunk is not yet scheduled */ COM_ES_NOT_SCHEDULED = 0, /** * \brief chunk is scheduled, but not yet executed */ COM_ES_SCHEDULED = 1, /** * \brief chunk is executed. */ COM_ES_EXECUTED = 2 } ChunkExecutionState; /** * \brief Class ExecutionGroup is a group of Operations that are executed as one. * This grouping is used to combine Operations that can be executed as one whole when multi-processing. * \ingroup Execution */ class ExecutionGroup { public: typedef std::vector Operations; private: // fields /** * \brief list of operations in this ExecutionGroup */ Operations m_operations; /** * \brief is this ExecutionGroup an input ExecutionGroup * an input execution group is a group that is at the end of the calculation (the output is important for the user) */ int m_isOutput; /** * \brief Width of the output */ unsigned int m_width; /** * \brief Height of the output */ unsigned int m_height; /** * \brief size of a single chunk, being Width or of height * a chunk is always a square, except at the edges of the MemoryBuffer */ unsigned int m_chunkSize; /** * \brief number of chunks in the x-axis */ unsigned int m_numberOfXChunks; /** * \brief number of chunks in the y-axis */ unsigned int m_numberOfYChunks; /** * \brief total number of chunks */ unsigned int m_numberOfChunks; /** * \brief contains this ExecutionGroup a complex NodeOperation. */ bool m_complex; /** * \brief can this ExecutionGroup be scheduled on an OpenCLDevice */ bool m_openCL; /** * \brief Is this Execution group SingleThreaded */ bool m_singleThreaded; /** * \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup. * \note this is used to construct the MemoryBuffers that will be passed during execution. */ unsigned int m_cachedMaxReadBufferOffset; /** * \brief a cached vector of all read operations in the execution group. */ Operations m_cachedReadOperations; /** * \brief reference to the original bNodeTree, this field is only set for the 'top' execution group. * \note can only be used to call the callbacks for progress, status and break */ const bNodeTree *m_bTree; /** * \brief total number of chunks that have been calculated for this ExecutionGroup */ unsigned int m_chunksFinished; /** * \brief the chunkExecutionStates holds per chunk the execution state. this state can be * - COM_ES_NOT_SCHEDULED: not scheduled * - COM_ES_SCHEDULED: scheduled * - COM_ES_EXECUTED: executed */ ChunkExecutionState *m_chunkExecutionStates; /** * \brief indicator when this ExecutionGroup has valid Operations in its vector for Execution * \note When building the ExecutionGroup Operations are added via recursion. First a WriteBufferOperations is added, then the * \note Operation containing the settings that is important for the ExecutiongGroup is added, * \note When this occurs, these settings are copied over from the node to the ExecutionGroup * \note and the Initialized flag is set to true. * \see complex * \see openCL */ bool m_initialized; /** * \brief denotes boundary for border compositing * \note measured in pixel space */ rcti m_viewerBorder; /** * \brief start time of execution */ double m_executionStartTime; // methods /** * \brief check whether parameter operation can be added to the execution group * \param operation the operation to be added */ bool canContainOperation(NodeOperation *operation); /** * \brief calculate the actual chunk size of this execution group. * \note A chunk size is an unsigned int that is both the height and width of a chunk. * \note The chunk size will not be stored in the chunkSize field. This needs to be done * \note by the calling method. */ unsigned int determineChunkSize(); /** * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position. * \note Only gives useful results ater the determination of the chunksize * \see determineChunkSize() */ void determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const; /** * \brief determine the number of chunks, based on the chunkSize, width and height. * \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks */ void determineNumberOfChunks(); /** * \brief try to schedule a specific chunk. * \note scheduling succeeds when all input requirements are met and the chunks hasn't been scheduled yet. * \param graph * \param xChunk * \param yChunk * \return [true:false] * true: package(s) are scheduled * false: scheduling is deferred (depending workpackages are scheduled) */ bool scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk); /** * \brief try to schedule a specific area. * \note Check if a certain area is available, when not available this are will be checked. * \note This method is called from other ExecutionGroup's. * \param graph * \param rect * \return [true:false] * true: package(s) are scheduled * false: scheduling is deferred (depending workpackages are scheduled) */ bool scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *rect); /** * \brief add a chunk to the WorkScheduler. * \param chunknumber */ bool scheduleChunk(unsigned int chunkNumber); /** * \brief determine the area of interest of a certain input area * \note This method only evaluates a single ReadBufferOperation * \param input the input area * \param readOperation The ReadBufferOperation where the area needs to be evaluated * \param output the area needed of the ReadBufferOperation. Result */ void determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); public: // constructors ExecutionGroup(); // methods /** * \brief add an operation to this ExecutionGroup * \note this method will add input of the operations recursively * \note this method can create multiple ExecutionGroup's * \param system * \param operation * \return True if the operation was successfully added */ bool addOperation(NodeOperation *operation); /** * \brief is this ExecutionGroup an output ExecutionGroup * \note An OutputExecution group are groups containing a * \note ViewerOperation, CompositeOperation, PreviewOperation. * \see NodeOperation.isOutputOperation */ int isOutputExecutionGroup() const { return this->m_isOutput; } /** * \brief set whether this ExecutionGroup is an output * \param isOutput */ void setOutputExecutionGroup(int isOutput) { this->m_isOutput = isOutput; } /** * \brief determine the resolution of this ExecutionGroup * \param resolution */ void determineResolution(unsigned int resolution[2]); /** * \brief set the resolution of this executiongroup * \param resolution */ void setResolution(unsigned int resolution[2]) { this->m_width = resolution[0]; this->m_height = resolution[1]; } /** * \brief get the width of this execution group */ unsigned int getWidth() const { return m_width; } /** * \brief get the height of this execution group */ unsigned int getHeight() const { return m_height; } /** * \brief does this ExecutionGroup contains a complex NodeOperation */ bool isComplex() const { return m_complex; } /** * \brief get the output operation of this ExecutionGroup * \return NodeOperation *output operation */ NodeOperation *getOutputOperation() const; /** * \brief compose multiple chunks into a single chunk * \return Memorybuffer *consolidated chunk */ MemoryBuffer *constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy, rcti *output); /** * \brief initExecution is called just before the execution of the whole graph will be done. * \note The implementation will calculate the chunkSize of this execution group. */ void initExecution(); /** * \brief get all inputbuffers needed to calculate an chunk * \note all inputbuffers must be executed * \param chunkNumber the chunk to be calculated * \return (MemoryBuffer **) the inputbuffers */ MemoryBuffer **getInputBuffersCPU(); /** * \brief get all inputbuffers needed to calculate an chunk * \note all inputbuffers must be executed * \param chunkNumber the chunk to be calculated * \return (MemoryBuffer **) the inputbuffers */ MemoryBuffer **getInputBuffersOpenCL(int chunkNumber); /** * \brief allocate the outputbuffer of a chunk * \param chunkNumber the number of the chunk in the ExecutionGroup * \param rect the rect of that chunk * \see determineChunkRect */ MemoryBuffer *allocateOutputBuffer(int chunkNumber, rcti *rect); /** * \brief after a chunk is executed the needed resources can be freed or unlocked. * \param chunknumber * \param memorybuffers */ void finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers); /** * \brief deinitExecution is called just after execution the whole graph. * \note It will release all needed resources */ void deinitExecution(); /** * \brief schedule an ExecutionGroup * \note this method will return when all chunks have been calculated, or the execution has breaked (by user) * * first the order of the chunks will be determined. This is determined by finding the ViewerOperation and get the relevant information from it. * - ChunkOrdering * - CenterX * - CenterY * * After determining the order of the chunks the chunks will be scheduled * * \see ViewerOperation * \param system */ void execute(ExecutionSystem *system); /** * \brief this method determines the MemoryProxy's where this execution group depends on. * \note After this method determineDependingAreaOfInterest can be called to determine * \note the area of the MemoryProxy.creator that has to be executed. * \param memoryProxies result */ void determineDependingMemoryProxies(vector *memoryProxies); /** * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk. * \note Only gives useful results ater the determination of the chunksize * \see determineChunkSize() */ void determineChunkRect(rcti *rect, const unsigned int chunkNumber) const; /** * \brief can this ExecutionGroup be scheduled on an OpenCLDevice * \see WorkScheduler.schedule */ bool isOpenCL(); void setChunksize(int chunksize) { this->m_chunkSize = chunksize; } /** * \brief get the Render priority of this ExecutionGroup * \see ExecutionSystem.execute */ CompositorPriority getRenderPriotrity(); /** * \brief set border for viewer operation * \note all the coordinates are assumed to be in normalized space */ void setViewerBorder(float xmin, float xmax, float ymin, float ymax); void setRenderBorder(float xmin, float xmax, float ymin, float ymax); /* allow the DebugInfo class to look at internals */ friend class DebugInfo; #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionGroup") #endif }; #endif