将OpenCL制作为动态链接库
当源代码不在改变时,将其制作为静态或动态链接库,是模块化程序设计的一个步骤。这里选择动态链接库。当前工作机是Intel的集成显卡主板,所以使用的是Intel提供的SDK。
1. 安装 intel_sdk_for_opencl_2017,NVIDIA和AMD有自己的SDK。
2. 启动Microsoft Visual C++ 2017,选择文件->新建项目,弹出新建项目对话框,选择Windows桌面->控制台应用程序。名称及解决方案名称均为DLL_OpenCL。选择项目属性->常规->配置类型:动态库(.DLL)。
3. 参考我的《VS创建Win32应用选项》设置项目参数。
4. 参考我的《OpenCL入门测试》设置项目包含等。为使用OpenCL做准备。
5. 在DLL_OpenCL源文件中输入:
#include <cstdlib> #include <iostream> #include <iomanip> #include <cstring> #include <cassert> #include <windows.h> #define CL_USE_DEPRECATED_OPENCL_1_2_APIS // 定义使用OpenCL 1.2 #include "DLL_OpenCL.h" using namespace std; cl_device_id DevicesID; // 设备ID cl_context Context = 0; // 设备管理 cl_command_queue CommandQueue = 0; // 命令队列 cl_program Program = 0; // 核对象 //--------------------------------------------------------------------------- bool Install(void)// 安装 { cl_int Err; cl_platform_id PlatformID; Err = clGetPlatformIDs(1, &PlatformID, NULL);//获取第一个平台(ID) if (Err != CL_SUCCESS) return true; Err = clGetDeviceIDs(PlatformID, CL_DEVICE_TYPE_GPU, 1, &DevicesID, NULL);//获得设备(ID) if (Err != CL_SUCCESS) return true; Context = clCreateContext(0, 1, &DevicesID, NULL, NULL, &Err);//创建设备环境 if (Err != CL_SUCCESS) return true; CommandQueue = clCreateCommandQueue(Context, DevicesID, 0, &Err);//创建命令队列 if (Err != CL_SUCCESS) return true; return false; } void ReleaseKernel(cl_kernel Kernel)// 释放核 { if (Kernel != 0) { clReleaseKernel(Kernel); Kernel = 0; } } void UnInstall(void)// 卸载 { if (Program != 0) { clReleaseProgram(Program); Program = 0; } if (CommandQueue != 0) { clReleaseCommandQueue(CommandQueue); CommandQueue = 0; } if (Context != 0) { clReleaseContext(Context); Context = 0; } } cl_mem CreateBuffer(size_t mem_size, cl_mem_flags mem_flag)// 创建缓冲区 { return clCreateBuffer(Context, mem_flag, mem_size, NULL, NULL); } bool WriteBuffer(cl_mem RefersBufferObject, void *pInputData, size_t mem_size)// 写缓冲区 { cl_int Err = clEnqueueWriteBuffer(CommandQueue, RefersBufferObject, CL_TRUE, 0, mem_size, pInputData, 0, NULL, NULL); if (Err != CL_SUCCESS) return false; return true; } bool ReadBuffer(cl_mem RefersBufferObject, void *pOutData, size_t mem_size)// 读缓冲区 { cl_int Err = clEnqueueReadBuffer(CommandQueue, RefersBufferObject, CL_TRUE, 0, mem_size, pOutData, 0, NULL, NULL); if (Err != CL_SUCCESS) return false; return true; } cl_kernel CreateProgramSource(const char *pKernelProgramSource, const char *pKernelFunctionName)// 创建异构源代码 { size_t Src_size[] = { strlen(pKernelProgramSource) };//读入源代码数组 Program = clCreateProgramWithSource(Context, 1, &pKernelProgramSource, Src_size, NULL);// 输入设备源程序 clBuildProgram(Program, 1, &DevicesID, NULL, NULL, NULL);// 编译设备源程序 return clCreateKernel(Program, pKernelFunctionName, NULL);// 创建核函数 } bool SetKernelArg(cl_kernel Kernel, cl_int ParamNumber, cl_mem Mem_D)// 输入核参数 { cl_int Err; Err = clSetKernelArg(Kernel, ParamNumber, sizeof(cl_mem), &Mem_D); if (Err != CL_SUCCESS) return false; return true; } bool ExecuteKernel(cl_kernel KernelName, cl_uint work_dims, const size_t *global_work_size, const size_t *local_work_size)// 运行GPU核函数 { cl_int Err; Err = clEnqueueNDRangeKernel(CommandQueue, KernelName, work_dims, NULL, global_work_size, local_work_size, 0, NULL, NULL);//运行核函数 if (Err != CL_SUCCESS) return false; return true; } cl_uchar *MapBuf(cl_mem RefersBufferObject, size_t MapByteCount, cl_map_flags map_flag)// 映射缓冲区 { return (cl_uchar *)clEnqueueMapBuffer(CommandQueue, RefersBufferObject, CL_TRUE, map_flag, 0, MapByteCount, 0, NULL, NULL, NULL); } void ReleaseMemObject(cl_mem MemObject)// 释放存储器资源 { if (MemObject != 0) { clReleaseMemObject(MemObject); MemObject = 0; } } void Finish(void)// 等待刷新 { clFinish(CommandQueue); } //---------------------------------------------------------------------------
6. 在项目目录下新建DLL_OpenCL.h头文件,编辑输入:
#ifndef DLL_OpenCLH #define DLL_OpenCLH #include <CL/cl.h> extern "C" __declspec(dllexport) bool Install(void);// 安装 extern "C" __declspec(dllexport) void ReleaseKernel(cl_kernel Kernel);// 释放核 extern "C" __declspec(dllexport) void UnInstall(void);// 卸载 extern "C" __declspec(dllexport) cl_mem CreateBuffer(size_t mem_size, cl_mem_flags mem_flag);// 创建缓冲区 extern "C" __declspec(dllexport) bool WriteBuffer(cl_mem RefersBufferObject, void *pInputData, size_t mem_size);// 写缓冲区 extern "C" __declspec(dllexport) bool ReadBuffer(cl_mem RefersBufferObject, void *pOutData, size_t mem_size);// 读缓冲区 extern "C" __declspec(dllexport) cl_kernel CreateProgramSource(const char *pKernelProgramSource, const char *pKernelFunctionName);// 创建异构源代码 extern "C" __declspec(dllexport) bool SetKernelArg(cl_kernel Kernel, cl_int ParamNumber, cl_mem Mem_D);// 输入核参数 extern "C" __declspec(dllexport) bool ExecuteKernel(cl_kernel KernelName, cl_uint work_dims, const size_t *global_work_size, const size_t *local_work_size);// 运行GPU核函数 extern "C" __declspec(dllexport) cl_uchar *MapBuf(cl_mem RefersBufferObject, size_t MapByteCount, cl_map_flags map_flag);// 映射缓冲区 extern "C" __declspec(dllexport) void ReleaseMemObject(cl_mem MemObject);// 释放存储器资源 extern "C" __declspec(dllexport) void Finish(void);// 等待刷新 #endif
在解决方案->头文件->添加->现有项中输入DLL_OpenCL.h头文件。
7. 选择菜单生成->生成解决方案或重新生成解决方案。提示编译正确。
8. 在解决方案目录下的Debug目录中,提取验证DLL_OpenCL.dll和DLL_OpenCL.lib,结果正确。
注意事项:
1. 动态链接库有两种导出/调用风格,即C/C++风格,C风格需要加extern “C”修饰。
extern “C”__declspec(dllexport) void FunName(void); // C导出风格。VC默认_cdecl调用约定可以不写。
__declspec(dllexport) void FunName(void); // C++导出风格。VC默认_cdecl调用约定可以不写。
2. 调用约定为三种:__cdecl为C语言调用约定,__stdcall为API调用约定,__fastcall为快速调用约定。调用约定即为传递函数参数的约定,双方应一致。
3. 静态式调用需要将__declspec(dllexport)改为__declspec(dllexport),即将导出改为导入。
4. 导入调试需要将.lib考入项目目录下,.dll考入解决方案的Debug目录下。