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