rename remaining class members with m_ prefix.
[blender.git] / source / blender / compositor / intern / COM_ExecutionGroup.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: 
19  *              Jeroen Bakker 
20  *              Monique Dewanchand
21  */
22
23 #include <algorithm>
24 #include <math.h>
25 #include <sstream>
26 #include <stdlib.h>
27
28 #include "BLI_math.h"
29 #include "PIL_time.h"
30
31 #include "COM_ExecutionGroup.h"
32 #include "COM_InputSocket.h"
33 #include "COM_SocketConnection.h"
34 #include "COM_defines.h"
35 #include "COM_ExecutionSystem.h"
36 #include "COM_ReadBufferOperation.h"
37 #include "COM_WriteBufferOperation.h"
38 #include "COM_ReadBufferOperation.h"
39 #include "COM_WorkScheduler.h"
40 #include "COM_ViewerOperation.h"
41 #include "COM_ChunkOrder.h"
42 #include "COM_ExecutionSystemHelper.h"
43
44 ExecutionGroup::ExecutionGroup()
45 {
46         this->m_isOutput = false;
47         this->m_complex = false;
48         this->m_chunkExecutionStates = NULL;
49         this->m_bTree = NULL;
50         this->m_height = 0;
51         this->m_width = 0;
52         this->m_cachedMaxReadBufferOffset = 0;
53         this->m_numberOfXChunks = 0;
54         this->m_numberOfYChunks = 0;
55         this->m_numberOfChunks = 0;
56         this->m_initialized = false;
57         this->m_openCL = false;
58         this->m_singleThreaded = false;
59         this->m_chunksFinished = 0;
60 }
61
62 CompositorPriority ExecutionGroup::getRenderPriotrity()
63 {
64         return this->getOutputNodeOperation()->getRenderPriority();
65 }
66
67 bool ExecutionGroup::containsOperation(NodeOperation *operation)
68 {
69         for (vector<NodeOperation *>::const_iterator iterator = this->m_operations.begin(); iterator != this->m_operations.end(); ++iterator) {
70                 NodeOperation *inListOperation = *iterator;
71                 if (inListOperation == operation) {
72                         return true;
73                 }
74         }
75         return false;
76 }
77
78 const bool ExecutionGroup::isComplex() const
79 {
80         return this->m_complex;
81 }
82
83 bool ExecutionGroup::canContainOperation(NodeOperation *operation)
84 {
85         if (!this->m_initialized) { return true; }
86         if (operation->isReadBufferOperation()) { return true; }
87         if (operation->isWriteBufferOperation()) { return false; }
88         if (operation->isSetOperation()) { return true; }
89
90         if (!this->isComplex()) {
91                 return (!operation->isComplex());
92         }
93         else {
94                 return false;
95         }
96 }
97
98 void ExecutionGroup::addOperation(ExecutionSystem *system, NodeOperation *operation)
99 {
100         if (containsOperation(operation)) return;
101         if (canContainOperation(operation)) {
102                 if (!operation->isBufferOperation()) {
103                         this->m_complex = operation->isComplex();
104                         this->m_openCL = operation->isOpenCL();
105                         this->m_singleThreaded = operation->isSingleThreaded();
106                         this->m_initialized = true;
107                 }
108                 this->m_operations.push_back(operation);
109                 if (operation->isReadBufferOperation()) {
110                         ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
111                         WriteBufferOperation *writeOperation = readOperation->getMemoryProxy()->getWriteBufferOperation();
112                         this->addOperation(system, writeOperation);
113                 }
114                 else {
115                         unsigned int index;
116                         for (index = 0; index < operation->getNumberOfInputSockets(); index++) {
117                                 InputSocket *inputSocket = operation->getInputSocket(index);
118                                 if (inputSocket->isConnected()) {
119                                         NodeOperation *node = (NodeOperation *)inputSocket->getConnection()->getFromNode();
120                                         this->addOperation(system, node);
121                                 }
122                         }
123                 }
124         }
125         else {
126                 if (operation->isWriteBufferOperation()) {
127                         WriteBufferOperation *writeoperation = (WriteBufferOperation *)operation;
128                         if (writeoperation->getMemoryProxy()->getExecutor() == NULL) {
129                                 ExecutionGroup *newGroup = new ExecutionGroup();
130                                 writeoperation->getMemoryProxy()->setExecutor(newGroup);
131                                 newGroup->addOperation(system, operation);
132                                 ExecutionSystemHelper::addExecutionGroup(system->getExecutionGroups(), newGroup);
133                         }
134                 }
135         }
136 }
137
138 NodeOperation *ExecutionGroup::getOutputNodeOperation() const
139 {
140         return this->m_operations[0]; // the first operation of the group is always the output operation.
141 }
142
143 void ExecutionGroup::initExecution()
144 {
145         if (this->m_chunkExecutionStates != NULL) {
146                 delete[] this->m_chunkExecutionStates;
147         }
148         unsigned int index;
149         determineNumberOfChunks();
150
151         this->m_chunkExecutionStates = NULL;
152         if (this->m_numberOfChunks != 0) {
153                 this->m_chunkExecutionStates = new ChunkExecutionState[this->m_numberOfChunks];
154                 for (index = 0; index < this->m_numberOfChunks; index++) {
155                         this->m_chunkExecutionStates[index] = COM_ES_NOT_SCHEDULED;
156                 }
157         }
158
159
160         unsigned int maxNumber = 0;
161
162         for (index = 0; index < this->m_operations.size(); index++) {
163                 NodeOperation *operation = this->m_operations[index];
164                 if (operation->isReadBufferOperation()) {
165                         ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
166                         this->m_cachedReadOperations.push_back(readOperation);
167                         maxNumber = max(maxNumber, readOperation->getOffset());
168                 }
169         }
170         maxNumber++;
171         this->m_cachedMaxReadBufferOffset = maxNumber;
172
173 }
174
175 void ExecutionGroup::deinitExecution()
176 {
177         if (this->m_chunkExecutionStates != NULL) {
178                 delete[] this->m_chunkExecutionStates;
179                 this->m_chunkExecutionStates = NULL;
180         }
181         this->m_numberOfChunks = 0;
182         this->m_numberOfXChunks = 0;
183         this->m_numberOfYChunks = 0;
184         this->m_cachedReadOperations.clear();
185         this->m_bTree = NULL;
186 }
187 void ExecutionGroup::determineResolution(unsigned int resolution[])
188 {
189         NodeOperation *operation = this->getOutputNodeOperation();
190         resolution[0] = operation->getWidth();
191         resolution[1] = operation->getHeight();
192         this->setResolution(resolution);
193 }
194
195 void ExecutionGroup::determineNumberOfChunks()
196 {
197         if (this->m_singleThreaded) {
198                 this->m_numberOfXChunks = 1;
199                 this->m_numberOfYChunks = 1;
200                 this->m_numberOfChunks = 1;
201         } 
202         else {
203                 const float chunkSizef = this->m_chunkSize;
204                 this->m_numberOfXChunks = ceil(this->m_width / chunkSizef);
205                 this->m_numberOfYChunks = ceil(this->m_height / chunkSizef);
206                 this->m_numberOfChunks = this->m_numberOfXChunks * this->m_numberOfYChunks;
207         }
208 }
209
210 /**
211  * this method is called for the top execution groups. containing the compositor node or the preview node or the viewer node)
212  */
213 void ExecutionGroup::execute(ExecutionSystem *graph)
214 {
215         CompositorContext& context = graph->getContext();
216         const bNodeTree *bTree = context.getbNodeTree();
217         if (this->m_width == 0 || this->m_height == 0) {return; } /// @note: break out... no pixels to calculate.
218         if (bTree->test_break && bTree->test_break(bTree->tbh)) {return; } /// @note: early break out for blur and preview nodes
219         if (this->m_numberOfChunks == 0) {return; } /// @note: early break out
220         unsigned int chunkNumber;
221
222         this->m_chunksFinished = 0;
223         this->m_bTree = bTree;
224         unsigned int index;
225         unsigned int *chunkOrder = new unsigned int[this->m_numberOfChunks];
226
227         for (chunkNumber = 0; chunkNumber < this->m_numberOfChunks; chunkNumber++) {
228                 chunkOrder[chunkNumber] = chunkNumber;
229         }
230         NodeOperation *operation = this->getOutputNodeOperation();
231         float centerX = 0.5;
232         float centerY = 0.5;
233         OrderOfChunks chunkorder = COM_ORDER_OF_CHUNKS_DEFAULT;
234
235         if (operation->isViewerOperation()) {
236                 ViewerBaseOperation *viewer = (ViewerBaseOperation *)operation;
237                 centerX = viewer->getCenterX();
238                 centerY = viewer->getCenterY();
239                 chunkorder = viewer->getChunkOrder();
240         }
241
242         switch (chunkorder) {
243                 case COM_TO_RANDOM:
244                         for (index = 0; index < 2 * this->m_numberOfChunks; index++) {
245                                 int index1 = rand() % this->m_numberOfChunks;
246                                 int index2 = rand() % this->m_numberOfChunks;
247                                 int s = chunkOrder[index1];
248                                 chunkOrder[index1] = chunkOrder[index2];
249                                 chunkOrder[index2] = s;
250                         }
251                         break;
252                 case COM_TO_CENTER_OUT:
253                 {
254                         ChunkOrderHotspot **hotspots = new ChunkOrderHotspot *[1];
255                         hotspots[0] = new ChunkOrderHotspot(this->m_width * centerX, this->m_height * centerY, 0.0f);
256                         rcti rect;
257                         ChunkOrder *chunkOrders = new ChunkOrder[this->m_numberOfChunks];
258                         for (index = 0; index < this->m_numberOfChunks; index++) {
259                                 determineChunkRect(&rect, index);
260                                 chunkOrders[index].setChunkNumber(index);
261                                 chunkOrders[index].setX(rect.xmin);
262                                 chunkOrders[index].setY(rect.ymin);
263                                 chunkOrders[index].determineDistance(hotspots, 1);
264                         }
265
266                         sort(&chunkOrders[0], &chunkOrders[this->m_numberOfChunks - 1]);
267                         for (index = 0; index < this->m_numberOfChunks; index++) {
268                                 chunkOrder[index] = chunkOrders[index].getChunkNumber();
269                         }
270
271                         delete hotspots[0];
272                         delete[] hotspots;
273                         delete[] chunkOrders;
274                 }
275                 break;
276                 case COM_TO_RULE_OF_THIRDS:
277                 {
278                         ChunkOrderHotspot **hotspots = new ChunkOrderHotspot *[9];
279                         unsigned int tx = this->m_width / 6;
280                         unsigned int ty = this->m_height / 6;
281                         unsigned int mx = this->m_width / 2;
282                         unsigned int my = this->m_height / 2;
283                         unsigned int bx = mx + 2 * tx;
284                         unsigned int by = my + 2 * ty;
285
286                         float addition = this->m_numberOfChunks / COM_RULE_OF_THIRDS_DIVIDER;
287                         hotspots[0] = new ChunkOrderHotspot(mx, my, addition * 0);
288                         hotspots[1] = new ChunkOrderHotspot(tx, my, addition * 1);
289                         hotspots[2] = new ChunkOrderHotspot(bx, my, addition * 2);
290                         hotspots[3] = new ChunkOrderHotspot(bx, by, addition * 3);
291                         hotspots[4] = new ChunkOrderHotspot(tx, ty, addition * 4);
292                         hotspots[5] = new ChunkOrderHotspot(bx, ty, addition * 5);
293                         hotspots[6] = new ChunkOrderHotspot(tx, by, addition * 6);
294                         hotspots[7] = new ChunkOrderHotspot(mx, ty, addition * 7);
295                         hotspots[8] = new ChunkOrderHotspot(mx, by, addition * 8);
296                         rcti rect;
297                         ChunkOrder *chunkOrders = new ChunkOrder[this->m_numberOfChunks];
298                         for (index = 0; index < this->m_numberOfChunks; index++) {
299                                 determineChunkRect(&rect, index);
300                                 chunkOrders[index].setChunkNumber(index);
301                                 chunkOrders[index].setX(rect.xmin);
302                                 chunkOrders[index].setY(rect.ymin);
303                                 chunkOrders[index].determineDistance(hotspots, 9);
304                         }
305
306                         sort(&chunkOrders[0], &chunkOrders[this->m_numberOfChunks]);
307
308                         for (index = 0; index < this->m_numberOfChunks; index++) {
309                                 chunkOrder[index] = chunkOrders[index].getChunkNumber();
310                         }
311
312                         delete hotspots[0];
313                         delete hotspots[1];
314                         delete hotspots[2];
315                         delete hotspots[3];
316                         delete hotspots[4];
317                         delete hotspots[5];
318                         delete hotspots[6];
319                         delete hotspots[7];
320                         delete hotspots[8];
321                         delete[] hotspots;
322                         delete[] chunkOrders;
323                 }
324                 break;
325                 case COM_TO_TOP_DOWN:
326                 default:
327                         break;
328         }
329
330         bool breaked = false;
331         bool finished = false;
332         unsigned int startIndex = 0;
333         const int maxNumberEvaluated = BLI_system_thread_count() * 2;
334
335         while (!finished && !breaked) {
336                 unsigned int index;
337                 bool startEvaluated = false;
338                 finished = true;
339                 int numberEvaluated = 0;
340
341                 for (index = startIndex; index < this->m_numberOfChunks && numberEvaluated < maxNumberEvaluated; index++) {
342                         int chunkNumber = chunkOrder[index];
343                         int yChunk = chunkNumber / this->m_numberOfXChunks;
344                         int xChunk = chunkNumber - (yChunk * this->m_numberOfXChunks);
345                         const ChunkExecutionState state = this->m_chunkExecutionStates[chunkNumber];
346                         if (state == COM_ES_NOT_SCHEDULED) {
347                                 scheduleChunkWhenPossible(graph, xChunk, yChunk);
348                                 finished = false;
349                                 startEvaluated = true;
350                                 numberEvaluated++;
351                         }
352                         else if (state == COM_ES_SCHEDULED) {
353                                 finished = false;
354                                 startEvaluated = true;
355                                 numberEvaluated++;
356                         }
357                         else if (state == COM_ES_EXECUTED && !startEvaluated) {
358                                 startIndex = index + 1;
359                         }
360                 }
361
362                 WorkScheduler::finish();
363
364                 if (bTree->test_break && bTree->test_break(bTree->tbh)) {
365                         breaked = true;
366                 }
367         }
368
369         delete[] chunkOrder;
370 }
371
372 MemoryBuffer **ExecutionGroup::getInputBuffersCPU()
373 {
374         vector<MemoryProxy *> memoryproxies;
375         unsigned int index;
376
377         this->determineDependingMemoryProxies(&memoryproxies);
378         MemoryBuffer **memoryBuffers = new MemoryBuffer *[this->m_cachedMaxReadBufferOffset];
379         for (index = 0; index < this->m_cachedMaxReadBufferOffset; index++) {
380                 memoryBuffers[index] = NULL;
381         }
382         for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
383                 ReadBufferOperation *readOperation = (ReadBufferOperation *)this->m_cachedReadOperations[index];
384                 memoryBuffers[readOperation->getOffset()] = readOperation->getMemoryProxy()->getBuffer();
385         }
386         return memoryBuffers;
387 }
388
389 MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
390 {
391         rcti rect;
392         vector<MemoryProxy *> memoryproxies;
393         unsigned int index;
394         determineChunkRect(&rect, chunkNumber);
395
396         this->determineDependingMemoryProxies(&memoryproxies);
397         MemoryBuffer **memoryBuffers = new MemoryBuffer *[this->m_cachedMaxReadBufferOffset];
398         for (index = 0; index < this->m_cachedMaxReadBufferOffset; index++) {
399                 memoryBuffers[index] = NULL;
400         }
401         rcti output;
402         for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
403                 ReadBufferOperation *readOperation = (ReadBufferOperation *)this->m_cachedReadOperations[index];
404                 MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
405                 this->determineDependingAreaOfInterest(&rect, readOperation, &output);
406                 MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(memoryProxy, &output);
407                 memoryBuffers[readOperation->getOffset()] = memoryBuffer;
408         }
409         return memoryBuffers;
410 }
411
412 MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy, rcti *rect)
413 {
414         MemoryBuffer *imageBuffer = memoryProxy->getBuffer();
415         MemoryBuffer *result = new MemoryBuffer(memoryProxy, rect);
416         result->copyContentFrom(imageBuffer);
417         return result;
418 }
419
420 void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
421 {
422         if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED)
423                 this->m_chunkExecutionStates[chunkNumber] = COM_ES_EXECUTED;
424         
425         this->m_chunksFinished++;
426         if (memoryBuffers) {
427                 for (unsigned int index = 0; index < this->m_cachedMaxReadBufferOffset; index++) {
428                         MemoryBuffer *buffer = memoryBuffers[index];
429                         if (buffer) {
430                                 if (buffer->isTemporarily()) {
431                                         memoryBuffers[index] = NULL;
432                                         delete buffer;
433                                 }
434                         }
435                 }
436                 delete[] memoryBuffers;
437         }
438         if (this->m_bTree) {
439                 // status report is only performed for top level Execution Groups.
440                 float progress = this->m_chunksFinished;
441                 progress /= this->m_numberOfChunks;
442                 this->m_bTree->progress(this->m_bTree->prh, progress);
443         }
444 }
445
446 inline void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const
447 {
448         if (this->m_singleThreaded) {
449                 BLI_init_rcti(rect, 0, this->m_width, 0, this->m_height);
450         }
451         else {
452                 const unsigned int minx = xChunk * this->m_chunkSize;
453                 const unsigned int miny = yChunk * this->m_chunkSize;
454                 BLI_init_rcti(rect, minx, min(minx + this->m_chunkSize, this->m_width), miny, min(miny + this->m_chunkSize, this->m_height));
455         }
456 }
457
458 void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumber) const
459 {
460         const unsigned int yChunk = chunkNumber / this->m_numberOfXChunks;
461         const unsigned int xChunk = chunkNumber - (yChunk * this->m_numberOfXChunks);
462         determineChunkRect(rect, xChunk, yChunk);
463 }
464
465 MemoryBuffer *ExecutionGroup::allocateOutputBuffer(int chunkNumber, rcti *rect)
466 {
467         // we asume that this method is only called from complex execution groups.
468         NodeOperation *operation = this->getOutputNodeOperation();
469         if (operation->isWriteBufferOperation()) {
470                 WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
471                 MemoryBuffer *buffer = new MemoryBuffer(writeOperation->getMemoryProxy(), rect);
472                 return buffer;
473         }
474         return NULL;
475 }
476
477
478 bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
479 {
480         if (this->m_singleThreaded) {
481                 return scheduleChunkWhenPossible(graph, 0, 0);
482         }
483         // find all chunks inside the rect
484         // determine minxchunk, minychunk, maxxchunk, maxychunk where x and y are chunknumbers
485
486         float chunkSizef = this->m_chunkSize;
487
488         int indexx, indexy;
489         const int minxchunk = floor(area->xmin / chunkSizef);
490         const int maxxchunk = ceil((area->xmax - 1) / chunkSizef);
491         const int minychunk = floor(area->ymin / chunkSizef);
492         const int maxychunk = ceil((area->ymax - 1) / chunkSizef);
493
494         bool result = true;
495         for (indexx = max(minxchunk, 0); indexx < maxxchunk; indexx++) {
496                 for (indexy = max(minychunk, 0); indexy < maxychunk; indexy++) {
497                         if (!scheduleChunkWhenPossible(graph, indexx, indexy)) {
498                                 result = false;
499                         }
500                 }
501         }
502
503         return result;
504 }
505
506 bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
507 {
508         if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_NOT_SCHEDULED) {
509                 this->m_chunkExecutionStates[chunkNumber] = COM_ES_SCHEDULED;
510                 WorkScheduler::schedule(this, chunkNumber);
511                 return true;
512         }
513         return false;
514 }
515
516 bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk)
517 {
518         if (xChunk < 0 || xChunk >= (int)this->m_numberOfXChunks) {
519                 return true;
520         }
521         if (yChunk < 0 || yChunk >= (int)this->m_numberOfYChunks) {
522                 return true;
523         }
524         int chunkNumber = yChunk * this->m_numberOfXChunks + xChunk;
525         // chunk is already executed
526         if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_EXECUTED) {
527                 return true;
528         }
529
530         // chunk is scheduled, but not executed
531         if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED) {
532                 return false;
533         }
534
535         // chunk is nor executed nor scheduled.
536         vector<MemoryProxy *> memoryProxies;
537         this->determineDependingMemoryProxies(&memoryProxies);
538
539         rcti rect;
540         determineChunkRect(&rect, xChunk, yChunk);
541         unsigned int index;
542         bool canBeExecuted = true;
543         rcti area;
544
545         for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
546                 ReadBufferOperation *readOperation = (ReadBufferOperation *)this->m_cachedReadOperations[index];
547                 BLI_init_rcti(&area, 0, 0, 0, 0);
548                 MemoryProxy *memoryProxy = memoryProxies[index];
549                 determineDependingAreaOfInterest(&rect, readOperation, &area);
550                 ExecutionGroup *group = memoryProxy->getExecutor();
551
552                 if (group != NULL) {
553                         if (!group->scheduleAreaWhenPossible(graph, &area)) {
554                                 canBeExecuted = false;
555                         }
556                 }
557                 else {
558                         throw "ERROR";
559                 }
560         }
561
562         if (canBeExecuted) {
563                 scheduleChunk(chunkNumber);
564         }
565
566         return false;
567 }
568
569 void ExecutionGroup::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
570 {
571         this->getOutputNodeOperation()->determineDependingAreaOfInterest(input, readOperation, output);
572 }
573
574 void ExecutionGroup::determineDependingMemoryProxies(vector<MemoryProxy *> *memoryProxies)
575 {
576         unsigned int index;
577         for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
578                 ReadBufferOperation *readOperation = (ReadBufferOperation *) this->m_cachedReadOperations[index];
579                 memoryProxies->push_back(readOperation->getMemoryProxy());
580         }
581 }
582
583 bool ExecutionGroup::isOpenCL()
584 {
585         return this->m_openCL;
586 }