Cool Stack

OpenCL en C

L'API OpenCL pour le langage C est la couche la plus basse pour accéder à OpenCL. C'est certainement celle qui évoluera le plus vite et qui fait l'objet de spécification par le Kronos Group. Les autres API sont souvent basées sur cette API C. Voici une présentation rapide de l'api opencl pour le C. Le but est simplement d'illustrer l'utilisation de l'API, je ne vous conseille pas d'écrire vos applications directement avec cette API à moins de commencer par écrire une petite couche d'abstraction (simplification).

Voici les différentes étapes de création:

Récupérer le(s) device(s)

cl_uint nbDevice;
cl_device_id device;
err=clGetDeviceIDs(NULL,CL_DEVICE_TYPE_GPU,1,&device,&nbDevice);

Récupérer les infos sur chaque device

char deviceName[1024];
cl_uint maxComputeUnits;
cl_uint maxClockFrequency;
err=clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(deviceName), &deviceName, NULL);
cout << deviceName << endl;
err=clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(maxComputeUnits), &maxComputeUnits, NULL);
cout << "Max Compute Units : " << maxComputeUnits << endl;
err=clGetDeviceInfo(device, CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(maxClockFrequency), &maxClockFrequency, NULL);
cout << "Max Clock Frequency : " << maxClockFrequency << " MHz" << endl;

Créer un context

cl_context context;
context=clCreateContext(0,1,&device,NULL,NULL,&err);

Créer une queue de commande sur chaqu'une des devices

cl_command_queue cQueue;
cQueue = clCreateCommandQueue(context, device, 0, &err);

Allouer les buffers dans le context

cl_mem ib_A,ib_B,ob_dest;
ib_A = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(cl_float) * szGlobalWorkSize, NULL, &err);
ib_B = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(cl_float) * szGlobalWorkSize, NULL, &err);
ob_dest = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_float) * szGlobalWorkSize, NULL, &err);
err = clEnqueueWriteBuffer(cQueue, ib_A, CL_FALSE, 0, sizeof(cl_float)  * szGlobalWorkSize, A, 0, NULL, NULL);
err = clEnqueueWriteBuffer(cQueue, ib_B, CL_FALSE, 0, sizeof(cl_float)  * szGlobalWorkSize, B, 0, NULL, NULL);

Créer/compiler un Program depuis les sources en OpenCL

cl_program hProgram;
hProgram = clCreateProgramWithSource(context, 1, &sProgramSource, 0, &err);
err = clBuildProgram(hProgram, 0, 0, 0, 0, 0);
if(err) {
  size_t len;
  char buffer[2048];
  clGetProgramBuildInfo(hProgram, device, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer,&len);
  cout << buffer << endl;
}

Créer un kernel a partir de ce Program

cl_kernel hKernel;
hKernel = clCreateKernel(hProgram, "myadd", &err);

Lancer le kernel

La fonction clEnqueueNDRangeKernel permet de lancer un kernel existant. Il faut au préalable avoir transmit les arguments clSetKernelArg.

err = clSetKernelArg(hKernel, 0, sizeof(cl_mem), (void*)&ib_A);
err = clSetKernelArg(hKernel, 1, sizeof(cl_mem), (void*)&ib_B);
err = clSetKernelArg(hKernel, 2, sizeof(cl_mem), (void*)&ob_dest);
err = clSetKernelArg(hKernel, 3, sizeof(cl_int), (void*)&iNumElements);
err = clEnqueueNDRangeKernel(cQueue, hKernel, 1, 0, &szGlobalWorkSize, &szLocalWorkSize, 0, 0, 0);

Récupérer le résultat

err = clEnqueueReadBuffer(cQueue, ob_dest, CL_TRUE, 0, sizeof(cl_float)  * szGlobalWorkSize, dest, 0, NULL, NULL);

Posted Jeu 31 décembre 2009 by Stéphane Planquart in programmation graphique