parallel/CLWorkFlow.hpp
00001 /* CLWorkFlow.hpp
00002  *
00003  * Classes for streamlining the work flow with OpenCL objects: command queues,
00004  * kernels, and memory buffers.
00005  *
00006  * Copyright 2011 Jerry Gagelman
00007  * Copyright 2009 David W. Gohara, Ph.D. and MacResearch.org
00008  *
00009  * This program is free software: you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation, either version 3 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00021  */
00022 
00023 #ifndef MULTIQUAD_CLWORKFLOW_HPP
00024 #define MULTIQUAD_CLWORKFLOW_HPP
00025 
00026 #include "CLTools.h"
00027 #include <string>
00028 #include <vector>
00029 #include <sys/stat.h>
00030 #include <cstring>
00031 
00034 class CLContext
00035 {
00036 protected:
00037     cl_context context_;
00038     cl_device_id device_;
00039     
00040 public:
00042     cl_context context() const {return context_;}
00044     cl_device_id device() const {return device_;}
00045     
00049     CLContext(cl_device_type T =CL_DEVICE_TYPE_GPU) 
00050     : context_(NULL), device_(NULL) 
00051     {
00052         int err;
00053         err = clGetDeviceIDs(NULL, T, 1, &device_, NULL);   
00054         context_ = clCreateContext(0, 1, &device_, NULL, NULL, &err);
00055     }
00056     
00057     ~CLContext() 
00058     {
00059         if (context_) clReleaseContext(context_);
00060     }
00061 };
00062 
00072 class CLQueue : public CLContext 
00073 {
00074 protected:
00075     cl_command_queue queue_;
00076     
00077 public:
00079     operator cl_command_queue() const {return queue_;}
00080     
00084     CLQueue(cl_device_type T =CL_DEVICE_TYPE_GPU)
00085     : queue_(NULL), CLContext(T)
00086     {
00087         queue_ = clCreateCommandQueue(context_, device_, 0, NULL);
00088     }
00089 
00090     ~CLQueue()
00091     {
00092         if (queue_) clReleaseCommandQueue(queue_);
00093     }
00094     
00095 };
00096 
00099 class CLKernel
00100 {
00101 protected:
00103     const char* name_;
00104     cl_context context_;
00105     cl_device_id device_;
00106     cl_program program_;
00107     cl_kernel kernel_;
00109     std::vector<std::string> sources;
00110     char adieu[128]; // error string for exceptions
00111     char log[4096]; // string for build log.
00112     
00113 public:
00115     operator cl_kernel() const {return kernel_;}
00117     void add_source(const char* name);
00118     std::string dump_srcs();
00120     void build(const char* options ="\0");
00122     void set_arg(cl_uint n, size_t arg_size, const void* arg_value);
00123     
00125     CLKernel(const char* name, CLContext& C)
00126     : name_(name), context_(C.context()), device_(C.device()), program_(NULL), kernel_(NULL) {}
00127     ~CLKernel()
00128     {
00129         if (program_) clReleaseProgram(program_);
00130         if (kernel_) clReleaseKernel(kernel_);
00131     }
00132 };
00133 
00145 template <typename T>
00146 class CLArrayBuf
00147 {
00148 protected:
00150     cl_command_queue queue_;
00152     size_t length_;
00154     cl_mem buffer_;
00156     size_t bytes_;
00157     char adieu[128]; // error string for throwing exceptions
00158     
00159 public:
00161     operator cl_mem() {return buffer_;}
00163     cl_mem* ptr() {return &buffer_;}
00165     void put(const T* ptr);
00167     void get(T* ptr);
00168     
00170     CLArrayBuf(CLQueue& Q, cl_mem_flags FLAG, size_t len)
00171     : queue_(Q), length_(len), buffer_(NULL)
00172     {
00173         int err;
00174         bytes_ = len * sizeof(T);
00175         buffer_ = clCreateBuffer(Q.context(), FLAG, bytes_, NULL, &err);
00176     }
00177 
00178     ~CLArrayBuf()
00179     {
00180         if (buffer_) clReleaseMemObject(buffer_);
00181     }
00182 };
00183 
00184 /* ---------- CLKernel member definitions ----------------------------------- */
00185 
00186 void CLKernel::add_source(const char* name)
00187 {
00188     struct stat statbuf;
00189     FILE *fh;
00190     std::string source;
00191     
00192     fh = fopen(name, "r");
00193     if (fh == 0) {
00194         std::strcpy(adieu, name);
00195         std::strcat(adieu, " not found.\n");
00196         throw CLExcept((const char*)adieu);
00197     }
00198     
00199     stat(name, &statbuf);
00200     size_t n = statbuf.st_size;
00201     source.resize(n+1);
00202     fread(&source[0], n, 1, fh);
00203     source[n] = '\0';
00204     sources.push_back(source);
00205 }
00206 
00207 std::string CLKernel::dump_srcs()
00208 {
00209     std::string output;
00210     std::vector<std::string>::iterator iter;
00211     for (iter = sources.begin(); iter != sources.end(); ++iter) {
00212         output.append(*iter);
00213         output.append("\n");
00214     }
00215     return output;
00216 }
00217 
00218 void CLKernel::build(const char* options)
00219 {
00220     int err;
00221     size_t ret_size;
00222     const char* ftn_name = " in CLKernel::build";
00223     
00224     size_t count = sources.size();
00225     
00226     if (count) 
00227     {
00228         // first "load" source list into C-array...
00229         char** strings = new char*[count];
00230         for (size_t j = 0; j < count; ++j) {
00231             strings[j] = &sources[j][0];
00232         }
00233         // sources are null-terminated by construction; no "length" argument needed.
00234         program_ = clCreateProgramWithSource(context_, count, (const char**)strings, NULL, &err);
00235         delete[] strings;
00236         if (err != CL_SUCCESS) {
00237             std::strcpy(adieu, CL_err_string(err));
00238             std::strcat(adieu, ftn_name);
00239             throw CLExcept(adieu); 
00240         }
00241         
00242         err = clBuildProgram(program_, 1, &device_, options, NULL, NULL);
00243         err |= clGetProgramBuildInfo(program_, device_, CL_PROGRAM_BUILD_LOG, sizeof(log), log, &ret_size);
00244         if (err != CL_SUCCESS) {
00245             throw CLExcept(log);
00246         }
00247         
00248         kernel_ = clCreateKernel(program_, name_, &err);
00249         if (err != CL_SUCCESS) {
00250             std::strcpy(adieu, CL_err_string(err));
00251             std::strcat(adieu, ftn_name);
00252             throw CLExcept(adieu); 
00253         }
00254     }
00255 }
00256 
00257 void CLKernel::set_arg(cl_uint arg_index, size_t arg_size, const void* arg_value)
00258 {
00259     int err;
00260     const char* ftn_name = " in CLKernel::set_arg";
00261     
00262     err = clSetKernelArg(kernel_, arg_index, arg_size, arg_value);
00263     if (err != CL_SUCCESS) {
00264         std::strcpy(adieu, CL_err_string(err));
00265         std::strcat(adieu, ftn_name);
00266         throw CLExcept(adieu); 
00267     }
00268 }
00269 
00270 /* ---------- CLArrayBuf member definitions --------------------------------- */
00271 
00275 template <typename T>
00276 void CLArrayBuf<T>::put(const T* ptr)
00277 {
00278     int err;
00279     const char* ftn_name = " in CLArrayBuf::put";
00280     
00281     err = clEnqueueWriteBuffer(queue_, buffer_, CL_TRUE, 0, bytes_, ptr, 0, NULL, NULL);
00282     if (err != CL_SUCCESS) {
00283         std::strcpy(adieu, CL_err_string(err));
00284         std::strcat(adieu, ftn_name);
00285         throw CLExcept(adieu); 
00286     }
00287 }
00288 
00292 template <typename T>
00293 void CLArrayBuf<T>::get(T* ptr)
00294 {
00295     int err;
00296     const char* ftn_name = " in CLArrayBuf::get";
00297     
00298     err = clEnqueueReadBuffer(queue_, buffer_, CL_TRUE, 0, bytes_, ptr, 0, NULL, NULL);
00299     if (err != CL_SUCCESS) {
00300         std::strcpy(adieu, CL_err_string(err));
00301         std::strcat(adieu, ftn_name);
00302         throw CLExcept(adieu); 
00303     }
00304 }
00305 
00306 #endif // MULTIQUAD_CLWORKFLOW_HPP
 All Classes Functions Variables