//+------------------------------------------------------------------+ //| OpenCL.mqh | //| Copyright 2016-2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016-2017, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Class for working with OpenCL | //+------------------------------------------------------------------+ class COpenCL { protected: int m_context; int m_program; //--- kernel string m_kernel_names[]; int m_kernels[]; int m_kernels_total; //--- buffers int m_buffers[]; int m_buffers_total; string m_device_extensions; bool m_support_cl_khr_fp64; public: //--- constructor/destructor COpenCL(); ~COpenCL(); //--- get handles int GetContext(void) const { return(m_context); } int GetProgram(void) const { return(m_program); } int GetKernel(const int kernel_index) const; string GetKernelName(const int kernel_index) const; //--- global memory size bool GetGlobalMemorySize(long &global_memory_size); //--- check support working with double bool SupportDouble(void) const { return(m_support_cl_khr_fp64); } //--- initialization and shutdown bool Initialize(const string program,const bool show_log=true); void Shutdown(); //--- set buffers/kernels count bool SetBuffersCount(const int total_buffers); bool SetKernelsCount(const int total_kernels); //--- kernel operations bool KernelCreate(const int kernel_index,const string kernel_name); bool KernelFree(const int kernel_index); //--- buffer operations bool BufferCreate(const int buffer_index,const uint size_in_bytes,const uint flags); bool BufferFree(const int buffer_index); template bool BufferFromArray(const int buffer_index,T &data[],const uint data_array_offset,const uint data_array_count,const uint flags); template bool BufferRead(const int buffer_index,T &data[],const uint cl_buffer_offset,const uint data_array_offset,const uint data_array_count); template bool BufferWrite(const int buffer_index,T &data[],const uint cl_buffer_offset,const uint data_array_offset,const uint data_array_count); //--- set kernel arguments template bool SetArgument(const int kernel_index,const int arg_index,T value); bool SetArgumentBuffer(const int kernel_index,const int arg_index,const int buffer_index); bool SetArgumentLocalMemory(const int kernel_index,const int arg_index,const int local_memory_size); //--- kernel execution bool Execute(const int kernel_index,const int work_dim,const uint &work_offset[],const uint &work_size[]); bool Execute(const int kernel_index,const int work_dim,const uint &work_offset[],const uint &work_size[],const uint &local_work_size[]); }; //+------------------------------------------------------------------+ //| COpenCL class constructor | //+------------------------------------------------------------------+ COpenCL::COpenCL() { m_context=INVALID_HANDLE; m_program=INVALID_HANDLE; m_buffers_total=0; m_kernels_total=0; m_device_extensions=""; m_support_cl_khr_fp64=false; } //+------------------------------------------------------------------+ //| COpenCL class destructor | //+------------------------------------------------------------------+ COpenCL::~COpenCL() { Shutdown(); } //+------------------------------------------------------------------+ //| GetKernel | //+------------------------------------------------------------------+ int COpenCL::GetKernel(const int kernel_index) const { if(m_kernels_total<=0 || kernel_index<0 || kernel_index>=m_kernels_total) return(INVALID_HANDLE); //--- return m_kernels[kernel_index]; } //+------------------------------------------------------------------+ //| GetKernelName | //+------------------------------------------------------------------+ string COpenCL::GetKernelName(const int kernel_index) const { if(m_kernels_total<=0 || kernel_index<0 || kernel_index>=m_kernels_total) return(""); //--- return m_kernel_names[kernel_index]; } //+------------------------------------------------------------------+ //| GetGlobalMemorySize | //+------------------------------------------------------------------+ bool COpenCL::GetGlobalMemorySize(long &global_memory_size) { if(m_context==INVALID_HANDLE) return(false); //--- get global memory size global_memory_size=CLGetInfoInteger(m_context,CL_DEVICE_GLOBAL_MEM_SIZE); if(global_memory_size==-1) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Initialize | //+------------------------------------------------------------------+ bool COpenCL::Initialize(const string program,const bool show_log=true) { if((m_context=CLContextCreate(CL_USE_ANY))==INVALID_HANDLE) { Print("OpenCL not found. Error code=",GetLastError()); return(false); } //--- check support working with doubles (cl_khr_fp64) if(CLGetInfoString(m_context,CL_DEVICE_EXTENSIONS,m_device_extensions)) { string extenstions[]; StringSplit(m_device_extensions,' ',extenstions); m_support_cl_khr_fp64=false; int size=ArraySize(extenstions); for(int i=0; i=m_kernels_total) return(false); //--- int kernel_handle=m_kernels[kernel_index]; if(kernel_handle==INVALID_HANDLE || m_kernel_names[kernel_index]!=kernel_name) { //--- create kernel if((kernel_handle=CLKernelCreate(m_program,kernel_name))==INVALID_HANDLE) { CLProgramFree(m_program); CLContextFree(m_context); Print("OpenCL kernel create failed. Error code=",GetLastError()); return(false); } else { m_kernels[kernel_index]=kernel_handle; m_kernel_names[kernel_index]=kernel_name; } } return(true); } //+------------------------------------------------------------------+ //| KernelFree | //+------------------------------------------------------------------+ bool COpenCL::KernelFree(const int kernel_index) { //--- check kernel index if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); if(m_kernels[kernel_index]==INVALID_HANDLE) return(false); //--- free kernel handle CLKernelFree(m_kernels[kernel_index]); m_kernels[kernel_index]=INVALID_HANDLE; //--- return(true); } //+------------------------------------------------------------------+ //| BufferCreate | //+------------------------------------------------------------------+ bool COpenCL::BufferCreate(const int buffer_index,const uint size_in_bytes,const uint flags) { //--- check parameters if(buffer_index<0 || buffer_index>=m_buffers_total) return(false); if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); //--- int buffer_handle=CLBufferCreate(m_context,size_in_bytes,flags); if(buffer_handle!=INVALID_HANDLE) { m_buffers[buffer_index]=buffer_handle; return(true); } else return(false); } //+------------------------------------------------------------------+ //| BufferFree | //+------------------------------------------------------------------+ bool COpenCL::BufferFree(const int buffer_index) { //--- check buffer index if(buffer_index<0 || buffer_index>=m_buffers_total) return(false); if(m_buffers[buffer_index]==INVALID_HANDLE) return(false); //--- free buffer handle CLBufferFree(m_buffers[buffer_index]); m_buffers[buffer_index]=INVALID_HANDLE; //--- return(true); } //+------------------------------------------------------------------+ //| BufferFromArray | //+------------------------------------------------------------------+ template bool COpenCL::BufferFromArray(const int buffer_index,T &data[],const uint data_array_offset,const uint data_array_count,const uint flags) { //--- check parameters if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); if(buffer_index<0 || buffer_index>=m_buffers_total || data_array_count<=0) return(false); //--- buffer does not exists, create it if(m_buffers[buffer_index]==INVALID_HANDLE) { uint size_in_bytes=data_array_count*sizeof(T); int buffer_handle=CLBufferCreate(m_context,size_in_bytes,flags); if(buffer_handle!=INVALID_HANDLE) { m_buffers[buffer_index]=buffer_handle; } else return(false); } //--- write data to OpenCL buffer uint data_written=CLBufferWrite(m_buffers[buffer_index],data,0,data_array_offset,data_array_count); if(data_written!=data_array_count) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| BufferRead | //+------------------------------------------------------------------+ template bool COpenCL::BufferRead(const int buffer_index,T &data[],const uint cl_buffer_offset,const uint data_array_offset,const uint data_array_count) { //--- check parameters if(buffer_index<0 || buffer_index>=m_buffers_total || data_array_count<=0) return(false); if(m_buffers[buffer_index]==INVALID_HANDLE) return(false); if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); //--- read data from OpenCL buffer uint data_read=CLBufferRead(m_buffers[buffer_index],data,cl_buffer_offset,data_array_offset,data_array_count); if(data_read!=data_array_count) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| BufferWrite | //+------------------------------------------------------------------+ template bool COpenCL::BufferWrite(const int buffer_index,T &data[],const uint cl_buffer_offset,const uint data_array_offset,const uint data_array_count) { //--- check parameters if(buffer_index<0 || buffer_index>=m_buffers_total || data_array_count<=0) return(false); if(m_buffers[buffer_index]==INVALID_HANDLE) return(false); if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); //--- write data to OpenCL buffer uint data_written=CLBufferWrite(m_buffers[buffer_index],data,cl_buffer_offset,data_array_offset,data_array_count); if(data_written!=data_array_count) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| SetArgument | //+------------------------------------------------------------------+ template bool COpenCL::SetArgument(const int kernel_index,const int arg_index,T value) { if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); int kernel_handle=m_kernels[kernel_index]; if(kernel_handle==INVALID_HANDLE) return(false); //--- return CLSetKernelArg(kernel_handle,arg_index,value); } //+------------------------------------------------------------------+ //| SetArgumentBuffer | //+------------------------------------------------------------------+ bool COpenCL::SetArgumentBuffer(const int kernel_index,const int arg_index,const int buffer_index) { if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); if(buffer_index<0 || buffer_index>=m_buffers_total) return(false); if(m_buffers[buffer_index]==INVALID_HANDLE) return(false); //--- return CLSetKernelArgMem(m_kernels[kernel_index],arg_index,m_buffers[buffer_index]); } //+------------------------------------------------------------------+ //| SetArgumentLocalMemory | //+------------------------------------------------------------------+ bool COpenCL::SetArgumentLocalMemory(const int kernel_index,const int arg_index,const int local_memory_size) { if(m_context==INVALID_HANDLE || m_program==INVALID_HANDLE) return(false); if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); //--- check device local memory size long device_local_memory_size=CLGetInfoInteger(m_context,CL_DEVICE_LOCAL_MEM_SIZE); if(local_memory_size>device_local_memory_size) return(false); //--- return CLSetKernelArgMemLocal(m_kernels[kernel_index],arg_index,local_memory_size); } //+------------------------------------------------------------------+ //| Execute | //+------------------------------------------------------------------+ bool COpenCL::Execute(const int kernel_index,const int work_dim,const uint &work_offset[],const uint &work_size[]) { if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); int kernel_handle=m_kernels[kernel_index]; if(kernel_handle==INVALID_HANDLE) return(false); //--- return CLExecute(kernel_handle,work_dim,work_offset,work_size); } //+------------------------------------------------------------------+ //| Execute | //+------------------------------------------------------------------+ bool COpenCL::Execute(const int kernel_index,const int work_dim,const uint &work_offset[],const uint &work_size[],const uint &local_work_size[]) { if(kernel_index<0 || kernel_index>=m_kernels_total) return(false); //--- return CLExecute(m_kernels[kernel_index],work_dim,work_offset,work_size,local_work_size); } //+------------------------------------------------------------------+