2 * Copyright 2011, Blender Foundation.
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.
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.
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.
26 #include "BKE_global.h"
28 #include "COM_compositor.h"
29 #include "COM_WorkScheduler.h"
30 #include "COM_CPUDevice.h"
31 #include "COM_OpenCLDevice.h"
32 #include "COM_OpenCLKernels.cl.h"
33 #include "OCL_opencl.h"
34 #include "COM_WriteBufferOperation.h"
36 #include "MEM_guardedalloc.h"
39 #include "BLI_threads.h"
41 #if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD
42 # ifndef DEBUG /* test this so we dont get warnings in debug builds */
43 # warning COM_CURRENT_THREADING_MODEL COM_TM_NOTHREAD is activated. Use only for debugging.
45 #elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
46 /* do nothing - default */
48 # error COM_CURRENT_THREADING_MODEL No threading model selected
52 /// @brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is created
53 static vector<CPUDevice *> g_cpudevices;
55 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
56 /// @brief list of all thread for every CPUDevice in cpudevices a thread exists
57 static ListBase g_cputhreads;
58 static bool g_cpuInitialized = false;
59 /// @brief all scheduled work for the cpu
60 static ThreadQueue *g_cpuqueue;
61 static ThreadQueue *g_gpuqueue;
62 #ifdef COM_OPENCL_ENABLED
63 static cl_context g_context;
64 static cl_program g_program;
65 /// @brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice is created
66 static vector<OpenCLDevice *> g_gpudevices;
67 /// @brief list of all thread for every GPUDevice in cpudevices a thread exists
68 static ListBase g_gputhreads;
69 /// @brief all scheduled work for the gpu
70 #ifdef COM_OPENCL_ENABLED
71 static bool g_openclActive = false;
72 static bool g_openclInitialized = false;
77 #define MAX_HIGHLIGHT 8
78 static bool g_highlightInitialized = false;
81 void **g_highlightedNodes;
82 void **g_highlightedNodesRead;
84 #define HIGHLIGHT(wp) \
86 ExecutionGroup *group = wp->getExecutionGroup(); \
87 if (group->isComplex()) { \
88 NodeOperation *operation = group->getOutputNodeOperation(); \
89 if (operation->isWriteBufferOperation()) { \
90 WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation; \
91 NodeOperation *complexOperation = writeOperation->getInput(); \
92 bNode *node = complexOperation->getbNode(); \
94 if (node->original) { \
95 node = node->original; \
97 if (g_highlightInitialized && g_highlightedNodes) { \
98 if (g_highlightIndex < MAX_HIGHLIGHT) { \
99 g_highlightedNodes[g_highlightIndex++] = node; \
107 void COM_startReadHighlights()
109 if (!g_highlightInitialized)
114 if (g_highlightedNodesRead)
116 MEM_freeN(g_highlightedNodesRead);
119 g_highlightedNodesRead = g_highlightedNodes;
120 g_highlightedNodes = (void **)MEM_callocN(sizeof(void *) * MAX_HIGHLIGHT, __func__);
121 g_highlightIndex = 0;
124 int COM_isHighlightedbNode(bNode *bnode)
126 if (!g_highlightInitialized) {
130 if (!g_highlightedNodesRead) {
134 for (int i = 0; i < MAX_HIGHLIGHT; i++) {
135 void *p = g_highlightedNodesRead[i];
136 if (!p) return false;
137 if (p == bnode) return true;
143 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
144 void *WorkScheduler::thread_execute_cpu(void *data)
146 Device *device = (Device *)data;
149 while ((work = (WorkPackage *)BLI_thread_queue_pop(g_cpuqueue))) {
151 device->execute(work);
158 void *WorkScheduler::thread_execute_gpu(void *data)
160 Device *device = (Device *)data;
163 while ((work = (WorkPackage *)BLI_thread_queue_pop(g_gpuqueue))) {
165 device->execute(work);
175 void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber)
177 WorkPackage *package = new WorkPackage(group, chunkNumber);
178 #if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD
180 device.execute(package);
182 #elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
183 #ifdef COM_OPENCL_ENABLED
184 if (group->isOpenCL() && g_openclActive) {
185 BLI_thread_queue_push(g_gpuqueue, package);
188 BLI_thread_queue_push(g_cpuqueue, package);
191 BLI_thread_queue_push(cpuqueue, package);
196 void WorkScheduler::start(CompositorContext &context)
198 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
200 g_cpuqueue = BLI_thread_queue_init();
201 BLI_init_threads(&g_cputhreads, thread_execute_cpu, g_cpudevices.size());
202 for (index = 0; index < g_cpudevices.size(); index++) {
203 Device *device = g_cpudevices[index];
204 BLI_insert_thread(&g_cputhreads, device);
206 #ifdef COM_OPENCL_ENABLED
207 if (context.getHasActiveOpenCLDevices()) {
208 g_gpuqueue = BLI_thread_queue_init();
209 BLI_init_threads(&g_gputhreads, thread_execute_gpu, g_gpudevices.size());
210 for (index = 0; index < g_gpudevices.size(); index++) {
211 Device *device = g_gpudevices[index];
212 BLI_insert_thread(&g_gputhreads, device);
214 g_openclActive = true;
217 g_openclActive = false;
222 void WorkScheduler::finish()
224 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
225 #ifdef COM_OPENCL_ENABLED
226 if (g_openclActive) {
227 BLI_thread_queue_wait_finish(g_gpuqueue);
228 BLI_thread_queue_wait_finish(g_cpuqueue);
231 BLI_thread_queue_wait_finish(g_cpuqueue);
234 BLI_thread_queue_wait_finish(cpuqueue);
238 void WorkScheduler::stop()
240 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
241 BLI_thread_queue_nowait(g_cpuqueue);
242 BLI_end_threads(&g_cputhreads);
243 BLI_thread_queue_free(g_cpuqueue);
245 #ifdef COM_OPENCL_ENABLED
246 if (g_openclActive) {
247 BLI_thread_queue_nowait(g_gpuqueue);
248 BLI_end_threads(&g_gputhreads);
249 BLI_thread_queue_free(g_gpuqueue);
256 bool WorkScheduler::hasGPUDevices()
258 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
259 #ifdef COM_OPENCL_ENABLED
260 return g_gpudevices.size() > 0;
269 static void clContextError(const char *errinfo, const void *private_info, size_t cb, void *user_data)
271 printf("OPENCL error: %s\n", errinfo);
274 void WorkScheduler::initialize(bool use_opencl)
276 /* initialize highlighting */
277 if (!g_highlightInitialized) {
278 if (g_highlightedNodesRead) MEM_freeN(g_highlightedNodesRead);
279 if (g_highlightedNodes) MEM_freeN(g_highlightedNodes);
281 g_highlightedNodesRead = NULL;
282 g_highlightedNodes = NULL;
284 COM_startReadHighlights();
286 g_highlightInitialized = true;
289 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
290 /* initialize CPU threads */
291 if (!g_cpuInitialized) {
292 int numberOfCPUThreads = BLI_system_thread_count();
294 for (int index = 0; index < numberOfCPUThreads; index++) {
295 CPUDevice *device = new CPUDevice();
296 device->initialize();
297 g_cpudevices.push_back(device);
300 g_cpuInitialized = true;
303 #ifdef COM_OPENCL_ENABLED
304 /* deinitialize OpenCL GPU's */
305 if (use_opencl && !g_openclInitialized) {
309 OCL_init(); /* this will check and skip if already initialized */
311 if (clCreateContextFromType) {
312 cl_uint numberOfPlatforms = 0;
314 error = clGetPlatformIDs(0, 0, &numberOfPlatforms);
315 if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
316 if (G.f & G_DEBUG) printf("%d number of platforms\n", numberOfPlatforms);
317 cl_platform_id *platforms = (cl_platform_id *)MEM_mallocN(sizeof(cl_platform_id) * numberOfPlatforms, __func__);
318 error = clGetPlatformIDs(numberOfPlatforms, platforms, 0);
319 unsigned int indexPlatform;
320 for (indexPlatform = 0; indexPlatform < numberOfPlatforms; indexPlatform++) {
321 cl_platform_id platform = platforms[indexPlatform];
322 cl_uint numberOfDevices = 0;
323 clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, 0, &numberOfDevices);
324 if (numberOfDevices <= 0)
327 cl_device_id *cldevices = (cl_device_id *)MEM_mallocN(sizeof(cl_device_id) * numberOfDevices, __func__);
328 clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numberOfDevices, cldevices, 0);
330 g_context = clCreateContext(NULL, numberOfDevices, cldevices, clContextError, NULL, &error);
331 if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
332 const char *cl_str[2] = {datatoc_COM_OpenCLKernels_cl, NULL};
333 g_program = clCreateProgramWithSource(g_context, 1, cl_str, 0, &error);
334 error = clBuildProgram(g_program, numberOfDevices, cldevices, 0, 0, 0);
335 if (error != CL_SUCCESS) {
337 size_t ret_val_size = 0;
338 printf("CLERROR[%d]: %s\n", error, clewErrorString(error));
339 error2 = clGetProgramBuildInfo(g_program, cldevices[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
340 if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
341 char *build_log = (char *)MEM_mallocN(sizeof(char) * ret_val_size + 1, __func__);
342 error2 = clGetProgramBuildInfo(g_program, cldevices[0], CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);
343 if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); }
344 build_log[ret_val_size] = '\0';
345 printf("%s", build_log);
346 MEM_freeN(build_log);
349 unsigned int indexDevices;
350 for (indexDevices = 0; indexDevices < numberOfDevices; indexDevices++) {
351 cl_device_id device = cldevices[indexDevices];
353 cl_int error2 = clGetDeviceInfo(device, CL_DEVICE_VENDOR_ID, sizeof(cl_int), &vendorID, NULL);
354 if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2)); }
355 OpenCLDevice *clDevice = new OpenCLDevice(g_context, device, g_program, vendorID);
356 clDevice->initialize();
357 g_gpudevices.push_back(clDevice);
360 MEM_freeN(cldevices);
362 MEM_freeN(platforms);
365 g_openclInitialized = true;
371 void WorkScheduler::deinitialize()
373 #if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
374 /* deinitialize CPU threads */
375 if (g_cpuInitialized) {
377 while (g_cpudevices.size() > 0) {
378 device = g_cpudevices.back();
379 g_cpudevices.pop_back();
380 device->deinitialize();
384 g_cpuInitialized = false;
387 #ifdef COM_OPENCL_ENABLED
388 /* deinitialize OpenCL GPU's */
389 if (g_openclInitialized) {
391 while (g_gpudevices.size() > 0) {
392 device = g_gpudevices.back();
393 g_gpudevices.pop_back();
394 device->deinitialize();
398 clReleaseProgram(g_program);
402 clReleaseContext(g_context);
406 g_openclInitialized = false;
411 /* deinitialize highlighting */
412 if (g_highlightInitialized) {
413 g_highlightInitialized = false;
414 if (g_highlightedNodes) {
415 MEM_freeN(g_highlightedNodes);
416 g_highlightedNodes = NULL;
419 if (g_highlightedNodesRead) {
420 MEM_freeN(g_highlightedNodesRead);
421 g_highlightedNodesRead = NULL;