compositor: replace C++ new/delete with guardedalloc.
[blender.git] / source / blender / compositor / intern / COM_WorkScheduler.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 <list>
24 #include <stdio.h>
25
26 #include "BKE_global.h"
27
28 #include "COM_WorkScheduler.h"
29 #include "COM_CPUDevice.h"
30 #include "COM_OpenCLDevice.h"
31 #include "COM_OpenCLKernels.cl.h"
32 #include "OCL_opencl.h"
33 #include "COM_WriteBufferOperation.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "PIL_time.h"
38 #include "BLI_threads.h"
39
40 #if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD
41 #  ifndef DEBUG  /* test this so we dont get warnings in debug builds */
42 #    warning COM_CURRENT_THREADING_MODEL COM_TM_NOTHREAD is activated. Use only for debugging.
43 #  endif
44 #elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
45    /* do nothing - default */
46 #else
47 #  error COM_CURRENT_THREADING_MODEL No threading model selected
48 #endif
49
50
51 /// @brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is created
52 static vector<CPUDevice *> g_cpudevices;
53
54 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
55 /// @brief list of all thread for every CPUDevice in cpudevices a thread exists
56 static ListBase g_cputhreads;
57 /// @brief all scheduled work for the cpu
58 static ThreadQueue *g_cpuqueue;
59 static ThreadQueue *g_gpuqueue;
60 #ifdef COM_OPENCL_ENABLED
61 static cl_context g_context;
62 static cl_program g_program;
63 /// @brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice is created
64 static vector<OpenCLDevice *> g_gpudevices;
65 /// @brief list of all thread for every GPUDevice in cpudevices a thread exists
66 static ListBase g_gputhreads;
67 /// @brief all scheduled work for the gpu
68 #ifdef COM_OPENCL_ENABLED
69 static bool g_openclActive = false;
70 #endif
71 #endif
72 #endif
73
74 #define MAX_HIGHLIGHT 8
75 extern "C" {
76 int g_highlightIndex;
77 void **g_highlightedNodes;
78 void **g_highlightedNodesRead;
79
80 #define HIGHLIGHT(wp) \
81 { \
82         ExecutionGroup *group = wp->getExecutionGroup(); \
83         if (group->isComplex()) { \
84                 NodeOperation *operation = group->getOutputNodeOperation(); \
85                 if (operation->isWriteBufferOperation()) { \
86                         WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation; \
87                         NodeOperation *complexOperation = writeOperation->getInput(); \
88                         bNode *node = complexOperation->getbNode(); \
89                         if (node) { \
90                                 if (node->original) { \
91                                         node = node->original; \
92                                 } \
93                                 if (g_highlightIndex < MAX_HIGHLIGHT) { \
94                                         g_highlightedNodes[g_highlightIndex++] = node; \
95                                 } \
96                         } \
97                 } \
98         } \
99 }
100
101 void COM_startReadHighlights()
102 {
103         if (g_highlightedNodesRead) {
104                 MEM_freeN(g_highlightedNodesRead);
105         }
106         
107         g_highlightedNodesRead = g_highlightedNodes;
108         g_highlightedNodes = (void **)MEM_callocN(sizeof(void *) * MAX_HIGHLIGHT, __func__);
109         g_highlightIndex = 0;
110 }
111
112 int COM_isHighlightedbNode(bNode *bnode)
113 {
114         if (!g_highlightedNodesRead) {
115                 return false;
116         }
117
118         for (int i = 0 ; i < MAX_HIGHLIGHT; i++) {
119                 void *p = g_highlightedNodesRead[i];
120                 if (!p) return false;
121                 if (p == bnode) return true;
122         }
123         return false;
124 }
125 } // end extern "C"
126
127 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
128 void *WorkScheduler::thread_execute_cpu(void *data)
129 {
130         Device *device = (Device *)data;
131         WorkPackage *work;
132         
133         while ((work = (WorkPackage *)BLI_thread_queue_pop(g_cpuqueue))) {
134                 HIGHLIGHT(work);
135                 device->execute(work);
136                 delete work;
137         }
138         
139         return NULL;
140 }
141
142 void *WorkScheduler::thread_execute_gpu(void *data)
143 {
144         Device *device = (Device *)data;
145         WorkPackage *work;
146         
147         while ((work = (WorkPackage *)BLI_thread_queue_pop(g_gpuqueue))) {
148                 HIGHLIGHT(work);
149                 device->execute(work);
150                 delete work;
151         }
152         
153         return NULL;
154 }
155 #endif
156
157
158
159 void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber)
160 {
161         WorkPackage *package = new WorkPackage(group, chunkNumber);
162 #if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD
163         CPUDevice device;
164         device.execute(package);
165         delete package;
166 #elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
167 #ifdef COM_OPENCL_ENABLED
168         if (group->isOpenCL() && g_openclActive) {
169                 BLI_thread_queue_push(g_gpuqueue, package);
170         }
171         else {
172                 BLI_thread_queue_push(g_cpuqueue, package);
173         }
174 #else
175         BLI_thread_queue_push(cpuqueue, package);
176 #endif
177 #endif
178 }
179
180 void WorkScheduler::start(CompositorContext &context)
181 {
182 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
183         unsigned int index;
184         g_cpuqueue = BLI_thread_queue_init();
185         BLI_init_threads(&g_cputhreads, thread_execute_cpu, g_cpudevices.size());
186         for (index = 0; index < g_cpudevices.size(); index++) {
187                 Device *device = g_cpudevices[index];
188                 BLI_insert_thread(&g_cputhreads, device);
189         }
190 #ifdef COM_OPENCL_ENABLED
191         if (context.getHasActiveOpenCLDevices()) {
192                 g_gpuqueue = BLI_thread_queue_init();
193                 BLI_init_threads(&g_gputhreads, thread_execute_gpu, g_gpudevices.size());
194                 for (index = 0; index < g_gpudevices.size(); index++) {
195                         Device *device = g_gpudevices[index];
196                         BLI_insert_thread(&g_gputhreads, device);
197                 }
198                 g_openclActive = true;
199         }
200         else {
201                 g_openclActive = false;
202         }
203 #endif
204 #endif
205 }
206 void WorkScheduler::finish()
207 {
208 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
209 #ifdef COM_OPENCL_ENABLED
210         if (g_openclActive) {
211                 BLI_thread_queue_wait_finish(g_gpuqueue);
212                 BLI_thread_queue_wait_finish(g_cpuqueue);
213         }
214         else {
215                 BLI_thread_queue_wait_finish(g_cpuqueue);
216         }
217 #else
218         BLI_thread_queue_wait_finish(cpuqueue);
219 #endif
220 #endif
221 }
222 void WorkScheduler::stop()
223 {
224 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
225         BLI_thread_queue_nowait(g_cpuqueue);
226         BLI_end_threads(&g_cputhreads);
227         BLI_thread_queue_free(g_cpuqueue);
228         g_cpuqueue = NULL;
229 #ifdef COM_OPENCL_ENABLED
230         if (g_openclActive) {
231                 BLI_thread_queue_nowait(g_gpuqueue);
232                 BLI_end_threads(&g_gputhreads);
233                 BLI_thread_queue_free(g_gpuqueue);
234                 g_gpuqueue = NULL;
235         }
236 #endif
237 #endif
238 }
239
240 bool WorkScheduler::hasGPUDevices()
241 {
242 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
243 #ifdef COM_OPENCL_ENABLED
244         return g_gpudevices.size() > 0;
245 #else
246         return 0;
247 #endif
248 #else
249         return 0;
250 #endif
251 }
252
253 extern void clContextError(const char *errinfo, const void *private_info, size_t cb, void *user_data)
254 {
255         printf("OPENCL error: %s\n", errinfo);
256 }
257
258 void WorkScheduler::initialize()
259 {
260         g_highlightedNodesRead = NULL;
261         g_highlightedNodes = NULL;
262         COM_startReadHighlights();
263 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
264         int numberOfCPUThreads = BLI_system_thread_count();
265
266         for (int index = 0; index < numberOfCPUThreads; index++) {
267                 CPUDevice *device = new CPUDevice();
268                 device->initialize();
269                 g_cpudevices.push_back(device);
270         }
271 #ifdef COM_OPENCL_ENABLED
272         g_context = NULL;
273         g_program = NULL;
274         if (clCreateContextFromType) {
275                 cl_uint numberOfPlatforms = 0;
276                 cl_int error;
277                 error = clGetPlatformIDs(0, 0, &numberOfPlatforms);
278                 if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error));  }
279                 if (G.f & G_DEBUG) printf("%d number of platforms\n", numberOfPlatforms);
280                 cl_platform_id *platforms = (cl_platform_id *)MEM_mallocN(sizeof(cl_platform_id) * numberOfPlatforms, __func__);
281                 error = clGetPlatformIDs(numberOfPlatforms, platforms, 0);
282                 unsigned int indexPlatform;
283                 for (indexPlatform = 0; indexPlatform < numberOfPlatforms; indexPlatform++) {
284                         cl_platform_id platform = platforms[indexPlatform];
285                         cl_uint numberOfDevices = 0;
286                         clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, 0, &numberOfDevices);
287                         if (numberOfDevices > 0) {
288                                 cl_device_id *cldevices = (cl_device_id *)MEM_mallocN(sizeof(cl_device_id) * numberOfDevices, __func__);
289                                 clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numberOfDevices, cldevices, 0);
290
291                                 g_context = clCreateContext(NULL, numberOfDevices, cldevices, clContextError, NULL, &error);
292                                 if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error));  }
293                                 const char *cl_str[2] = {datatoc_COM_OpenCLKernels_cl, NULL};
294                                 g_program = clCreateProgramWithSource(g_context, 1, cl_str, 0, &error);
295                                 error = clBuildProgram(g_program, numberOfDevices, cldevices, 0, 0, 0);
296                                 if (error != CL_SUCCESS) { 
297                                         cl_int error2;
298                                         size_t ret_val_size = 0;
299                                         printf("CLERROR[%d]: %s\n", error, clewErrorString(error));     
300                                         error2 = clGetProgramBuildInfo(g_program, cldevices[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
301                                         if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
302                                         char *build_log = (char *)MEM_mallocN(sizeof(char) * ret_val_size + 1, __func__);
303                                         error2 = clGetProgramBuildInfo(g_program, cldevices[0], CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);
304                                         if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
305                                         build_log[ret_val_size] = '\0';
306                                         printf("%s", build_log);
307                                         MEM_freeN(build_log);
308                                 }
309                                 else {
310                                         unsigned int indexDevices;
311                                         for (indexDevices = 0; indexDevices < numberOfDevices; indexDevices++) {
312                                                 cl_device_id device = cldevices[indexDevices];
313                                                 cl_int vendorID = 0;
314                                                 cl_int error2 = clGetDeviceInfo(device, CL_DEVICE_VENDOR_ID, sizeof(cl_int), &vendorID, NULL);
315                                                 if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2)); }
316                                                 OpenCLDevice *clDevice = new OpenCLDevice(g_context, device, g_program, vendorID);
317                                                 clDevice->initialize();
318                                                 g_gpudevices.push_back(clDevice);
319                                         }
320                                 }
321                                 MEM_freeN(cldevices);
322                         }
323                 }
324                 MEM_freeN(platforms);
325         }
326 #endif
327 #endif
328 }
329
330 void WorkScheduler::deinitialize()
331 {
332 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
333         Device *device;
334         while (g_cpudevices.size() > 0) {
335                 device = g_cpudevices.back();
336                 g_cpudevices.pop_back();
337                 device->deinitialize();
338                 delete device;
339         }
340 #ifdef COM_OPENCL_ENABLED
341         while (g_gpudevices.size() > 0) {
342                 device = g_gpudevices.back();
343                 g_gpudevices.pop_back();
344                 device->deinitialize();
345                 delete device;
346         }
347         if (g_program) {
348                 clReleaseProgram(g_program);
349                 g_program = NULL;
350         }
351         if (g_context) {
352                 clReleaseContext(g_context);
353                 g_context = NULL;
354         }
355 #endif
356 #endif
357 }
358