将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目录下。

猜你喜欢

转载自www.cnblogs.com/hbg200/p/10196013.html
今日推荐