OpenCL编程步骤(一):创建上下文

OpenCL编程的第一步就是查询OpenCL平台集合 ,选择其中一个或多个平台在应用中使用。
平台集查询可以用下列命令:
   
   
cl_int clGetPlatformIDs(cl_uint num_entries,
cl_platform_id *platforms,
cl_uint *num_platforms)
这个函数一般被调用两次:
  • 第一次调用这个函数是获得可用平台的数目
  • 然后为平台对象分配内存空间
  • 第二次调用用来获取平台对象
          num_entries是platforms中可以容纳的cl_platform_id表项的数目。如果platform不是NULL,那么num_entries必须大于零。
          platforms返回所找到的OpenCL平台清单。platforms中的每个cl_platform_id都用来标识某个特定的OpenCL平台。如果platforms是NULL,则被忽略。所返回的 OpenCL平台的数目是num_entries和实际数目中较小的那一个。
          num_platforms返回实际可用的OpenCL平台数目。如果num_Platforms为NULL,则被忽略。

如果函数执行成功,clGetPlatformIDs会返回CL_SUCCESS。否则返回下面的错误
  • CL_INVALID_VALUE        如果num_entries是0,而且platforms不为NULL;或则两者都是NULL
  • CL_OUT_OF_HOST_MEMORY    在主机上为OpenCL平台分配内存空间失败
下面是一段调用clGetPlatformIDs的代码
   
   
cl_int errNum;
cl_uint numPlatforms;
cl_platform_id *platformIds;
cl_context context = NULL;
 
errNum = clGetPlatformIDs(0, NULL, &numPlatforms); /*获取可用的平台数目,保存在numPlatforms中*/
if (errNum != CL_SUCCESS || numPlatforms <= 0)
{
cerr << "get the available platforms error:" << endl;
return NULL;
}
/* 为平台分配内存空间 */
platformIds = (cl_platform_id *)alloca(sizeof(cl_platform_id) * numPlatforms);
if (platformIds)
{
cerr << "alloca error" << endl;
return NULL;
}
errNum = clGetPlatformIDs(numPlatforms, platformIds, NULL); //把找到的平台全部填充到platformIds中
给定一个平台可用下面的命令查询各个属性
   
   
cl_int clGetPlatformInfo(cl_platform_id platform,
cl_platform_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret)
          platform即clGetPlatformIDs所返回的平台ID,指明要查询那个平台,也可以是NULL,如果是NULL,其行为依赖于具体操作。
          param_name是一个枚举常量指明要查询什么信息。
          param_value是一个指针,所指内存用来存储param_name所对应的信息,如果是NULL,则忽略。
          param_value_size即param_value所指内存块的大小(单位:字节),其值要大于等于
下表中的返回类型的大小。
          param_value_size_ret返回所查询信息的实际大小。如果是NULL,则忽略。

如果执行成功,clGetPlatformInfo会返回CL_SUCCESS。否则,返回下面错误之一:
  • CL_INVALID_PLATFORM,  platform无效
  • CL_INVALID_VALUE, 如果param_name不是所支持的值,或者param_size_value的返回值小于表中的返回值的大小,且param_value不为NULL
  • CL_OUT_OF_HOST_MEMORY ,在主机上为opencl操作分配内存失败

当查询所需要的信息的时候,可以把param_name和param_value的值分别置为0和NULL来查询返回值的大小。然后分配相应的内存大小,再调用函数把信息写入到所分配的内存中。

示例程序:
  
  
/* 查询第一个可用的Opencl平台的名称 */
#include <iostream>
#include <CL/cl.h>
 
using namespace std;
 
int main(int argc, char *argv[])
{
cl_int errNum;
cl_uint numPlatforms;
cl_platform_id *platformIds;
cl_context context = NULL;
errNum = clGetPlatformIDs(0, NULL, &numPlatforms);
if (errNum != CL_SUCCESS || numPlatforms <= 0)
{
cerr << "Failed to find any OpenCL platform." << endl;
return -1;
}
platformIds = (cl_platform_id *)alloca(sizeof(cl_platform_id) * numPlatforms);
errNum = clGetPlatformIDs(numPlatforms, platformIds, NULL);
if (errNum != CL_SUCCESS)
{
cerr << "Failed to find the OpenCL platform..." << endl;
return -1;
}
size_t size;
errNum = clGetPlatformInfo(platformIds[0], CL_PLATFORM_NAME, 0, NULL, &size);
char *name = (char *)alloca(sizeof(char) * size);
errNum = clGetPlatformInfo(platformIds[0], CL_PLATFORM_NAME, size, name, NULL);
cout << "Platform Name: " << name << endl;
 
return 0;
OpenCL编程的第二步就是获取设备
各个平台可能会关联一组计算设备,应用程序将利用这些计算设备执行代码。
   
   
cl_int clGetDeviceIDs(cl_platform_id platform,
cl_device_type device_type,
cl_uint num_entries,
cl_device_id *devices,
cl_uint *num_devices)
这个函数一般被调用两次:
  • 第一次调用这个函数是获得设备的数目
  • 第二次调用用来获取设备对象
函数clGetDeviceIDs可用来获取一个平台上的可用设备清单。
           platform即 clGetPlatformIDs所返回的平台id,也可能是NULL,如果是NULL,则行为依赖于具体的操作。
           device_type是位域,用来标识OpenCL设备的类型。也可以用来查询某种OpenCL设备,也可以查询所有的设备。
           num_entries是devices中能容纳的cl_device_id表项的数目。如果devices不为NULL,则num_entries必须大于零。
         devices用来返回可用的OpenCL设备。devices中返回的cl_device_id用来标识一个OpenCL设备。如果devices为NULL,则忽略。所返回的OpenCL设备数目为num_entries和符合device_type类型的设备的数目的最小值。
         num_devices返回符合device_type的所有的OpenCL设备的数目。如果num_devices为NULL,则忽略。
如果函数执行成功,则clGetDeviceIDs会返回CL_SUCCESS,否则返回下面错误之一:
  • CL_INVALID_PLATFORM,platform无效。
  • CL_INVALID_DEVICE_TYPE,device_type无效。
  • CL_INVALID_VALUE,num_entries为零,但是devices不为NULL,或则两个都为NULL。
  • CL_DEVICE_NOT_FOUND,没有找到符号device_type的OpenCL设备。
  • CL_OUT_OF_RESOURCES,为设备上的OpenCL操作分配内存失败。
  • CL_OUT_OF_HOST_MEMORY,为主机上的OpenCL操作分配内存失败。
cl_device_type:
  • CL_DEVICE_TYPE_CPU:主机处理器。OpenCL操作运行在其上,是单核或多核CPU。
  • CL_DEVICE_TYPE_GPU:GPU。这意味着设备也可以用来加速3D API。
  • CL_DEVICE_TYPE_ADDELERATOR:OpenCL专用加速器。
  • CL_DEVICE_TYPE_CUSTOM:一般专用加速器,但是不支持OpenCL C编写的程序
  • CL_DEVICE_TYPE_DEFAULT:系统中默认的OpenCL设备。不能是CL_DEVICE_TYPE_CUSTOM类型的设备。
  • CL_DEVICE_TYPE_ALL:系统中所有的OpenCL设备。不包括CL_DEVICE_TYPE_CUSTOM类型的设备。
调用clGetDeviceIDs的代码:
   
   
cl_int errNum;
cl_uint numDevices;
cl_device_id *deviceIds;
 
/* 获取符合cl_device_type类型的OpenCL设备的数目,并保存在numDevices中 */
errNum = clGetDeviceIDs(platform,
CL_DEVICE_TYPE_GPU,
0,
NULL,
&numDevices);
if (errNum != CL_SUCCESS || numDevices <= 0)
{
cerr << "No GPU device found for platform." << endl;
return -1;
}
 
/* 分配相应数目的设备所占用的内存空间,然后填入到devicesIds中 */
devicesIds = (cl_device_id *)alloca(sizeof(cl_device_id) * numDevices);
errNum = clGetDeviceIDs(platform,
CL_DEVICE_TYPE_GPU,
numDevices,
devicesIds,
NULL);
if (errNum != CL_SUCCESS)
{
cerr << "No GPU found for the platform." << endl;
return -1;
}
给定一个设备可以用一下命令来查询设备的各种属性:
   
   
cl_int clGetDeviceInfo(cl_device_id device,
cl_device_info param_name,
size_t param_value_size,
void *param_value,
size_t param_value_size_ret)
这个函数的基本用发和上面的clGetPlatformInfo的用法是一样的。参照上面的clGetPlatformInfo来使用。
惟一的不同就是cl_device_info有好多中不同的信息参数。
OpenCL编程的第三步就是生成上下文
可以由下面的命令生成上下文
   
   
cl_context clCreateContext(
const cl_context_properties *properties,
cl_uint num_entries,
const cl_device_id *devices,
void (CL_CALLBACK *pfn_notify)(const char *errinfo,
const void *private_info,
size_t cb,
void *user_data),
void *user_data,
cl_int *errcode_ret)
这个函数可以创建一个OpenCL上下文。OpenCL上下文是在一个或多个设备上创建。上下文是OpenCL runtime 用来管理像命令队列、内存队列、程序对象和内核对象,并且在上下文中指定的一个或多个设备上执行内核函数。
properties指定了上下文的一系列属性名和对应的值。每个属性名后面紧跟相应的期望值。此清单以0终止。如果properties是NULL,选择那个平台依赖具体操作。

cl_context_properties属性值
CL_CONTEXT_PLATFORMcl_platform_id
指定使用那个平台。
CL_CONTEXT_INTEROP_USER_SYNCcl_bool
OpenCL和其他API之间的同步是否由用户自己负责。如果没有指定此属性,则默认值为CL_FALSE

num_devices是参数devices中设备的数目。
devices指向一个设备清单,其中的设备都是惟一的,都是由clGetDeviceIDs所返回的,或者使用clCreateSubDevices创建的子设备。
pfn_notify是应用所注册的回调函数。对于此上下文,无论创建时还是运行时,只要发生了错误,OpenCL的操作都会调用此函数,但调用可能是异步的。应用需要保证此函数是线程安全的。这个回调函数的参数是:
  • errinfo    指向一个错误字符串
  • private_info 指向一块二进制数据,其大小为cb,这块数据由OpenCL操作所返回,可以用来记录一些附加资讯来帮助调试错误。
  • user_data指向用户提供的数据
如果pfn_notify是NULL,则表示不注册回调函数。
user_data会在调用pfn_notify时作为参数user_data使用。user_data可以是NULL。
errcode_ret是用来返回相应的错误码。如果errcode_ret是NULL,则不会返回错误码。
如果成功创建了上下文,clCreateContext会将其返回(非零),并将errcode_ret置为CL_SUCCESS。否则返回NULL,并将errcode_ret置为下面错误吗之一:
  • CL_INVALID_PLATFORM,properties是NULL且没有可选的平台,或者properties中的平台无效。
  • CL_INVALID_PROPERTY,properties中的属性名不受支持,或者支持此属性但是其值无效,或者同一属性名出现多次。
  • CL_INVALID_VALUE, devices是NULL,或者num_devices等于0,或者pfn_notify是NULL但是user_data不是NULL。
  • CL_INVALID_DEVICE,devices中有无效的设备
  • CL_DEVICE_NOT_AVAILABLE,devices中的某个设备当前不可用,即使该设备是由clGetDeviceIDs返回的。
  • CL_OUT_OF_RESOURCES,为设备上的OpenCL操作分为内存失败。
  • CL_OUT_OF_HOST_MEMORY,为主机上的OpenCL操作分配内存失败。
   
   
cl_context clCreateContextFromType(
const cl_context_properties *properties,
cl_device_type device_type,
void (CL_CALLBACK *pfn_notify)(const char *errinfo,
const void *private_info,
size_t cb,
void *user_data),
void *user_data,
cl_int *errcode_ret)
函数clCreateContexFromType会由特定类型的设备创建一个OpenCL上下文,此上下文不会引用这些设备所创建的子设备。
properties和clCreateContext中一样。
devcie_type是位段,用来标志设备类型。类型见上面的cl_device_type。
pfn_notify和user_data跟clCreateContext中所描述的是一样的。
errcode_ret用来返回相应的错误码,如果errcode_ret是NULL,则不返回错误码。

跟clCreateContext错误码不同的是:
CL_INVALID_DEVCIE_TYPE,device_type的值是无效的。
CL_DEVCIE_NOT_AVALIABLE,当前没用同时符合device_type以及properties中属性值的设备可用。
CL_DEVICE_NOT_FOUND,没有找到同时符合device_type以及properties中的属性值的设备。

调用clCreateContext的步骤:
先获取平台,然后获取设备,再根据平台和设备创建上下文。
   
   
cl_platform platform;
cl_uint num;
cl_device_id *devices;
cl_context context;
cl_int errNum;
 
errNum = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &num);
if (errNum != CL_SUCCESS || num <= 0)
{
cerr << "Failed to find the device." << endl;
return -1;
}
devices = (cl_device_id *)alloca(sizeof(cl_device_id) * num);
errNum = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, num, devices, NULL);
cl_context_properties properties[] =
{
CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0
};
 
context = clCreateContext(properties, num, devices, NULL, NULL, &errNum);
if (context == NULL || errNum != CL_SUCCESS)
{
cerr << "Failed to create context." << endl;
return -1;
}
调用clCreateContextFromType的步骤:
先获取平台,然后根据平台和设备的类型创建上下文,创建上下文后再获取设备。
在说明clCreateContextFromType的用法之前,需要先了解另一个命令
   
   
cl_int clGetContextInfo(cl_context context,
cl_context_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret)
这个函数用来查询上下文的相关信息。
context 指定要查询的OpenCL上下文。
param_name 是枚举常量,指定要查询的信息。
param_value 指向的内存用来存放查询的结果。如果param_value是NULL,则忽略。
param_value_size 即param_value所指内存的大小。必须大于param_name所指定类型的大小。
param_value_size_ret 返回param_value所存查询结果的实际字节数。如果param_value_size_ret是NULL,则忽略。

cl_context_info返回类别
CL_CONTEXT_REFERENCE_COUNTcl_uint
返回context的引用计数
CL_CONTEXT_NUM_DEVCIEScl_uint
返回context中设备的数目
CL_CONTEXT_DEVCIEScl_device_id[]
返回context中的设备清单
CL_CONTEXT_PROPERTIEScl_context_properties[]
返回clCreateContext或clCreateContextFromType时所指定的参数properties。
对于调用 clCreateContext或clCreateContextFromType创建context时所指定的参数properties而言,如果该参数不为NULL,操作必须返回该参数的值。如果该参数不为NULL,操作可以现在把param_value_size_ret置为0,也可以把param_value的内容置为0。
0在上下文属性清单中是终止标记。

该函数执行成功后,clGetContextInfo会返回CL_SUCCESS。否则,返回下列错误码之一:
CL_INVALID_CONTEXT, context不是一个有效的上下文。
CL_INVALID_VALUE, param_name的值不受支持,或者param_value_size的值小于cl_context_info中的返回类型且param_value不是NULL。
CL_OUT_OF_RESOURCE,为设备上的OpenCL操作分配内存失败。
CL_OUT_OF_HOST_MEMROY,为主机上的OpenCL操作分配内存失败。

调用clCreateContextFromType的实例程序
   
   
cl_uint numPlatforms;
cl_paltform_id *platforms;
cl_context context = NULL;
cl_int errNum;
size_t size;
 
errNum = clGetPlatformIDs(0, NULL, &numPlatforms);
if (errNum != CL_SUCCESS || numPlatforms <= 0)
{
cerr << "Failed to found platforms" << endl;
return -1;
}
platforms = (cl_platform_id *)alloca(sizeof(cl_paltform_id) * numPlatforms);
errNum = clGetPlatformIDs(numPlatforms, platforms, NULL);
 
cl_context_properties properties[] =
{
CL_CONTEXT_PLATFORM, (cl_context_properties)platforms[0], 0
};
 
context = clCreateContextFromType(
properties, CL_DEVICE_TYPE_GPU, NULL, NULL, &errNum);
if (errNum != CL_SUCCESS || context == NULL)
{
cerr << "Failed to create context." << endl;
return -1;
}
 
errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &size);
cl_device_id *devices = (cl_device_id *)alloca(sizeof(cl_device_id) * size);
errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, size, devices, NULL);
 
for (size_t i = 0; i < size / sizeof(cl_device_id); i++)
{
cl_device_type type;
errNum = clGetDeviceInfo(
devices[i], CL_DEIVCE_TYPE, sizeof(cl_devict_type), &type, NULL);
switch (type)
{
case CL_DEVICE_TYPE_GPU:
cout << "CL_DEVICE_TYPE_GPU." << endl;
break;
case CL_DEVICE_TYPE_CPU:
cout << "CL_DEVICE_TYPE_CPU." << endl;
break;
case CL_DEVICE_TYPE_ACCELERATOR:
cout << "CL_DEIVCE_TYPE_ACCELERATOR." << endl;
break;
default:
break;
}
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值