diff options
Diffstat (limited to 'source/blender/compositor/intern/COM_WorkScheduler.cpp')
-rw-r--r-- | source/blender/compositor/intern/COM_WorkScheduler.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cpp b/source/blender/compositor/intern/COM_WorkScheduler.cpp new file mode 100644 index 00000000000..e7c1e00dd96 --- /dev/null +++ b/source/blender/compositor/intern/COM_WorkScheduler.cpp @@ -0,0 +1,313 @@ +/* + * Copyright 2011, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor: + * Jeroen Bakker + * Monique Dewanchand + */ + +#include <list> +#include "COM_WorkScheduler.h" +#include "PIL_time.h" +#include "BLI_threads.h" +#include "COM_CPUDevice.h" +#include "COM_OpenCLDevice.h" +#include "OCL_opencl.h" +#include "stdio.h" +#include "COM_OpenCLKernels.cl.cpp" + +#if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD +#warning COM_CURRENT_THREADING_MODEL COM_TM_NOTHREAD is activated. Use only for debugging. +#elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +#else +#error COM_CURRENT_THREADING_MODEL No threading model selected +#endif + + +/// @brief global state of the WorkScheduler. +static WorkSchedulerState state; +/// @brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is created +static vector<CPUDevice*> cpudevices; + +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +/// @brief list of all thread for every CPUDevice in cpudevices a thread exists +static ListBase cputhreads; +/// @brief all scheduled work for the cpu +static ThreadQueue * cpuqueue; +static ThreadQueue * gpuqueue; +#ifdef COM_OPENCL_ENABLED +static cl_context context; +static cl_program program; +/// @brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice is created +static vector<OpenCLDevice*> gpudevices; +/// @brief list of all thread for every GPUDevice in cpudevices a thread exists +static ListBase gputhreads; +/// @brief all scheduled work for the gpu +#ifdef COM_OPENCL_ENABLED +static bool openclActive = false; +#endif +#endif +#endif + + +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +void* WorkScheduler::thread_execute_cpu(void* data) { + bool continueLoop = true; + Device* device = (Device*)data; + while (continueLoop) { + WorkPackage* work = (WorkPackage*)BLI_thread_queue_pop(cpuqueue); + if (work) { + device->execute(work); + delete work; + } + PIL_sleep_ms(10); + + if (WorkScheduler::isStopping()) { + continueLoop = false; + } + } + return NULL; +} + +void* WorkScheduler::thread_execute_gpu(void* data) { + bool continueLoop = true; + Device* device = (Device*)data; + while (continueLoop) { + WorkPackage* work = (WorkPackage*)BLI_thread_queue_pop(gpuqueue); + if (work) { + device->execute(work); + delete work; + } + PIL_sleep_ms(10); + + if (WorkScheduler::isStopping()) { + continueLoop = false; + } + } + return NULL; +} + +bool WorkScheduler::isStopping() {return state == COM_WSS_STOPPING;} +#endif + + + +void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber) { + WorkPackage* package = new WorkPackage(group, chunkNumber); +#if COM_CURRENT_THREADING_MODEL == COM_TM_NOTHREAD + CPUDevice device; + device.execute(package); + delete package; +#elif COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +#ifdef COM_OPENCL_ENABLED + if (group->isOpenCL() && openclActive){ + BLI_thread_queue_push(gpuqueue, package); + } else{ + BLI_thread_queue_push(cpuqueue, package); + } +#else + BLI_thread_queue_push(cpuqueue, package); +#endif +#endif +} + +void WorkScheduler::start(CompositorContext &context) { +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE + unsigned int index; + cpuqueue = BLI_thread_queue_init(); + BLI_thread_queue_nowait(cpuqueue); + BLI_init_threads(&cputhreads, thread_execute_cpu, cpudevices.size()); + for (index = 0 ; index < cpudevices.size() ; index ++) { + Device* device = cpudevices[index]; + BLI_insert_thread(&cputhreads, device); + } +#ifdef COM_OPENCL_ENABLED + if (context.getHasActiveOpenCLDevices()) { + gpuqueue = BLI_thread_queue_init(); + BLI_thread_queue_nowait(gpuqueue); + BLI_init_threads(&gputhreads, thread_execute_gpu, gpudevices.size()); + for (index = 0 ; index < gpudevices.size() ; index ++) { + Device* device = gpudevices[index]; + BLI_insert_thread(&gputhreads, device); + } + openclActive = true; + } else { + openclActive = false; + } +#endif +#endif + state = COM_WSS_STARTED; +} +void WorkScheduler::finish() { +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +#ifdef COM_OPENCL_ENABLED + if (openclActive) { + while (BLI_thread_queue_size(gpuqueue) + BLI_thread_queue_size(cpuqueue) > 0) { + PIL_sleep_ms(10); + } + } else { + while (BLI_thread_queue_size(cpuqueue) > 0) { + PIL_sleep_ms(10); + } + } +#else + while (BLI_thread_queue_size(cpuqueue) > 0) { + PIL_sleep_ms(10); + } +#endif +#endif +} +void WorkScheduler::stop() { + state = COM_WSS_STOPPING; +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE + BLI_end_threads(&cputhreads); + BLI_thread_queue_free(cpuqueue); + cpuqueue = NULL; +#ifdef COM_OPENCL_ENABLED + if (openclActive) { + BLI_end_threads(&gputhreads); + BLI_thread_queue_free(gpuqueue); + gpuqueue = NULL; + } +#endif +#endif + state = COM_WSS_STOPPED; +} + +bool WorkScheduler::hasGPUDevices() { +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE +#ifdef COM_OPENCL_ENABLED + return gpudevices.size()>0; +#else + return 0; +#endif +#else + return 0; +#endif +} + +extern void clContextError(const char *errinfo, const void *private_info, size_t cb, void *user_data) { + printf("OPENCL error: %s\n", errinfo); +} + +void WorkScheduler::initialize() { + state = COM_WSS_UNKNOWN; + +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE + int numberOfCPUThreads = BLI_system_thread_count(); + + for (int index = 0 ; index < numberOfCPUThreads ; index ++) { + CPUDevice *device = new CPUDevice(); + device->initialize(); + cpudevices.push_back(device); + } +#ifdef COM_OPENCL_ENABLED + context = NULL; + program = NULL; + if (clCreateContextFromType) { + cl_uint numberOfPlatforms; + cl_int error; + error = clGetPlatformIDs(0, 0, &numberOfPlatforms); + if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } + printf("%d number of platforms\n", numberOfPlatforms); + cl_platform_id *platforms = new cl_platform_id[numberOfPlatforms]; + error = clGetPlatformIDs(numberOfPlatforms, platforms, 0); + unsigned int indexPlatform; + cl_uint totalNumberOfDevices = 0; + for (indexPlatform = 0 ; indexPlatform < numberOfPlatforms ; indexPlatform ++) { + cl_platform_id platform = platforms[indexPlatform]; + cl_uint numberOfDevices; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, 0, &numberOfDevices); + totalNumberOfDevices += numberOfDevices; + } + + cl_device_id *cldevices = new cl_device_id[totalNumberOfDevices]; + unsigned int numberOfDevicesReceived = 0; + for (indexPlatform = 0 ; indexPlatform < numberOfPlatforms ; indexPlatform ++) { + cl_platform_id platform = platforms[indexPlatform]; + cl_uint numberOfDevices; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, 0, &numberOfDevices); + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numberOfDevices, cldevices+numberOfDevicesReceived*sizeof (cl_device_id), 0); + numberOfDevicesReceived += numberOfDevices; + } + context = clCreateContext(NULL, totalNumberOfDevices, cldevices, clContextError, NULL, &error); + if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } + program = clCreateProgramWithSource(context, 1, &sourcecode, 0, &error); + error = clBuildProgram(program, totalNumberOfDevices, cldevices, 0, 0, 0); + if (error != CL_SUCCESS) { + cl_int error2; + size_t ret_val_size; + printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); + error2 = clGetProgramBuildInfo(program, cldevices[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size); + if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } + char* build_log = new char[ret_val_size+1]; + error2 = clGetProgramBuildInfo(program, cldevices[0], CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL); + if (error2 != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } + build_log[ret_val_size] = '\0'; + printf("%s", build_log); + delete build_log; + + } + unsigned int indexDevices; + for (indexDevices = 0 ; indexDevices < totalNumberOfDevices ; indexDevices ++) { + cl_device_id device = cldevices[indexDevices]; + OpenCLDevice* clDevice = new OpenCLDevice(context, device, program); + clDevice->initialize(), + gpudevices.push_back(clDevice); + char resultString[32]; + error = clGetDeviceInfo(device, CL_DEVICE_NAME, 32, resultString, 0); + printf("OPENCL_DEVICE: %s, ", resultString); + error = clGetDeviceInfo(device, CL_DEVICE_VENDOR, 32, resultString, 0); + printf("%s\n", resultString); + } + delete cldevices; + delete platforms; + } +#endif +#endif + + state = COM_WSS_INITIALIZED; +} + +void WorkScheduler::deinitialize() { +#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE + Device* device; + while(cpudevices.size()>0) { + device = cpudevices.back(); + cpudevices.pop_back(); + device->deinitialize(); + delete device; + } +#ifdef COM_OPENCL_ENABLED + while(gpudevices.size()>0) { + device = gpudevices.back(); + gpudevices.pop_back(); + device->deinitialize(); + delete device; + } + if (program) { + clReleaseProgram(program); + program = NULL; + } + if (context) { + clReleaseContext(context); + context = NULL; + } +#endif +#endif + state = COM_WSS_DEINITIALIZED; +} |