PageRenderTime 80ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Chapter_2/HelloWorld/HelloWorld.cpp

https://gitlab.com/adam.lukaitis/opencl-book-samples
C++ | 335 lines | 231 code | 42 blank | 62 comment | 53 complexity | 225d92d908bafad3e71ed6fe8d8d1db0 MD5 | raw file
  1. //
  2. // Book: OpenCL(R) Programming Guide
  3. // Authors: Aaftab Munshi, Benedict Gaster, Timothy Mattson, James Fung, Dan Ginsburg
  4. // ISBN-10: 0-321-74964-2
  5. // ISBN-13: 978-0-321-74964-2
  6. // Publisher: Addison-Wesley Professional
  7. // URLs: http://safari.informit.com/9780132488006/
  8. // http://www.openclprogrammingguide.com
  9. //
  10. // HelloWorld.cpp
  11. //
  12. // This is a simple example that demonstrates basic OpenCL setup and
  13. // use.
  14. #include <iostream>
  15. #include <fstream>
  16. #include <sstream>
  17. #ifdef __APPLE__
  18. #include <OpenCL/cl.h>
  19. #else
  20. #include <CL/cl.h>
  21. #endif
  22. ///
  23. // Constants
  24. //
  25. const int ARRAY_SIZE = 1000;
  26. ///
  27. // Create an OpenCL context on the first available platform using
  28. // either a GPU or CPU depending on what is available.
  29. //
  30. cl_context CreateContext()
  31. {
  32. cl_int errNum;
  33. cl_uint numPlatforms;
  34. cl_platform_id firstPlatformId;
  35. cl_context context = NULL;
  36. // First, select an OpenCL platform to run on. For this example, we
  37. // simply choose the first available platform. Normally, you would
  38. // query for all available platforms and select the most appropriate one.
  39. errNum = clGetPlatformIDs(1, &firstPlatformId, &numPlatforms);
  40. if (errNum != CL_SUCCESS || numPlatforms <= 0)
  41. {
  42. std::cerr << "Failed to find any OpenCL platforms." << std::endl;
  43. return NULL;
  44. }
  45. // Next, create an OpenCL context on the platform. Attempt to
  46. // create a GPU-based context, and if that fails, try to create
  47. // a CPU-based context.
  48. cl_context_properties contextProperties[] =
  49. {
  50. CL_CONTEXT_PLATFORM,
  51. (cl_context_properties)firstPlatformId,
  52. 0
  53. };
  54. context = clCreateContextFromType(contextProperties, CL_DEVICE_TYPE_GPU,
  55. NULL, NULL, &errNum);
  56. if (errNum != CL_SUCCESS)
  57. {
  58. std::cout << "Could not create GPU context, trying CPU..." << std::endl;
  59. context = clCreateContextFromType(contextProperties, CL_DEVICE_TYPE_CPU,
  60. NULL, NULL, &errNum);
  61. if (errNum != CL_SUCCESS)
  62. {
  63. std::cerr << "Failed to create an OpenCL GPU or CPU context." << std::endl;
  64. return NULL;
  65. }
  66. }
  67. return context;
  68. }
  69. ///
  70. // Create a command queue on the first device available on the
  71. // context
  72. //
  73. cl_command_queue CreateCommandQueue(cl_context context, cl_device_id *device)
  74. {
  75. cl_int errNum;
  76. cl_device_id *devices;
  77. cl_command_queue commandQueue = NULL;
  78. size_t deviceBufferSize = -1;
  79. // First get the size of the devices buffer
  80. errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &deviceBufferSize);
  81. if (errNum != CL_SUCCESS)
  82. {
  83. std::cerr << "Failed call to clGetContextInfo(...,GL_CONTEXT_DEVICES,...)";
  84. return NULL;
  85. }
  86. if (deviceBufferSize <= 0)
  87. {
  88. std::cerr << "No devices available.";
  89. return NULL;
  90. }
  91. // Allocate memory for the devices buffer
  92. devices = new cl_device_id[deviceBufferSize / sizeof(cl_device_id)];
  93. errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, deviceBufferSize, devices, NULL);
  94. if (errNum != CL_SUCCESS)
  95. {
  96. delete [] devices;
  97. std::cerr << "Failed to get device IDs";
  98. return NULL;
  99. }
  100. // In this example, we just choose the first available device. In a
  101. // real program, you would likely use all available devices or choose
  102. // the highest performance device based on OpenCL device queries
  103. commandQueue = clCreateCommandQueue(context, devices[0], 0, NULL);
  104. if (commandQueue == NULL)
  105. {
  106. delete [] devices;
  107. std::cerr << "Failed to create commandQueue for device 0";
  108. return NULL;
  109. }
  110. *device = devices[0];
  111. delete [] devices;
  112. return commandQueue;
  113. }
  114. ///
  115. // Create an OpenCL program from the kernel source file
  116. //
  117. cl_program CreateProgram(cl_context context, cl_device_id device, const char* fileName)
  118. {
  119. cl_int errNum;
  120. cl_program program;
  121. std::ifstream kernelFile(fileName, std::ios::in);
  122. if (!kernelFile.is_open())
  123. {
  124. std::cerr << "Failed to open file for reading: " << fileName << std::endl;
  125. return NULL;
  126. }
  127. std::ostringstream oss;
  128. oss << kernelFile.rdbuf();
  129. std::string srcStdStr = oss.str();
  130. const char *srcStr = srcStdStr.c_str();
  131. program = clCreateProgramWithSource(context, 1,
  132. (const char**)&srcStr,
  133. NULL, NULL);
  134. if (program == NULL)
  135. {
  136. std::cerr << "Failed to create CL program from source." << std::endl;
  137. return NULL;
  138. }
  139. errNum = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
  140. if (errNum != CL_SUCCESS)
  141. {
  142. // Determine the reason for the error
  143. char buildLog[16384];
  144. clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,
  145. sizeof(buildLog), buildLog, NULL);
  146. std::cerr << "Error in kernel: " << std::endl;
  147. std::cerr << buildLog;
  148. clReleaseProgram(program);
  149. return NULL;
  150. }
  151. return program;
  152. }
  153. ///
  154. // Create memory objects used as the arguments to the kernel
  155. // The kernel takes three arguments: result (output), a (input),
  156. // and b (input)
  157. //
  158. bool CreateMemObjects(cl_context context, cl_mem memObjects[3],
  159. float *a, float *b)
  160. {
  161. memObjects[0] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
  162. sizeof(float) * ARRAY_SIZE, a, NULL);
  163. memObjects[1] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
  164. sizeof(float) * ARRAY_SIZE, b, NULL);
  165. memObjects[2] = clCreateBuffer(context, CL_MEM_READ_WRITE,
  166. sizeof(float) * ARRAY_SIZE, NULL, NULL);
  167. if (memObjects[0] == NULL || memObjects[1] == NULL || memObjects[2] == NULL)
  168. {
  169. std::cerr << "Error creating memory objects." << std::endl;
  170. return false;
  171. }
  172. return true;
  173. }
  174. ///
  175. // Cleanup any created OpenCL resources
  176. //
  177. void Cleanup(cl_context context, cl_command_queue commandQueue,
  178. cl_program program, cl_kernel kernel, cl_mem memObjects[3])
  179. {
  180. for (int i = 0; i < 3; i++)
  181. {
  182. if (memObjects[i] != 0)
  183. clReleaseMemObject(memObjects[i]);
  184. }
  185. if (commandQueue != 0)
  186. clReleaseCommandQueue(commandQueue);
  187. if (kernel != 0)
  188. clReleaseKernel(kernel);
  189. if (program != 0)
  190. clReleaseProgram(program);
  191. if (context != 0)
  192. clReleaseContext(context);
  193. }
  194. ///
  195. // main() for HelloWorld example
  196. //
  197. int main(int argc, char** argv)
  198. {
  199. cl_context context = 0;
  200. cl_command_queue commandQueue = 0;
  201. cl_program program = 0;
  202. cl_device_id device = 0;
  203. cl_kernel kernel = 0;
  204. cl_mem memObjects[3] = { 0, 0, 0 };
  205. cl_int errNum;
  206. // Create an OpenCL context on first available platform
  207. context = CreateContext();
  208. if (context == NULL)
  209. {
  210. std::cerr << "Failed to create OpenCL context." << std::endl;
  211. return 1;
  212. }
  213. // Create a command-queue on the first device available
  214. // on the created context
  215. commandQueue = CreateCommandQueue(context, &device);
  216. if (commandQueue == NULL)
  217. {
  218. Cleanup(context, commandQueue, program, kernel, memObjects);
  219. return 1;
  220. }
  221. // Create OpenCL program from HelloWorld.cl kernel source
  222. program = CreateProgram(context, device, "HelloWorld.cl");
  223. if (program == NULL)
  224. {
  225. Cleanup(context, commandQueue, program, kernel, memObjects);
  226. return 1;
  227. }
  228. // Create OpenCL kernel
  229. kernel = clCreateKernel(program, "hello_kernel", NULL);
  230. if (kernel == NULL)
  231. {
  232. std::cerr << "Failed to create kernel" << std::endl;
  233. Cleanup(context, commandQueue, program, kernel, memObjects);
  234. return 1;
  235. }
  236. // Create memory objects that will be used as arguments to
  237. // kernel. First create host memory arrays that will be
  238. // used to store the arguments to the kernel
  239. float result[ARRAY_SIZE];
  240. float a[ARRAY_SIZE];
  241. float b[ARRAY_SIZE];
  242. for (int i = 0; i < ARRAY_SIZE; i++)
  243. {
  244. a[i] = (float)i;
  245. b[i] = (float)(i * 2);
  246. }
  247. if (!CreateMemObjects(context, memObjects, a, b))
  248. {
  249. Cleanup(context, commandQueue, program, kernel, memObjects);
  250. return 1;
  251. }
  252. // Set the kernel arguments (result, a, b)
  253. errNum = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memObjects[0]);
  254. errNum |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &memObjects[1]);
  255. errNum |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &memObjects[2]);
  256. if (errNum != CL_SUCCESS)
  257. {
  258. std::cerr << "Error setting kernel arguments." << std::endl;
  259. Cleanup(context, commandQueue, program, kernel, memObjects);
  260. return 1;
  261. }
  262. size_t globalWorkSize[1] = { ARRAY_SIZE };
  263. size_t localWorkSize[1] = { 1 };
  264. // Queue the kernel up for execution across the array
  265. errNum = clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL,
  266. globalWorkSize, localWorkSize,
  267. 0, NULL, NULL);
  268. if (errNum != CL_SUCCESS)
  269. {
  270. std::cerr << "Error queuing kernel for execution." << std::endl;
  271. Cleanup(context, commandQueue, program, kernel, memObjects);
  272. return 1;
  273. }
  274. // Read the output buffer back to the Host
  275. errNum = clEnqueueReadBuffer(commandQueue, memObjects[2], CL_TRUE,
  276. 0, ARRAY_SIZE * sizeof(float), result,
  277. 0, NULL, NULL);
  278. if (errNum != CL_SUCCESS)
  279. {
  280. std::cerr << "Error reading result buffer." << std::endl;
  281. Cleanup(context, commandQueue, program, kernel, memObjects);
  282. return 1;
  283. }
  284. // Output the result buffer
  285. for (int i = 0; i < ARRAY_SIZE; i++)
  286. {
  287. std::cout << result[i] << " ";
  288. }
  289. std::cout << std::endl;
  290. std::cout << "Executed program succesfully." << std::endl;
  291. Cleanup(context, commandQueue, program, kernel, memObjects);
  292. return 0;
  293. }