1 #ifndef VIENNACL_OCL_CONTEXT_HPP_
2 #define VIENNACL_OCL_CONTEXT_HPP_
26 #include <OpenCL/cl.h>
57 typedef std::vector< tools::shared_ptr<viennacl::ocl::program> > program_container_type;
61 device_type_(CL_DEVICE_TYPE_DEFAULT),
62 current_device_id_(0),
63 default_device_num_(1),
67 if (std::getenv(
"VIENNACL_CACHE_PATH"))
68 cache_path_ = std::getenv(
"VIENNACL_CACHE_PATH");
78 void cache_path(std::string new_path) { cache_path_ = new_path; }
97 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
98 std::cout <<
"ViennaCL: Setting new device type for context " << h_ << std::endl;
101 device_type_ = dtype;
106 std::vector<viennacl::ocl::device>
const &
devices()
const
115 return devices_[current_device_id_];
121 assert(i < devices_.size() && bool(
"Provided device index out of range!"));
122 current_device_id_ = i;
128 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
129 std::cout <<
"ViennaCL: Setting new current device for context " << h_ << std::endl;
134 if (devices_[i] == d)
137 current_device_id_ = i;
142 std::cerr <<
"ViennaCL: Warning: Could not set device " << d.
name() <<
" for context." << std::endl;
148 assert(!initialized_ &&
bool(
"Device must be added to context before it is initialized!"));
149 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
150 std::cout <<
"ViennaCL: Adding new device to context " << h_ << std::endl;
152 if (std::find(devices_.begin(), devices_.end(), d) == devices_.end())
153 devices_.push_back(d);
159 assert(!initialized_ &&
bool(
"Device must be added to context before it is initialized!"));
198 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
199 std::cout <<
"ViennaCL: Creating memory of size " << size <<
" for context " << h_ <<
" (unsafe, returning cl_mem directly)" << std::endl;
202 flags |= CL_MEM_COPY_HOST_PTR;
204 cl_mem mem = clCreateBuffer(h_.
get(), flags,
size, ptr, &err);
226 template<
typename NumericT,
typename A,
template<
typename,
typename>
class VectorType >
237 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
238 std::cout <<
"ViennaCL: Adding existing queue " << q <<
" for device " << dev <<
" to context " << h_ << std::endl;
242 queues_[dev].back().handle().inc();
248 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
249 std::cout <<
"ViennaCL: Adding new queue for device " << dev <<
" to context " << h_ << std::endl;
252 #ifdef VIENNACL_PROFILING_ENABLED
268 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
269 std::cout <<
"ViennaCL: Getting queue for device " << devices_[current_device_id_].name() <<
" in context " << h_ << std::endl;
270 std::cout <<
"ViennaCL: Current queue id " << current_queue_id_ << std::endl;
273 return queues_[devices_[current_device_id_].id()][current_queue_id_];
278 typedef std::map< cl_device_id, std::vector<viennacl::ocl::command_queue> > QueueContainer;
280 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
281 std::cout <<
"ViennaCL: Getting const queue for device " << devices_[current_device_id_].name() <<
" in context " << h_ << std::endl;
282 std::cout <<
"ViennaCL: Current queue id " << current_queue_id_ << std::endl;
286 QueueContainer::const_iterator it = queues_.find(devices_[current_device_id_].
id());
287 if (it != queues_.end()) {
288 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
289 std::cout <<
"ViennaCL: Queue handle " << (it->second)[current_queue_id_].
handle() << std::endl;
291 return (it->second)[current_queue_id_];
303 if (i >= queues_[dev].
size())
306 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
307 std::cout <<
"ViennaCL: Getting queue " << i <<
" for device " << dev <<
" in context " << h_ << std::endl;
309 unsigned int device_index;
310 for (device_index = 0; device_index < devices_.size(); ++device_index)
312 if (devices_[device_index] == dev)
316 assert(device_index < devices_.size() && bool(
"Device not within context"));
318 return queues_[devices_[device_index].id()][i];
325 return queues_[devices_[current_device_id_].id()][current_queue_id_];
331 assert(i < queues_[devices_[current_device_id_].
id()].
size() &&
bool(
"In class 'context': Provided queue index out of range for device!"));
332 current_queue_id_ = i;
338 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
339 std::cout <<
"ViennaCL: Setting new current queue for context " << h_ << std::endl;
342 typedef std::map< cl_device_id, std::vector<viennacl::ocl::command_queue> > QueueContainer;
346 for (QueueContainer::const_iterator it=queues_.begin(); it != queues_.end(); it++,j++)
348 const std::vector<viennacl::ocl::command_queue> & qv = (it->second);
355 current_device_id_ = j;
356 current_queue_id_ = i;
362 std::cerr <<
"ViennaCL: Warning: Could not set queue " << q.
handle().
get() <<
" for context." << std::endl;
371 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
372 std::cout <<
"ViennaCL: Adding program '" << prog_name <<
"' with cl_program to context " << h_ << std::endl;
374 return *programs_.back();
381 const char * source_text = source.c_str();
385 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
386 std::cout <<
"ViennaCL: Adding program '" << prog_name <<
"' with source to context " << h_ << std::endl;
394 if (cache_path_.size())
396 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
397 std::cout <<
"ViennaCL: Cache at " << cache_path_ << std::endl;
401 for(std::vector< viennacl::ocl::device >::const_iterator it = devices_.begin(); it != devices_.end(); ++it)
402 prefix += it->name() + it->vendor() + it->driver_version();
405 std::ifstream cached((cache_path_+sha1).c_str(),std::ios::binary);
409 std::vector<unsigned char> buffer;
413 cached.read((
char*)(&buffer[0]), std::streamsize(len));
416 cl_device_id devid = devices_[0].id();
417 const unsigned char * bufdata = &buffer[0];
418 temp = clCreateProgramWithBinary(h_.
get(),1,&devid,&len, &bufdata,&status,&err);
425 temp = clCreateProgramWithSource(h_.
get(), 1, (
const char **)&source_text, &source_size, &err);
429 const char * options = build_options_.c_str();
430 err = clBuildProgram(temp, 0, NULL, options, NULL, NULL);
431 #ifndef VIENNACL_BUILD_INFO
432 if (err != CL_SUCCESS)
435 cl_build_status status;
436 clGetProgramBuildInfo(temp, devices_[0].
id(), CL_PROGRAM_BUILD_STATUS,
sizeof(cl_build_status), &status, NULL);
437 std::cout <<
"Build Status = " << status <<
" ( Err = " << err <<
" )" << std::endl;
441 err = clGetProgramBuildInfo(temp, devices_[0].
id(), CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
442 build_log =
new char[ret_val_size+1];
443 err = clGetProgramBuildInfo(temp, devices_[0].
id(), CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);
444 build_log[ret_val_size] =
'\0';
445 std::cout <<
"Log: " << build_log << std::endl;
448 std::cout <<
"Sources: " << source << std::endl;
455 if (cache_path_.size())
459 std::vector<vcl_size_t> sizes(devices_.size());
460 clGetProgramInfo(temp,CL_PROGRAM_BINARY_SIZES,0,NULL,&len);
461 clGetProgramInfo(temp,CL_PROGRAM_BINARY_SIZES,len,(
void*)&sizes[0],NULL);
463 std::vector<unsigned char*> binaries;
464 for (
vcl_size_t i = 0; i < devices_.size(); ++i)
465 binaries.push_back(
new unsigned char[sizes[i]]);
467 clGetProgramInfo(temp,CL_PROGRAM_BINARIES,0,NULL,&len);
468 clGetProgramInfo(temp,CL_PROGRAM_BINARIES,len,&binaries[0],NULL);
471 for(std::vector< viennacl::ocl::device >::const_iterator it = devices_.begin(); it != devices_.end(); ++it)
472 prefix += it->name() + it->vendor() + it->driver_version();
474 std::ofstream cached((cache_path_+sha1).c_str(),std::ios::binary);
476 cached.write((
char*)&sizes[0],
sizeof(
vcl_size_t));
477 cached.write((
char*)binaries[0], std::streamsize(sizes[0]));
479 for (
vcl_size_t i = 0; i < devices_.size(); ++i)
480 delete[] binaries[i];
493 cl_kernel kernels[1024];
494 cl_uint num_kernels_in_prog;
495 err = clCreateKernelsInProgram(prog.
handle().
get(), 1024, kernels, &num_kernels_in_prog);
498 for (cl_uint i=0; i<num_kernels_in_prog; ++i)
500 char kernel_name[128];
501 err = clGetKernelInfo(kernels[i], CL_KERNEL_FUNCTION_NAME, 128, kernel_name, NULL);
502 prog.
add_kernel(kernels[i], std::string(kernel_name));
505 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
506 std::cout <<
"ViennaCL: Stored program '" << programs_.back()->name() <<
"' in context " << h_ << std::endl;
507 std::cout <<
"ViennaCL: There is/are " << programs_.size() <<
" program(s)" << std::endl;
516 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
517 std::cout <<
"ViennaCL: Deleting program '" << name <<
"' from context " << h_ << std::endl;
519 for (program_container_type::iterator it = programs_.begin();
520 it != programs_.end();
523 if ((*it)->name() == name)
534 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
535 std::cout <<
"ViennaCL: Getting program '" << name <<
"' from context " << h_ << std::endl;
536 std::cout <<
"ViennaCL: There are " << programs_.size() <<
" programs" << std::endl;
538 for (program_container_type::iterator it = programs_.begin();
539 it != programs_.end();
543 if ((*it)->name() == name)
546 std::cerr <<
"ViennaCL: Could not find program '" << name <<
"'" << std::endl;
553 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
554 std::cout <<
"ViennaCL: Getting program '" << name <<
"' from context " << h_ << std::endl;
555 std::cout <<
"ViennaCL: There are " << programs_.size() <<
" programs" << std::endl;
557 for (program_container_type::const_iterator it = programs_.begin();
558 it != programs_.end();
562 if ((*it)->name() == name)
565 std::cerr <<
"ViennaCL: Could not find program '" << name <<
"'" << std::endl;
573 for (program_container_type::iterator it = programs_.begin();
574 it != programs_.end();
577 if ((*it)->name() == name)
return true;
585 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
586 std::cout <<
"ViennaCL: Getting program '" <<
id <<
"' from context " << h_ << std::endl;
587 std::cout <<
"ViennaCL: There are " << programs_.size() <<
" programs" << std::endl;
590 if (
id >= programs_.size())
593 return *programs_[id];
625 assert(!initialized_ &&
bool(
"Platform ID must be set before context is initialized!"));
626 pf_index_ = new_index;
632 return h_.
get() < other.h_.
get();
637 return h_.
get() == other.h_.
get();
644 assert(!initialized_ &&
bool(
"ViennaCL FATAL error: Context already created!"));
646 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
647 std::cout <<
"ViennaCL: Initializing new ViennaCL context." << std::endl;
651 std::vector<cl_device_id> device_id_array;
652 if (devices_.empty())
655 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
656 std::cout <<
"ViennaCL: Setting all devices for context..." << std::endl;
660 std::vector<device>
devices = pf.devices(device_type_);
661 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
662 std::cout <<
"ViennaCL: Number of devices for context: " << devices.size() << std::endl;
666 devices_.push_back(devices[i]);
668 if (devices.size() == 0)
670 std::cerr <<
"ViennaCL: FATAL ERROR: No devices of type '";
671 switch (device_type_)
673 case CL_DEVICE_TYPE_CPU: std::cout <<
"CPU";
break;
674 case CL_DEVICE_TYPE_GPU: std::cout <<
"GPU";
break;
675 case CL_DEVICE_TYPE_ACCELERATOR: std::cout <<
"ACCELERATOR";
break;
676 case CL_DEVICE_TYPE_DEFAULT: std::cout <<
"DEFAULT";
break;
678 std::cout <<
"UNKNOWN" << std::endl;
680 std::cout <<
"' found!" << std::endl;
685 for (std::vector< viennacl::ocl::device >::const_iterator iter = devices_.begin();
686 iter != devices_.end();
688 device_id_array.push_back(iter->id());
690 h_ = clCreateContext(0,
691 static_cast<cl_uint>(devices_.size()),
692 &(device_id_array[0]),
697 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
698 std::cout <<
"ViennaCL: Initialization of new ViennaCL context done." << std::endl;
703 void init_existing(cl_context c)
705 assert(!initialized_ &&
bool(
"ViennaCL FATAL error: Context already created!"));
706 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
707 std::cout <<
"ViennaCL: Initialization of ViennaCL context from existing context." << std::endl;
714 if (devices_.empty())
725 assert(temp > 0 &&
bool(
"ViennaCL: FATAL error: Provided context does not contain any devices!"));
726 num_devices =
static_cast<cl_uint
>(temp /
sizeof(cl_device_id));
728 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
729 std::cout <<
"ViennaCL: Reusing context with " << num_devices <<
" devices." << std::endl;
732 std::vector<cl_device_id> device_ids(num_devices);
733 err = clGetContextInfo(h_.
get(), CL_CONTEXT_DEVICES, num_devices *
sizeof(cl_device_id), &(device_ids[0]), NULL);
739 current_device_id_ = 0;
742 #if defined(VIENNACL_DEBUG_ALL) || defined(VIENNACL_DEBUG_CONTEXT)
743 std::cout <<
"ViennaCL: Initialization of ViennaCL context from existing context done." << std::endl;
749 std::string cache_path_;
750 cl_device_type device_type_;
752 std::vector< viennacl::ocl::device > devices_;
755 program_container_type programs_;
756 std::map< cl_device_id, std::vector< viennacl::ocl::command_queue> > queues_;
757 std::string build_options_;
767 assert(p_context_ != NULL &&
bool(
"Pointer to context invalid in viennacl::ocl::program object"));
769 return *kernels_.back();
776 for (kernel_container_type::iterator it = kernels_.begin();
777 it != kernels_.end();
780 if ((*it)->name() == name)
783 std::cerr <<
"ViennaCL: FATAL ERROR: Could not find kernel '" << name <<
"' from program '" << name_ <<
"'" << std::endl;
784 std::cout <<
"Number of kernels in program: " << kernels_.size() << std::endl;
790 inline void viennacl::ocl::kernel::set_work_size_defaults()
792 assert( p_program_ != NULL &&
bool(
"Kernel not initialized, program pointer invalid."));
793 assert( p_context_ != NULL &&
bool(
"Kernel not initialized, context pointer invalid."));
795 if ( (p_context_->current_device().type() == CL_DEVICE_TYPE_GPU)
796 || (p_context_->current_device().type() == CL_DEVICE_TYPE_ACCELERATOR)
799 local_work_size_[0] = 128; local_work_size_[1] = 0; local_work_size_[2] = 0;
800 global_work_size_[0] = 128*128; global_work_size_[1] = 0; global_work_size_[2] = 0;
805 local_work_size_[0] = 1; local_work_size_[1] = 0; local_work_size_[2] = 0;
807 size_type units = p_context_->current_device().max_compute_units();
813 global_work_size_[0] = s * local_work_size_[0]; global_work_size_[1] = 0; global_work_size_[2] = 0;
viennacl::ocl::device const & current_device() const
Returns the current device.
This file provides the forward declarations for the OpenCL layer of ViennaCL.
void switch_queue(vcl_size_t i)
Switches the current device to the i-th device in this context.
viennacl::ocl::kernel & add_kernel(cl_kernel kernel_handle, std::string const &kernel_name)
Adds a kernel to the program.
cl_device_id id() const
Returns the OpenCL device id.
void platform_index(vcl_size_t new_index)
Sets the platform ID of the platform to be used for the context.
program_container_type get_programs()
cl_mem create_memory_without_smart_handle(cl_mem_flags flags, unsigned int size, void *ptr=NULL) const
Creates a memory buffer within the context. Does not wrap the OpenCL handle into the smart-pointer-li...
Represents an OpenCL device within ViennaCL.
void add_queue(cl_device_id dev)
Adds a queue for the given device to the context.
void default_device_type(cl_device_type dtype)
Sets the device type for this context.
std::string build_options() const
Returns the current build option string.
void switch_device(vcl_size_t i)
Switches the current device to the i-th device in this context.
viennacl::ocl::command_queue & get_queue()
Represents an OpenCL kernel within ViennaCL.
viennacl::ocl::program & get_program(std::string const &name)
Returns the program with the provided name.
void build_options(std::string op)
Sets the build option string, which is passed to the OpenCL compiler in subsequent compilations...
bool operator==(context const &other) const
const viennacl::ocl::handle< cl_program > & handle() const
void delete_program(std::string const &name)
Delete the program with the provided name.
cl_device_type default_device_type()
Returns the default device type for the context.
Manages an OpenCL context and provides the respective convenience functions for creating buffers...
A class representing a compute device (e.g. a GPU)
A class representing a command queue.
vcl_size_t program_num()
Returns the number of programs within this context.
vcl_size_t platform_index() const
Returns the platform ID of the platform to be used for the context.
const viennacl::ocl::handle< cl_context > & handle() const
Returns the context handle.
viennacl::ocl::handle< cl_command_queue > const & handle() const
Implementations of command queue representations.
vcl_size_t default_device_num() const
Returns the maximum number of devices to be set up for the context.
#define VIENNACL_ERR_CHECK(err)
Implementation of a shared pointer class (cf. std::shared_ptr, boost::shared_ptr). Will be used until C++11 is widely available.
void init()
Initializes a new context.
viennacl::ocl::program & add_program(cl_program p, std::string const &prog_name)
Adds a program to the context.
const OCL_TYPE & get() const
vcl_size_t size(VectorType const &vec)
Generic routine for obtaining the size of a vector (ViennaCL, uBLAS, etc.)
bool operator<(context const &other) const
Less-than comparable for compatibility with std:map.
Exception thrown if an OpenCL program object handle is invalid (e.g. not initialized).
void inc()
Manually increment the OpenCL reference count. Typically called automatically, but is necessary if us...
void init(cl_context c)
Initializes the context from an existing, user-supplied context.
viennacl::ocl::kernel & get_kernel(std::string const &program_name, std::string const &kernel_name)
Convenience function for retrieving the kernel of a program directly from the context.
Implements an OpenCL program class for ViennaCL.
viennacl::ocl::program & get_program(vcl_size_t id)
Returns the program with the provided id.
std::string cache_path() const
Returns the compiled kernel cache path.
Implementation of a smart-pointer-like class for handling OpenCL handles.
void add_queue(viennacl::ocl::device d)
Adds a queue for the given device to the context.
void add_device(viennacl::ocl::device const &d)
Add a device to the context. Must be done before the context is initialized.
void cache_path(std::string new_path)
Sets the compiled kernel cache path.
Wrapper class for an OpenCL program.
void add_queue(cl_device_id dev, cl_command_queue q)
Adds an existing queue for the given device to the context.
std::string name() const
Device name string.
viennacl::ocl::program const & get_program(std::string const &name) const
void default_device_num(vcl_size_t new_num)
Sets the maximum number of devices to be set up for the context.
Exception thrown if an invalid OpenCL command queue is provided to an OpenCL function.
Error handling for the OpenCL layer of ViennaCL.
void switch_queue(viennacl::ocl::command_queue const &q)
If the supplied command_queue is used within the context, it becomes the current active command_queue...
bool has_program(std::string const &name)
Returns whether the program with the provided name exists or not.
#define VIENNACL_OCL_MAX_DEVICE_NUM
Representation of an OpenCL kernel in ViennaCL.
viennacl::ocl::command_queue & get_queue(cl_device_id dev, vcl_size_t i=0)
Returns the queue with the provided index for the given device.
viennacl::ocl::command_queue const & current_queue()
Returns the current device.
void add_device(cl_device_id d)
Add a device to the context. Must be done before the context is initialized.
viennacl::ocl::kernel & get_kernel(std::string const &name)
Returns the kernel with the provided name.
std::vector< viennacl::ocl::device > const & devices() const
Returns a vector with all devices in this context.
vcl_size_t device_num()
Returns the number of devices within this context.
void switch_device(viennacl::ocl::device const &d)
If the supplied device is used within the context, it becomes the current active device.
viennacl::ocl::handle< cl_mem > create_memory(cl_mem_flags flags, const VectorType< NumericT, A > &buffer) const
Creates a memory buffer within the context initialized from the supplied data.
viennacl::ocl::command_queue const & get_queue() const
viennacl::ocl::program & add_program(std::string const &source, std::string const &prog_name)
Adds a new program with the provided source to the context. Compiles the program and extracts all ker...
viennacl::ocl::handle< cl_mem > create_memory(cl_mem_flags flags, unsigned int size, void *ptr=NULL) const
Creates a memory buffer within the context.