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