1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2011, Blender Foundation.
17  */
18 
19 #include <algorithm>
20 #include <math.h>
21 #include <sstream>
22 #include <stdlib.h>
23 
24 #include "atomic_ops.h"
25 
26 #include "COM_ChunkOrder.h"
27 #include "COM_Debug.h"
28 #include "COM_ExecutionGroup.h"
29 #include "COM_ExecutionSystem.h"
30 #include "COM_ReadBufferOperation.h"
31 #include "COM_ViewerOperation.h"
32 #include "COM_WorkScheduler.h"
33 #include "COM_WriteBufferOperation.h"
34 #include "COM_defines.h"
35 
36 #include "BLI_math.h"
37 #include "BLI_string.h"
38 #include "BLT_translation.h"
39 #include "MEM_guardedalloc.h"
40 #include "PIL_time.h"
41 #include "WM_api.h"
42 #include "WM_types.h"
43 
ExecutionGroup()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   BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
61   this->m_executionStartTime = 0;
62 }
63 
getRenderPriotrity()64 CompositorPriority ExecutionGroup::getRenderPriotrity()
65 {
66   return this->getOutputOperation()->getRenderPriority();
67 }
68 
canContainOperation(NodeOperation * operation)69 bool ExecutionGroup::canContainOperation(NodeOperation *operation)
70 {
71   if (!this->m_initialized) {
72     return true;
73   }
74 
75   if (operation->isReadBufferOperation()) {
76     return true;
77   }
78   if (operation->isWriteBufferOperation()) {
79     return false;
80   }
81   if (operation->isSetOperation()) {
82     return true;
83   }
84 
85   /* complex groups don't allow further ops (except read buffer and values, see above) */
86   if (m_complex) {
87     return false;
88   }
89   /* complex ops can't be added to other groups (except their own, which they initialize, see
90    * above) */
91   if (operation->isComplex()) {
92     return false;
93   }
94 
95   return true;
96 }
97 
addOperation(NodeOperation * operation)98 bool ExecutionGroup::addOperation(NodeOperation *operation)
99 {
100   if (!canContainOperation(operation)) {
101     return false;
102   }
103 
104   if (!operation->isReadBufferOperation() && !operation->isWriteBufferOperation()) {
105     m_complex = operation->isComplex();
106     m_openCL = operation->isOpenCL();
107     m_singleThreaded = operation->isSingleThreaded();
108     m_initialized = true;
109   }
110 
111   m_operations.push_back(operation);
112 
113   return true;
114 }
115 
getOutputOperation() const116 NodeOperation *ExecutionGroup::getOutputOperation() const
117 {
118   return this
119       ->m_operations[0]; /* the first operation of the group is always the output operation. */
120 }
121 
initExecution()122 void ExecutionGroup::initExecution()
123 {
124   if (this->m_chunkExecutionStates != NULL) {
125     MEM_freeN(this->m_chunkExecutionStates);
126   }
127   unsigned int index;
128   determineNumberOfChunks();
129 
130   this->m_chunkExecutionStates = NULL;
131   if (this->m_numberOfChunks != 0) {
132     this->m_chunkExecutionStates = (ChunkExecutionState *)MEM_mallocN(
133         sizeof(ChunkExecutionState) * this->m_numberOfChunks, __func__);
134     for (index = 0; index < this->m_numberOfChunks; index++) {
135       this->m_chunkExecutionStates[index] = COM_ES_NOT_SCHEDULED;
136     }
137   }
138 
139   unsigned int maxNumber = 0;
140 
141   for (index = 0; index < this->m_operations.size(); index++) {
142     NodeOperation *operation = this->m_operations[index];
143     if (operation->isReadBufferOperation()) {
144       ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
145       this->m_cachedReadOperations.push_back(readOperation);
146       maxNumber = max(maxNumber, readOperation->getOffset());
147     }
148   }
149   maxNumber++;
150   this->m_cachedMaxReadBufferOffset = maxNumber;
151 }
152 
deinitExecution()153 void ExecutionGroup::deinitExecution()
154 {
155   if (this->m_chunkExecutionStates != NULL) {
156     MEM_freeN(this->m_chunkExecutionStates);
157     this->m_chunkExecutionStates = NULL;
158   }
159   this->m_numberOfChunks = 0;
160   this->m_numberOfXChunks = 0;
161   this->m_numberOfYChunks = 0;
162   this->m_cachedReadOperations.clear();
163   this->m_bTree = NULL;
164 }
determineResolution(unsigned int resolution[2])165 void ExecutionGroup::determineResolution(unsigned int resolution[2])
166 {
167   NodeOperation *operation = this->getOutputOperation();
168   resolution[0] = operation->getWidth();
169   resolution[1] = operation->getHeight();
170   this->setResolution(resolution);
171   BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
172 }
173 
determineNumberOfChunks()174 void ExecutionGroup::determineNumberOfChunks()
175 {
176   if (this->m_singleThreaded) {
177     this->m_numberOfXChunks = 1;
178     this->m_numberOfYChunks = 1;
179     this->m_numberOfChunks = 1;
180   }
181   else {
182     const float chunkSizef = this->m_chunkSize;
183     const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
184     const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
185     this->m_numberOfXChunks = ceil(border_width / chunkSizef);
186     this->m_numberOfYChunks = ceil(border_height / chunkSizef);
187     this->m_numberOfChunks = this->m_numberOfXChunks * this->m_numberOfYChunks;
188   }
189 }
190 
191 /**
192  * this method is called for the top execution groups. containing the compositor node or the
193  * preview node or the viewer node)
194  */
execute(ExecutionSystem * graph)195 void ExecutionGroup::execute(ExecutionSystem *graph)
196 {
197   const CompositorContext &context = graph->getContext();
198   const bNodeTree *bTree = context.getbNodeTree();
199   if (this->m_width == 0 || this->m_height == 0) {
200     return;
201   } /** \note Break out... no pixels to calculate. */
202   if (bTree->test_break && bTree->test_break(bTree->tbh)) {
203     return;
204   } /** \note Early break out for blur and preview nodes. */
205   if (this->m_numberOfChunks == 0) {
206     return;
207   } /** \note Early break out. */
208   unsigned int chunkNumber;
209 
210   this->m_executionStartTime = PIL_check_seconds_timer();
211 
212   this->m_chunksFinished = 0;
213   this->m_bTree = bTree;
214   unsigned int index;
215   unsigned int *chunkOrder = (unsigned int *)MEM_mallocN(
216       sizeof(unsigned int) * this->m_numberOfChunks, __func__);
217 
218   for (chunkNumber = 0; chunkNumber < this->m_numberOfChunks; chunkNumber++) {
219     chunkOrder[chunkNumber] = chunkNumber;
220   }
221   NodeOperation *operation = this->getOutputOperation();
222   float centerX = 0.5;
223   float centerY = 0.5;
224   OrderOfChunks chunkorder = COM_ORDER_OF_CHUNKS_DEFAULT;
225 
226   if (operation->isViewerOperation()) {
227     ViewerOperation *viewer = (ViewerOperation *)operation;
228     centerX = viewer->getCenterX();
229     centerY = viewer->getCenterY();
230     chunkorder = viewer->getChunkOrder();
231   }
232 
233   const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
234   const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
235 
236   switch (chunkorder) {
237     case COM_TO_RANDOM:
238       for (index = 0; index < 2 * this->m_numberOfChunks; index++) {
239         int index1 = rand() % this->m_numberOfChunks;
240         int index2 = rand() % this->m_numberOfChunks;
241         int s = chunkOrder[index1];
242         chunkOrder[index1] = chunkOrder[index2];
243         chunkOrder[index2] = s;
244       }
245       break;
246     case COM_TO_CENTER_OUT: {
247       ChunkOrderHotspot *hotspots[1];
248       hotspots[0] = new ChunkOrderHotspot(border_width * centerX, border_height * centerY, 0.0f);
249       rcti rect;
250       ChunkOrder *chunkOrders = (ChunkOrder *)MEM_mallocN(
251           sizeof(ChunkOrder) * this->m_numberOfChunks, __func__);
252       for (index = 0; index < this->m_numberOfChunks; index++) {
253         determineChunkRect(&rect, index);
254         chunkOrders[index].setChunkNumber(index);
255         chunkOrders[index].setX(rect.xmin - this->m_viewerBorder.xmin);
256         chunkOrders[index].setY(rect.ymin - this->m_viewerBorder.ymin);
257         chunkOrders[index].determineDistance(hotspots, 1);
258       }
259 
260       std::sort(&chunkOrders[0], &chunkOrders[this->m_numberOfChunks - 1]);
261       for (index = 0; index < this->m_numberOfChunks; index++) {
262         chunkOrder[index] = chunkOrders[index].getChunkNumber();
263       }
264 
265       delete hotspots[0];
266       MEM_freeN(chunkOrders);
267       break;
268     }
269     case COM_TO_RULE_OF_THIRDS: {
270       ChunkOrderHotspot *hotspots[9];
271       unsigned int tx = border_width / 6;
272       unsigned int ty = border_height / 6;
273       unsigned int mx = border_width / 2;
274       unsigned int my = border_height / 2;
275       unsigned int bx = mx + 2 * tx;
276       unsigned int by = my + 2 * ty;
277 
278       float addition = this->m_numberOfChunks / COM_RULE_OF_THIRDS_DIVIDER;
279       hotspots[0] = new ChunkOrderHotspot(mx, my, addition * 0);
280       hotspots[1] = new ChunkOrderHotspot(tx, my, addition * 1);
281       hotspots[2] = new ChunkOrderHotspot(bx, my, addition * 2);
282       hotspots[3] = new ChunkOrderHotspot(bx, by, addition * 3);
283       hotspots[4] = new ChunkOrderHotspot(tx, ty, addition * 4);
284       hotspots[5] = new ChunkOrderHotspot(bx, ty, addition * 5);
285       hotspots[6] = new ChunkOrderHotspot(tx, by, addition * 6);
286       hotspots[7] = new ChunkOrderHotspot(mx, ty, addition * 7);
287       hotspots[8] = new ChunkOrderHotspot(mx, by, addition * 8);
288       rcti rect;
289       ChunkOrder *chunkOrders = (ChunkOrder *)MEM_mallocN(
290           sizeof(ChunkOrder) * this->m_numberOfChunks, __func__);
291       for (index = 0; index < this->m_numberOfChunks; index++) {
292         determineChunkRect(&rect, index);
293         chunkOrders[index].setChunkNumber(index);
294         chunkOrders[index].setX(rect.xmin - this->m_viewerBorder.xmin);
295         chunkOrders[index].setY(rect.ymin - this->m_viewerBorder.ymin);
296         chunkOrders[index].determineDistance(hotspots, 9);
297       }
298 
299       std::sort(&chunkOrders[0], &chunkOrders[this->m_numberOfChunks]);
300 
301       for (index = 0; index < this->m_numberOfChunks; index++) {
302         chunkOrder[index] = chunkOrders[index].getChunkNumber();
303       }
304 
305       delete hotspots[0];
306       delete hotspots[1];
307       delete hotspots[2];
308       delete hotspots[3];
309       delete hotspots[4];
310       delete hotspots[5];
311       delete hotspots[6];
312       delete hotspots[7];
313       delete hotspots[8];
314       MEM_freeN(chunkOrders);
315       break;
316     }
317     case COM_TO_TOP_DOWN:
318     default:
319       break;
320   }
321 
322   DebugInfo::execution_group_started(this);
323   DebugInfo::graphviz(graph);
324 
325   bool breaked = false;
326   bool finished = false;
327   unsigned int startIndex = 0;
328   const int maxNumberEvaluated = BLI_system_thread_count() * 2;
329 
330   while (!finished && !breaked) {
331     bool startEvaluated = false;
332     finished = true;
333     int numberEvaluated = 0;
334 
335     for (index = startIndex;
336          index < this->m_numberOfChunks && numberEvaluated < maxNumberEvaluated;
337          index++) {
338       chunkNumber = chunkOrder[index];
339       int yChunk = chunkNumber / this->m_numberOfXChunks;
340       int xChunk = chunkNumber - (yChunk * this->m_numberOfXChunks);
341       const ChunkExecutionState state = this->m_chunkExecutionStates[chunkNumber];
342       if (state == COM_ES_NOT_SCHEDULED) {
343         scheduleChunkWhenPossible(graph, xChunk, yChunk);
344         finished = false;
345         startEvaluated = true;
346         numberEvaluated++;
347 
348         if (bTree->update_draw) {
349           bTree->update_draw(bTree->udh);
350         }
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   DebugInfo::execution_group_finished(this);
369   DebugInfo::graphviz(graph);
370 
371   MEM_freeN(chunkOrder);
372 }
373 
getInputBuffersOpenCL(int chunkNumber)374 MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
375 {
376   rcti rect;
377   vector<MemoryProxy *> memoryproxies;
378   unsigned int index;
379   determineChunkRect(&rect, chunkNumber);
380 
381   this->determineDependingMemoryProxies(&memoryproxies);
382   MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
383       sizeof(MemoryBuffer *) * this->m_cachedMaxReadBufferOffset, __func__);
384   rcti output;
385   for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
386     ReadBufferOperation *readOperation =
387         (ReadBufferOperation *)this->m_cachedReadOperations[index];
388     MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
389     this->determineDependingAreaOfInterest(&rect, readOperation, &output);
390     MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
391         memoryProxy, &output);
392     memoryBuffers[readOperation->getOffset()] = memoryBuffer;
393   }
394   return memoryBuffers;
395 }
396 
constructConsolidatedMemoryBuffer(MemoryProxy * memoryProxy,rcti * rect)397 MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy *memoryProxy,
398                                                                 rcti *rect)
399 {
400   MemoryBuffer *imageBuffer = memoryProxy->getBuffer();
401   MemoryBuffer *result = new MemoryBuffer(memoryProxy, rect);
402   result->copyContentFrom(imageBuffer);
403   return result;
404 }
405 
finalizeChunkExecution(int chunkNumber,MemoryBuffer ** memoryBuffers)406 void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
407 {
408   if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED) {
409     this->m_chunkExecutionStates[chunkNumber] = COM_ES_EXECUTED;
410   }
411 
412   atomic_add_and_fetch_u(&this->m_chunksFinished, 1);
413   if (memoryBuffers) {
414     for (unsigned int index = 0; index < this->m_cachedMaxReadBufferOffset; index++) {
415       MemoryBuffer *buffer = memoryBuffers[index];
416       if (buffer) {
417         if (buffer->isTemporarily()) {
418           memoryBuffers[index] = NULL;
419           delete buffer;
420         }
421       }
422     }
423     MEM_freeN(memoryBuffers);
424   }
425   if (this->m_bTree) {
426     // status report is only performed for top level Execution Groups.
427     float progress = this->m_chunksFinished;
428     progress /= this->m_numberOfChunks;
429     this->m_bTree->progress(this->m_bTree->prh, progress);
430 
431     char buf[128];
432     BLI_snprintf(buf,
433                  sizeof(buf),
434                  TIP_("Compositing | Tile %u-%u"),
435                  this->m_chunksFinished,
436                  this->m_numberOfChunks);
437     this->m_bTree->stats_draw(this->m_bTree->sdh, buf);
438   }
439 }
440 
determineChunkRect(rcti * rect,const unsigned int xChunk,const unsigned int yChunk) const441 inline void ExecutionGroup::determineChunkRect(rcti *rect,
442                                                const unsigned int xChunk,
443                                                const unsigned int yChunk) const
444 {
445   const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
446   const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
447 
448   if (this->m_singleThreaded) {
449     BLI_rcti_init(
450         rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
451   }
452   else {
453     const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
454     const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
455     const unsigned int width = min((unsigned int)this->m_viewerBorder.xmax, this->m_width);
456     const unsigned int height = min((unsigned int)this->m_viewerBorder.ymax, this->m_height);
457     BLI_rcti_init(rect,
458                   min(minx, this->m_width),
459                   min(minx + this->m_chunkSize, width),
460                   min(miny, this->m_height),
461                   min(miny + this->m_chunkSize, height));
462   }
463 }
464 
determineChunkRect(rcti * rect,const unsigned int chunkNumber) const465 void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumber) const
466 {
467   const unsigned int yChunk = chunkNumber / this->m_numberOfXChunks;
468   const unsigned int xChunk = chunkNumber - (yChunk * this->m_numberOfXChunks);
469   determineChunkRect(rect, xChunk, yChunk);
470 }
471 
allocateOutputBuffer(int,rcti * rect)472 MemoryBuffer *ExecutionGroup::allocateOutputBuffer(int /*chunkNumber*/, rcti *rect)
473 {
474   // we assume that this method is only called from complex execution groups.
475   NodeOperation *operation = this->getOutputOperation();
476   if (operation->isWriteBufferOperation()) {
477     WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
478     MemoryBuffer *buffer = new MemoryBuffer(writeOperation->getMemoryProxy(), rect);
479     return buffer;
480   }
481   return NULL;
482 }
483 
scheduleAreaWhenPossible(ExecutionSystem * graph,rcti * area)484 bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
485 {
486   if (this->m_singleThreaded) {
487     return scheduleChunkWhenPossible(graph, 0, 0);
488   }
489   // find all chunks inside the rect
490   // determine minxchunk, minychunk, maxxchunk, maxychunk where x and y are chunknumbers
491 
492   int indexx, indexy;
493   int minx = max_ii(area->xmin - m_viewerBorder.xmin, 0);
494   int maxx = min_ii(area->xmax - m_viewerBorder.xmin, m_viewerBorder.xmax - m_viewerBorder.xmin);
495   int miny = max_ii(area->ymin - m_viewerBorder.ymin, 0);
496   int maxy = min_ii(area->ymax - m_viewerBorder.ymin, m_viewerBorder.ymax - m_viewerBorder.ymin);
497   int minxchunk = minx / (int)m_chunkSize;
498   int maxxchunk = (maxx + (int)m_chunkSize - 1) / (int)m_chunkSize;
499   int minychunk = miny / (int)m_chunkSize;
500   int maxychunk = (maxy + (int)m_chunkSize - 1) / (int)m_chunkSize;
501   minxchunk = max_ii(minxchunk, 0);
502   minychunk = max_ii(minychunk, 0);
503   maxxchunk = min_ii(maxxchunk, (int)m_numberOfXChunks);
504   maxychunk = min_ii(maxychunk, (int)m_numberOfYChunks);
505 
506   bool result = true;
507   for (indexx = minxchunk; indexx < maxxchunk; indexx++) {
508     for (indexy = minychunk; indexy < maxychunk; indexy++) {
509       if (!scheduleChunkWhenPossible(graph, indexx, indexy)) {
510         result = false;
511       }
512     }
513   }
514 
515   return result;
516 }
517 
scheduleChunk(unsigned int chunkNumber)518 bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
519 {
520   if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_NOT_SCHEDULED) {
521     this->m_chunkExecutionStates[chunkNumber] = COM_ES_SCHEDULED;
522     WorkScheduler::schedule(this, chunkNumber);
523     return true;
524   }
525   return false;
526 }
527 
scheduleChunkWhenPossible(ExecutionSystem * graph,int xChunk,int yChunk)528 bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk)
529 {
530   if (xChunk < 0 || xChunk >= (int)this->m_numberOfXChunks) {
531     return true;
532   }
533   if (yChunk < 0 || yChunk >= (int)this->m_numberOfYChunks) {
534     return true;
535   }
536   int chunkNumber = yChunk * this->m_numberOfXChunks + xChunk;
537   // chunk is already executed
538   if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_EXECUTED) {
539     return true;
540   }
541 
542   // chunk is scheduled, but not executed
543   if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED) {
544     return false;
545   }
546 
547   // chunk is nor executed nor scheduled.
548   vector<MemoryProxy *> memoryProxies;
549   this->determineDependingMemoryProxies(&memoryProxies);
550 
551   rcti rect;
552   determineChunkRect(&rect, xChunk, yChunk);
553   unsigned int index;
554   bool canBeExecuted = true;
555   rcti area;
556 
557   for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
558     ReadBufferOperation *readOperation =
559         (ReadBufferOperation *)this->m_cachedReadOperations[index];
560     BLI_rcti_init(&area, 0, 0, 0, 0);
561     MemoryProxy *memoryProxy = memoryProxies[index];
562     determineDependingAreaOfInterest(&rect, readOperation, &area);
563     ExecutionGroup *group = memoryProxy->getExecutor();
564 
565     if (group != NULL) {
566       if (!group->scheduleAreaWhenPossible(graph, &area)) {
567         canBeExecuted = false;
568       }
569     }
570     else {
571       throw "ERROR";
572     }
573   }
574 
575   if (canBeExecuted) {
576     scheduleChunk(chunkNumber);
577   }
578 
579   return false;
580 }
581 
determineDependingAreaOfInterest(rcti * input,ReadBufferOperation * readOperation,rcti * output)582 void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
583                                                       ReadBufferOperation *readOperation,
584                                                       rcti *output)
585 {
586   this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
587 }
588 
determineDependingMemoryProxies(vector<MemoryProxy * > * memoryProxies)589 void ExecutionGroup::determineDependingMemoryProxies(vector<MemoryProxy *> *memoryProxies)
590 {
591   unsigned int index;
592   for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
593     ReadBufferOperation *readOperation =
594         (ReadBufferOperation *)this->m_cachedReadOperations[index];
595     memoryProxies->push_back(readOperation->getMemoryProxy());
596   }
597 }
598 
isOpenCL()599 bool ExecutionGroup::isOpenCL()
600 {
601   return this->m_openCL;
602 }
603 
setViewerBorder(float xmin,float xmax,float ymin,float ymax)604 void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
605 {
606   NodeOperation *operation = this->getOutputOperation();
607 
608   if (operation->isViewerOperation() || operation->isPreviewOperation()) {
609     BLI_rcti_init(&this->m_viewerBorder,
610                   xmin * this->m_width,
611                   xmax * this->m_width,
612                   ymin * this->m_height,
613                   ymax * this->m_height);
614   }
615 }
616 
setRenderBorder(float xmin,float xmax,float ymin,float ymax)617 void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
618 {
619   NodeOperation *operation = this->getOutputOperation();
620 
621   if (operation->isOutputOperation(true)) {
622     /* Basically, setting border need to happen for only operations
623      * which operates in render resolution buffers (like compositor
624      * output nodes).
625      *
626      * In this cases adding border will lead to mapping coordinates
627      * from output buffer space to input buffer spaces when executing
628      * operation.
629      *
630      * But nodes like viewer and file output just shall display or
631      * safe the same exact buffer which goes to their input, no need
632      * in any kind of coordinates mapping.
633      */
634 
635     bool operationNeedsBorder = !(operation->isViewerOperation() ||
636                                   operation->isPreviewOperation() ||
637                                   operation->isFileOutputOperation());
638 
639     if (operationNeedsBorder) {
640       BLI_rcti_init(&this->m_viewerBorder,
641                     xmin * this->m_width,
642                     xmax * this->m_width,
643                     ymin * this->m_height,
644                     ymax * this->m_height);
645     }
646   }
647 }
648