Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/compositor/intern/COM_ExecutionGroup.cpp')
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cpp566
1 files changed, 566 insertions, 0 deletions
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cpp b/source/blender/compositor/intern/COM_ExecutionGroup.cpp
new file mode 100644
index 00000000000..22d4366a1a8
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cpp
@@ -0,0 +1,566 @@
+/*
+ * 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
+ */
+
+#include "COM_ExecutionGroup.h"
+#include "COM_InputSocket.h"
+#include "COM_SocketConnection.h"
+#include "COM_defines.h"
+#include "math.h"
+#include "COM_ExecutionSystem.h"
+#include <sstream>
+#include "COM_ReadBufferOperation.h"
+#include "COM_WriteBufferOperation.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+#include "COM_ViewerOperation.h"
+#include <stdlib.h>
+#include "BLI_math.h"
+#include "COM_MemoryManager.h"
+#include "PIL_time.h"
+#include "COM_ChunkOrder.h"
+#include <algorithm>
+#include "BLI_math.h"
+#include "COM_ExecutionSystemHelper.h"
+
+ExecutionGroup::ExecutionGroup() {
+ this->isOutput = false;
+ this->complex = false;
+ this->chunkExecutionStates = NULL;
+ this->bTree = NULL;
+ this->height = 0;
+ this->width = 0;
+ this->cachedMaxReadBufferOffset = 0;
+ this->numberOfXChunks = 0;
+ this->numberOfYChunks = 0;
+ this->numberOfChunks = 0;
+ this->initialized = false;
+ this->openCL = false;
+ this->chunksFinished = 0;
+}
+
+int ExecutionGroup::getRenderPriotrity() {
+ return this->getOutputNodeOperation()->getRenderPriority();
+}
+
+bool ExecutionGroup::containsOperation(NodeOperation* operation) {
+ for (vector<NodeOperation*>::const_iterator iterator = this->operations.begin() ; iterator != this->operations.end() ; ++iterator) {
+ NodeOperation* inListOperation = *iterator;
+ if (inListOperation == operation) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const bool ExecutionGroup::isComplex() const {
+ return this->complex;
+}
+
+bool ExecutionGroup::canContainOperation(NodeOperation* operation) {
+ if (!this->initialized) {return true;}
+ if (operation->isReadBufferOperation()) {return true;}
+ if (operation->isWriteBufferOperation()) {return false;}
+ if (operation->isSetOperation()) {return true;}
+
+ if (!this->isComplex()) {
+ return (!operation->isComplex());
+ } else {
+ return false;
+ }
+}
+
+void ExecutionGroup::addOperation(ExecutionSystem *system, NodeOperation *operation) {
+ if (containsOperation(operation)) return;
+ if (canContainOperation(operation)) {
+ if (!operation->isBufferOperation()) {
+ this->complex = operation->isComplex();
+ this->openCL = operation->isOpenCL();
+ this->initialized = true;
+ }
+ this->operations.push_back(operation);
+ if (operation->isReadBufferOperation()) {
+ ReadBufferOperation* readOperation = (ReadBufferOperation*)operation;
+ WriteBufferOperation* writeOperation = readOperation->getMemoryProxy()->getWriteBufferOperation();
+ this->addOperation(system, writeOperation);
+ } else {
+ unsigned int index;
+ for (index = 0 ; index < operation->getNumberOfInputSockets(); index ++) {
+ InputSocket * inputSocket = operation->getInputSocket(index);
+ if (inputSocket->isConnected()) {
+ NodeOperation* node = (NodeOperation*)inputSocket->getConnection()->getFromNode();
+ this->addOperation(system, node);
+ }
+ }
+ }
+ } else {
+ if (operation->isWriteBufferOperation()) {
+ WriteBufferOperation * writeoperation = (WriteBufferOperation*)operation;
+ if (writeoperation->getMemoryProxy()->getExecutor() == NULL) {
+ ExecutionGroup* newGroup = new ExecutionGroup();
+ writeoperation->getMemoryProxy()->setExecutor(newGroup);
+ newGroup->addOperation(system, operation);
+ ExecutionSystemHelper::addExecutionGroup(system->getExecutionGroups(), newGroup);
+ }
+ }
+ }
+}
+
+NodeOperation* ExecutionGroup::getOutputNodeOperation() const{
+ return this->operations[0]; // the first operation of the group is always the output operation.
+}
+
+void ExecutionGroup::initExecution()
+{
+ if (this->chunkExecutionStates != NULL) {
+ delete[] this->chunkExecutionStates;
+ }
+ unsigned int index;
+ determineNumberOfChunks();
+
+ this->chunkExecutionStates = NULL;
+ if (this->numberOfChunks != 0) {
+ this->chunkExecutionStates = new ChunkExecutionState[numberOfChunks];
+ for (index = 0 ; index < numberOfChunks ; index ++) {
+ this->chunkExecutionStates[index] = COM_ES_NOT_SCHEDULED;
+ }
+ }
+
+
+ unsigned int maxNumber = 0;
+
+ for (index = 0 ; index < this->operations.size(); index ++) {
+ NodeOperation* operation = this->operations[index];
+ if (operation->isReadBufferOperation()) {
+ ReadBufferOperation *readOperation = (ReadBufferOperation*)operation;
+ this->cachedReadOperations.push_back(readOperation);
+ maxNumber = max(maxNumber, readOperation->getOffset());
+ }
+ }
+ maxNumber++;
+ this->cachedMaxReadBufferOffset = maxNumber;
+
+}
+
+void ExecutionGroup::deinitExecution() {
+ if (this->chunkExecutionStates != NULL) {
+ delete[] this->chunkExecutionStates;
+ this->chunkExecutionStates = NULL;
+ }
+ this->numberOfChunks = 0;
+ this->numberOfXChunks = 0;
+ this->numberOfYChunks = 0;
+ this->cachedReadOperations.clear();
+ this->bTree = NULL;
+}
+void ExecutionGroup::determineResolution(unsigned int resolution[]) {
+ NodeOperation* operation = this->getOutputNodeOperation();
+ unsigned int preferredResolution[2];
+ preferredResolution[0] = 0;
+ preferredResolution[1] = 0;
+ operation->determineResolution(resolution, preferredResolution);
+ operation->setResolution(resolution);
+ this->setResolution(resolution);
+}
+
+void ExecutionGroup::determineNumberOfChunks() {
+ const float chunkSizef = this->chunkSize;
+ this->numberOfXChunks = ceil(this->width / chunkSizef);
+ this->numberOfYChunks = ceil(this->height / chunkSizef);
+ this->numberOfChunks = this->numberOfXChunks * this->numberOfYChunks;
+}
+
+/**
+ * this method is called for the top execution groups. containing the compositor node or the preview node or the viewer node)
+ */
+void ExecutionGroup::execute(ExecutionSystem* graph) {
+ CompositorContext& context = graph->getContext();
+ const bNodeTree* bTree = context.getbNodeTree();
+ if (this->width == 0 || this->height == 0) {return;} /// @note: break out... no pixels to calculate.
+ if (bTree->test_break && bTree->test_break(bTree->tbh)) {return;} /// @note: early break out for blur and preview nodes
+ if (this->numberOfChunks == 0) {return;} /// @note: early break out
+ unsigned int chunkNumber;
+
+ this->chunksFinished = 0;
+ this->bTree = bTree;
+ unsigned int index;
+ unsigned int *chunkOrder = new unsigned int[this->numberOfChunks];
+
+ for (chunkNumber = 0 ; chunkNumber<this->numberOfChunks ; chunkNumber++) {
+ chunkOrder[chunkNumber] = chunkNumber;
+ }
+ NodeOperation *operation = this->getOutputNodeOperation();
+ float centerX = 0.5;
+ float centerY = 0.5;
+ int chunkorder = COM_TO_CENTER_OUT;
+
+ if (operation->isViewerOperation()) {
+ ViewerBaseOperation* viewer = (ViewerBaseOperation*)operation;
+ centerX = viewer->getCenterX();
+ centerY = viewer->getCenterY();
+ chunkorder = viewer->getChunkOrder();
+ }
+
+ switch (chunkorder) {
+ case COM_TO_RANDOM:
+ for (index = 0 ; index < 2* numberOfChunks ; index ++) {
+ int index1 = rand()%numberOfChunks;
+ int index2 = rand()%numberOfChunks;
+ int s = chunkOrder[index1];
+ chunkOrder[index1] = chunkOrder[index2];
+ chunkOrder[index2] = s;
+ }
+ break;
+ case COM_TO_CENTER_OUT:
+ {
+ ChunkOrderHotspot **hotspots = new ChunkOrderHotspot*[1];
+ hotspots[0] = new ChunkOrderHotspot(this->width*centerX, this->height*centerY, 0.0f);
+ rcti rect;
+ ChunkOrder *chunkOrders = new ChunkOrder[this->numberOfChunks];
+ for (index = 0 ; index < this->numberOfChunks; index ++) {
+ determineChunkRect(&rect, index);
+ chunkOrders[index].setChunkNumber(index);
+ chunkOrders[index].setX(rect.xmin);
+ chunkOrders[index].setY(rect.ymin);
+ chunkOrders[index].determineDistance(hotspots, 1);
+ }
+
+ sort(&chunkOrders[0], &chunkOrders[numberOfChunks-1]);
+ for (index = 0 ; index < numberOfChunks; index ++) {
+ chunkOrder[index] = chunkOrders[index].getChunkNumber();
+ }
+
+ delete hotspots[0];
+ delete[] hotspots;
+ delete[] chunkOrders;
+ }
+ break;
+ case COM_TO_RULE_OF_THIRDS:
+ {
+ ChunkOrderHotspot **hotspots = new ChunkOrderHotspot*[9];
+ unsigned int tx = this->width/6;
+ unsigned int ty = this->height/6;
+ unsigned int mx = this->width/2;
+ unsigned int my = this->height/2;
+ unsigned int bx = mx+2*tx;
+ unsigned int by = my+2*ty;
+
+ float addition = numberOfChunks/COM_RULE_OF_THIRDS_DIVIDER;
+ hotspots[0] = new ChunkOrderHotspot(mx, my, addition*0);
+ hotspots[1] = new ChunkOrderHotspot(tx, my, addition*1);
+ hotspots[2] = new ChunkOrderHotspot(bx, my, addition*2);
+ hotspots[3] = new ChunkOrderHotspot(bx, by, addition*3);
+ hotspots[4] = new ChunkOrderHotspot(tx, ty, addition*4);
+ hotspots[5] = new ChunkOrderHotspot(bx, ty, addition*5);
+ hotspots[6] = new ChunkOrderHotspot(tx, by, addition*6);
+ hotspots[7] = new ChunkOrderHotspot(mx, ty, addition*7);
+ hotspots[8] = new ChunkOrderHotspot(mx, by, addition*8);
+ rcti rect;
+ ChunkOrder *chunkOrders = new ChunkOrder[this->numberOfChunks];
+ for (index = 0 ; index < this->numberOfChunks; index ++) {
+ determineChunkRect(&rect, index);
+ chunkOrders[index].setChunkNumber(index);
+ chunkOrders[index].setX(rect.xmin);
+ chunkOrders[index].setY(rect.ymin);
+ chunkOrders[index].determineDistance(hotspots, 9);
+ }
+
+ sort(&chunkOrders[0], &chunkOrders[numberOfChunks]);
+
+ for (index = 0 ; index < numberOfChunks; index ++) {
+ chunkOrder[index] = chunkOrders[index].getChunkNumber();
+ }
+
+ delete hotspots[0];
+ delete hotspots[1];
+ delete hotspots[2];
+ delete hotspots[3];
+ delete hotspots[4];
+ delete hotspots[5];
+ delete hotspots[6];
+ delete hotspots[7];
+ delete hotspots[8];
+ delete[] hotspots;
+ delete[] chunkOrders;
+ }
+ break;
+ case COM_TO_TOP_DOWN:
+ default:
+ break;
+ }
+
+ bool breaked = false;
+ bool finished = false;
+ unsigned int startIndex = 0;
+ const int maxNumberEvaluated = BLI_system_thread_count()*2;
+
+ while (!finished && !breaked) {
+ unsigned int index;
+ bool startEvaluated = false;
+ finished = true;
+ int numberEvaluated = 0;
+
+ for (index = startIndex ; index < numberOfChunks && numberEvaluated < maxNumberEvaluated; index ++) {
+ int chunkNumber = chunkOrder[index];
+ int yChunk = chunkNumber/this->numberOfXChunks;
+ int xChunk = chunkNumber - (yChunk*this->numberOfXChunks);
+ const ChunkExecutionState state = this->chunkExecutionStates[chunkNumber];
+ if (state == COM_ES_NOT_SCHEDULED) {
+ scheduleChunkWhenPossible(graph, xChunk, yChunk);
+ finished=false;
+ startEvaluated = true;
+ numberEvaluated++;
+ } else if (state == COM_ES_SCHEDULED) {
+ finished=false;
+ startEvaluated = true;
+ numberEvaluated++;
+ } else if (state == COM_ES_EXECUTED && !startEvaluated) {
+ startIndex = index+1;
+ }
+ }
+ PIL_sleep_ms(10);
+
+ if (bTree->test_break && bTree->test_break(bTree->tbh)) {
+ breaked = true;
+ }
+ }
+
+ delete[] chunkOrder;
+}
+
+MemoryBuffer** ExecutionGroup::getInputBuffers(int chunkNumber) {
+ rcti rect;
+ vector<MemoryProxy*> memoryproxies;
+ unsigned int index;
+ determineChunkRect(&rect, chunkNumber);
+
+ this->determineDependingMemoryProxies(&memoryproxies);
+ MemoryBuffer **memoryBuffers = new MemoryBuffer*[this->cachedMaxReadBufferOffset];
+ for (index= 0 ; index < this->cachedMaxReadBufferOffset ; index ++) {
+ memoryBuffers[index] = NULL;
+ }
+ rcti output;
+ for (index = 0 ; index < this->cachedReadOperations.size(); index ++) {
+ ReadBufferOperation *readOperation = (ReadBufferOperation*)this->cachedReadOperations[index];
+ MemoryProxy * memoryProxy = readOperation->getMemoryProxy();
+ this->determineDependingAreaOfInterest(&rect, readOperation, &output);
+ MemoryBuffer* memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(memoryProxy, &output);
+ memoryBuffers[readOperation->getOffset()] = memoryBuffer;
+ }
+ return memoryBuffers;
+}
+
+MemoryBuffer* ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy, rcti *rect) {
+ // find all chunks inside the rect
+ // determine minxchunk, minychunk, maxxchunk, maxychunk where x and y are chunknumbers
+ float chunkSizef = this->chunkSize;
+
+ int indexx, indexy;
+
+ const int minxchunk = floor(rect->xmin/chunkSizef);
+ const int maxxchunk = ceil((rect->xmax-1)/chunkSizef);
+ const int minychunk = floor(rect->ymin/chunkSizef);
+ const int maxychunk = ceil((rect->ymax-1)/chunkSizef);
+
+ if (maxxchunk== minxchunk+1 && maxychunk == minychunk+1) {
+ const int chunkNumber = minxchunk+minychunk*numberOfXChunks;
+ MemoryBuffer *result = MemoryManager::getMemoryBuffer(memoryProxy, chunkNumber);
+ return result;
+ }
+
+ rcti chunkRect;
+ chunkRect.xmin = minxchunk*this->chunkSize;
+ chunkRect.xmax = maxxchunk*this->chunkSize;
+ chunkRect.ymin = minychunk*this->chunkSize;
+ chunkRect.ymax = maxychunk*this->chunkSize;
+
+ CLAMP(chunkRect.xmin, 0, (int)this->width);
+ CLAMP(chunkRect.xmax, 0, (int)this->width);
+ CLAMP(chunkRect.ymin, 0, (int)this->height);
+ CLAMP(chunkRect.ymax, 0, (int)this->height);
+
+ MemoryBuffer *result = new MemoryBuffer(memoryProxy, &chunkRect);
+
+ for (indexx = max(minxchunk, 0); indexx<min((int)this->numberOfXChunks, maxxchunk) ; indexx++) {
+ for (indexy = max(minychunk, 0); indexy<min((int)this->numberOfYChunks, maxychunk) ; indexy++) {
+ int chunkNumber = indexx+indexy*this->numberOfXChunks;
+ MemoryBuffer *chunkBuffer = MemoryManager::getMemoryBuffer(memoryProxy, chunkNumber);
+ result->copyContentFrom(chunkBuffer);
+ }
+ }
+
+ return result;
+}
+
+void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer** memoryBuffers) {
+ if (this->chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED)
+ this->chunkExecutionStates[chunkNumber] = COM_ES_EXECUTED;
+ else
+ throw "Threading inconsistency";
+
+ this->chunksFinished++;
+ if (memoryBuffers) {
+ for (unsigned int index = 0 ; index < this->cachedMaxReadBufferOffset; index ++) {
+ MemoryBuffer * buffer = memoryBuffers[index];
+ if (buffer) {
+ if (buffer->isTemporarily()) {
+ memoryBuffers[index] = NULL;
+ delete buffer;
+ }
+ }
+ }
+ delete[] memoryBuffers;
+ }
+ if (bTree) {
+ // status report is only performed for top level Execution Groups.
+ float progress = chunksFinished;
+ progress/=numberOfChunks;
+ bTree->progress(bTree->prh, progress);
+ }
+}
+
+inline void ExecutionGroup::determineChunkRect(rcti* rect, const unsigned int xChunk, const unsigned int yChunk ) const {
+ const unsigned int minx = xChunk * chunkSize;
+ const unsigned int miny = yChunk * chunkSize;
+ BLI_init_rcti(rect, minx, min(minx + this->chunkSize, this->width), miny, min(miny + this->chunkSize, this->height));
+}
+
+void ExecutionGroup::determineChunkRect(rcti* rect, const unsigned int chunkNumber) const {
+ const unsigned int yChunk = chunkNumber / numberOfXChunks;
+ const unsigned int xChunk = chunkNumber - (yChunk * numberOfXChunks);
+ determineChunkRect(rect, xChunk, yChunk);
+}
+
+MemoryBuffer* ExecutionGroup::allocateOutputBuffer(int chunkNumber, rcti* rect) {
+ MemoryBuffer* outputBuffer = NULL;
+ // output allocation is only valid when our outputoperation is a memorywriter
+ NodeOperation * operation = this->getOutputNodeOperation();
+ if (operation->isWriteBufferOperation()) {
+ WriteBufferOperation* writeOperation = (WriteBufferOperation*)operation;
+ outputBuffer = MemoryManager::allocateMemoryBuffer(writeOperation->getMemoryProxy(), chunkNumber, rect);
+ }
+ return outputBuffer;
+}
+
+
+bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem * graph, rcti *area) {
+ // find all chunks inside the rect
+ // determine minxchunk, minychunk, maxxchunk, maxychunk where x and y are chunknumbers
+
+ float chunkSizef = this->chunkSize;
+
+ int indexx, indexy;
+ const int minxchunk = floor(area->xmin/chunkSizef);
+ const int maxxchunk = ceil((area->xmax-1)/chunkSizef);
+ const int minychunk = floor(area->ymin/chunkSizef);
+ const int maxychunk = ceil((area->ymax-1)/chunkSizef);
+
+ bool result = true;
+ for (indexx = max(minxchunk, 0); indexx<maxxchunk ; indexx++) {
+ for (indexy = max(minychunk, 0); indexy<maxychunk ; indexy++) {
+ if (!scheduleChunkWhenPossible(graph, indexx, indexy)) {
+ result = false;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber) {
+ if (this->chunkExecutionStates[chunkNumber] == COM_ES_NOT_SCHEDULED) {
+ this->chunkExecutionStates[chunkNumber] = COM_ES_SCHEDULED;
+ WorkScheduler::schedule(this, chunkNumber);
+ return true;
+ }
+ return false;
+}
+
+bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem * graph, int xChunk, int yChunk) {
+ if (xChunk < 0 || xChunk >= (int)this->numberOfXChunks) {
+ return true;
+ }
+ if (yChunk < 0 || yChunk >= (int)this->numberOfYChunks) {
+ return true;
+ }
+ int chunkNumber = yChunk*this->numberOfXChunks + xChunk;
+ // chunk is already executed
+ if (this->chunkExecutionStates[chunkNumber] == COM_ES_EXECUTED) {
+ return true;
+ }
+
+ // chunk is scheduled, but not executed
+ if (this->chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED) {
+ return false;
+ }
+
+ // chunk is nor executed nor scheduled.
+ vector<MemoryProxy*> memoryProxies;
+ this->determineDependingMemoryProxies(&memoryProxies);
+
+ rcti rect;
+ determineChunkRect(&rect, xChunk, yChunk);
+ unsigned int index;
+ bool canBeExecuted = true;
+ rcti area;
+
+ for (index = 0 ; index < cachedReadOperations.size() ; index ++) {
+ ReadBufferOperation * readOperation = (ReadBufferOperation*)cachedReadOperations[index];
+ BLI_init_rcti(&area, 0, 0, 0, 0);
+ MemoryProxy * memoryProxy = memoryProxies[index];
+ determineDependingAreaOfInterest(&rect, readOperation, &area);
+ ExecutionGroup *group = memoryProxy->getExecutor();
+
+ if (group != NULL) {
+ if (!group->scheduleAreaWhenPossible(graph, &area)) {
+ canBeExecuted = false;
+ }
+ } else {
+ throw "ERROR";
+ }
+ }
+
+ if (canBeExecuted) {
+ scheduleChunk(chunkNumber);
+ }
+
+ return false;
+}
+
+void ExecutionGroup::determineDependingAreaOfInterest(rcti * input, ReadBufferOperation* readOperation, rcti* output) {
+ this->getOutputNodeOperation()->determineDependingAreaOfInterest(input, readOperation, output);
+}
+
+void ExecutionGroup::determineDependingMemoryProxies(vector<MemoryProxy*> *memoryProxies) {
+ unsigned int index;
+ for (index = 0 ; index < this->cachedReadOperations.size() ; index ++) {
+ ReadBufferOperation * readOperation = (ReadBufferOperation*) this->cachedReadOperations[index];
+ memoryProxies->push_back(readOperation->getMemoryProxy());
+ }
+}
+
+bool ExecutionGroup::operator ==(const ExecutionGroup & executionGroup) const {
+ return this->getOutputNodeOperation() == executionGroup.getOutputNodeOperation();
+}
+
+bool ExecutionGroup::isOpenCL() {
+ return this->openCL;
+}