mandelbrot.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * StarPU
  3. * Copyright (C) Université Bordeaux 1, CNRS 2008-2010 (see AUTHORS file)
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License as published by
  7. * the Free Software Foundation; either version 2.1 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. *
  14. * See the GNU Lesser General Public License in COPYING.LGPL for more details.
  15. */
  16. #include <starpu.h>
  17. #ifdef STARPU_USE_OPENCL
  18. #include <starpu_opencl.h>
  19. #endif
  20. #include <sys/time.h>
  21. #ifdef STARPU_HAVE_X11
  22. #include <X11/Xlib.h>
  23. #include <X11/Xutil.h>
  24. int use_x11 = 1;
  25. #endif
  26. int demo = 0;
  27. /* NB: The X11 code is inspired from the http://locklessinc.com/articles/mandelbrot/ article */
  28. static int nblocks = 16;
  29. static int height = 1280;
  30. static int width = 1600;
  31. static int maxIt = 20000;
  32. static double leftX = -0.745;
  33. static double rightX = -0.74375;
  34. static double topY = .15;
  35. static double bottomY = .14875;
  36. /*
  37. * X11 window management
  38. */
  39. #ifdef STARPU_HAVE_X11
  40. /* X11 data */
  41. static Display *dpy;
  42. static Window win;
  43. static XImage *bitmap;
  44. static GC gc;
  45. static KeySym Left=-1, Right, Down, Up, Alt ;
  46. static void exit_x11(void)
  47. {
  48. XDestroyImage(bitmap);
  49. XDestroyWindow(dpy, win);
  50. XCloseDisplay(dpy);
  51. }
  52. static void init_x11(int width, int height, unsigned *buffer)
  53. {
  54. /* Attempt to open the display */
  55. dpy = XOpenDisplay(NULL);
  56. /* Failure */
  57. if (!dpy)
  58. exit(0);
  59. unsigned long white = WhitePixel(dpy,DefaultScreen(dpy));
  60. unsigned long black = BlackPixel(dpy,DefaultScreen(dpy));
  61. win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
  62. width, height, 0, black, white);
  63. /* We want to be notified when the window appears */
  64. XSelectInput(dpy, win, StructureNotifyMask);
  65. /* Make it appear */
  66. XMapWindow(dpy, win);
  67. XTextProperty tp;
  68. char name[128] = "Mandelbrot";
  69. char *n = name;
  70. Status st = XStringListToTextProperty(&n, 1, &tp);
  71. if (st)
  72. XSetWMName(dpy, win, &tp);
  73. /* Wait for the MapNotify event */
  74. XFlush(dpy);
  75. int depth = DefaultDepth(dpy, DefaultScreen(dpy));
  76. Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
  77. /* Make bitmap */
  78. bitmap = XCreateImage(dpy, visual, depth,
  79. ZPixmap, 0, (char *)buffer,
  80. width, height, 32, 0);
  81. /* Init GC */
  82. gc = XCreateGC(dpy, win, 0, NULL);
  83. XSetForeground(dpy, gc, black);
  84. XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask);
  85. Atom wmDeleteMessage;
  86. wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  87. XSetWMProtocols(dpy, win, &wmDeleteMessage, 1);
  88. Left = XStringToKeysym ("Left");
  89. Right = XStringToKeysym ("Right");
  90. Up = XStringToKeysym ("Up");
  91. Down = XStringToKeysym ("Down");
  92. Alt = XStringToKeysym ("Alt");
  93. }
  94. static int handle_events(void)
  95. {
  96. XEvent event;
  97. XNextEvent(dpy, &event);
  98. KeySym key;
  99. char text[255];
  100. if (event.type == KeyPress)
  101. {
  102. XLookupString(&event.xkey,text,255,&key,0);
  103. if (key == Left)
  104. {
  105. double widthX = rightX - leftX;
  106. leftX -= 0.25*widthX;
  107. rightX -= 0.25*widthX;
  108. }
  109. else if (key == Right)
  110. {
  111. double widthX = rightX - leftX;
  112. leftX += 0.25*widthX;
  113. rightX += 0.25*widthX;
  114. }
  115. else if (key == Up)
  116. {
  117. double heightY = topY - bottomY;
  118. topY += 0.25*heightY;
  119. bottomY += 0.25*heightY;
  120. }
  121. else if (key == Down)
  122. {
  123. double heightY = topY - bottomY;
  124. topY -= 0.25*heightY;
  125. bottomY -= 0.25*heightY;
  126. }
  127. else {
  128. double widthX = rightX - leftX;
  129. double heightY = topY - bottomY;
  130. if (text[0] == '-')
  131. {
  132. /* Zoom out */
  133. leftX -= 0.125*widthX;
  134. rightX += 0.125*widthX;
  135. topY += 0.125*heightY;
  136. bottomY -= 0.125*heightY;
  137. }
  138. else if (text[0] == '+')
  139. {
  140. /* Zoom in */
  141. leftX += 0.125*widthX;
  142. rightX -= 0.125*widthX;
  143. topY -= 0.125*heightY;
  144. bottomY += 0.125*heightY;
  145. }
  146. }
  147. if (text[0]=='q') {
  148. return -1;
  149. }
  150. }
  151. if (event.type==ButtonPress) {
  152. /* tell where the mouse Button was Pressed */
  153. printf("You pressed a button at (%i,%i)\n",
  154. event.xbutton.x,event.xbutton.y);
  155. }
  156. return 0;
  157. }
  158. #endif
  159. /*
  160. * OpenCL kernel
  161. */
  162. #ifdef STARPU_USE_OPENCL
  163. char *mandelbrot_opencl_src = "\
  164. #pragma OPENCL EXTENSION cl_khr_fp64 : enable\n\
  165. #define MIN(a,b) (((a)<(b))? (a) : (b)) \n\
  166. __kernel void mandelbrot_kernel(__global unsigned* a, \n\
  167. double leftX, double topY, \n\
  168. double stepX, double stepY, \n\
  169. int maxIt, int iby, int block_size, int width) \n\
  170. { \n\
  171. size_t id_x = get_global_id(0); \n\
  172. size_t id_y = get_global_id(1); \n\
  173. if ((id_x < width) && (id_y < block_size)) \n\
  174. { \n\
  175. double xc = leftX + id_x * stepX; \n\
  176. double yc = topY - (id_y + iby*block_size) * stepY; \n\
  177. int it; \n\
  178. double x,y; \n\
  179. x = y = (double)0.0; \n\
  180. for (it=0;it<maxIt;it++) \n\
  181. { \n\
  182. double x2 = x*x; \n\
  183. double y2 = y*y; \n\
  184. if (x2+y2 > 4.0) break; \n\
  185. double twoxy = (double)2.0*x*y; \n\
  186. x = x2 - y2 + xc; \n\
  187. y = twoxy + yc; \n\
  188. } \n\
  189. unsigned int v = MIN((1024*((float)(it)/(2000))), 256); \n\
  190. a[id_x + width * id_y] = (v<<16|(255-v)<<8); \n\
  191. } \n\
  192. }";
  193. static struct starpu_opencl_program opencl_programs;
  194. static void compute_block_opencl(void *descr[], void *cl_arg)
  195. {
  196. int iby, block_size;
  197. double stepX, stepY;
  198. starpu_unpack_cl_args(cl_arg, &iby, &block_size, &stepX, &stepY);
  199. cl_mem data = (cl_mem)STARPU_VECTOR_GET_PTR(descr[0]);
  200. cl_kernel kernel;
  201. cl_command_queue queue;
  202. int id = starpu_worker_get_id();
  203. int devid = starpu_worker_get_devid(id);
  204. starpu_opencl_load_kernel(&kernel, &queue, &opencl_programs, "mandelbrot_kernel", devid);
  205. clSetKernelArg(kernel, 0, sizeof(cl_mem), &data);
  206. clSetKernelArg(kernel, 1, sizeof(double), &leftX);
  207. clSetKernelArg(kernel, 2, sizeof(double), &topY);
  208. clSetKernelArg(kernel, 3, sizeof(double), &stepX);
  209. clSetKernelArg(kernel, 4, sizeof(double), &stepY);
  210. clSetKernelArg(kernel, 5, sizeof(int), &maxIt);
  211. clSetKernelArg(kernel, 6, sizeof(int), &iby);
  212. clSetKernelArg(kernel, 7, sizeof(int), &block_size);
  213. clSetKernelArg(kernel, 8, sizeof(int), &width);
  214. unsigned dim = 16;
  215. size_t local[2] = {dim, 1};
  216. size_t global[2] = {width, block_size};
  217. clEnqueueNDRangeKernel(queue, kernel, 2, NULL, global, local, 0, NULL, NULL);
  218. clFinish(queue);
  219. starpu_opencl_release_kernel(kernel);
  220. }
  221. #endif
  222. /*
  223. * CPU kernel
  224. */
  225. static void compute_block(void *descr[], void *cl_arg)
  226. {
  227. int ix, iy;
  228. int iby, block_size;
  229. double stepX, stepY;
  230. starpu_unpack_cl_args(cl_arg, &iby, &block_size, &stepX, &stepY);
  231. unsigned *data = (unsigned *)STARPU_VECTOR_GET_PTR(descr[0]);
  232. int local_iy;
  233. for (local_iy = 0; local_iy < block_size; local_iy++)
  234. {
  235. iy = iby*block_size + local_iy;
  236. for (ix = 0; ix < width; ix++)
  237. {
  238. double cx = leftX + ix * stepX;
  239. double cy = topY - iy * stepY;
  240. // Z = X+I*Y
  241. double x = 0;
  242. double y = 0;
  243. int it;
  244. for (it = 0; it < maxIt; it++)
  245. {
  246. double x2 = x*x;
  247. double y2 = y*y;
  248. // Stop iterations when |Z| > 2
  249. if (x2 + y2 > 4.0)
  250. break;
  251. double twoxy = 2.0*x*y;
  252. // Z = Z^2 + C
  253. x = x2 - y2 + cx;
  254. y = twoxy + cy;
  255. }
  256. unsigned int v = STARPU_MIN((1024*((float)(it)/(2000))), 256);
  257. data[ix + local_iy*width] = (v<<16|(255-v)<<8);
  258. }
  259. }
  260. }
  261. static starpu_codelet mandelbrot_cl = {
  262. .where = STARPU_CPU|STARPU_OPENCL,
  263. .type = STARPU_SEQ,
  264. .cpu_func = compute_block,
  265. #ifdef STARPU_USE_OPENCL
  266. .opencl_func = compute_block_opencl,
  267. #endif
  268. .nbuffers = 1
  269. };
  270. static void parse_args(int argc, char **argv)
  271. {
  272. int i;
  273. for (i = 1; i < argc; i++) {
  274. if (strcmp(argv[i], "-h") == 0) {
  275. fprintf(stderr, "Usage: %s [-h] [ -width 800] [-height 600] [-nblocks 16] [-no-x11] [-pos leftx:rightx:bottomy:topy]\n", argv[0]);
  276. exit(-1);
  277. }
  278. if (strcmp(argv[i], "-width") == 0) {
  279. char *argptr;
  280. width = strtol(argv[++i], &argptr, 10);
  281. }
  282. if (strcmp(argv[i], "-height") == 0) {
  283. char *argptr;
  284. height = strtol(argv[++i], &argptr, 10);
  285. }
  286. if (strcmp(argv[i], "-nblocks") == 0) {
  287. char *argptr;
  288. nblocks = strtol(argv[++i], &argptr, 10);
  289. }
  290. if (strcmp(argv[i], "-pos") == 0) {
  291. int ret = sscanf(argv[++i], "%lf:%lf:%lf:%lf", &leftX, &rightX, &bottomY, &topY);
  292. assert(ret == 4);
  293. }
  294. if (strcmp(argv[i], "-demo") == 0) {
  295. demo = 1;
  296. leftX = -50.22749575062760;
  297. rightX = 48.73874621262927;
  298. topY = -49.35016705749115;
  299. bottomY = 49.64891691946615;
  300. }
  301. if (strcmp(argv[i], "-no-x11") == 0) {
  302. #ifdef STARPU_HAVE_X11
  303. use_x11 = 0;
  304. #endif
  305. }
  306. }
  307. }
  308. int main(int argc, char **argv)
  309. {
  310. parse_args(argc, argv);
  311. starpu_init(NULL);
  312. unsigned *buffer;
  313. starpu_data_malloc_pinned_if_possible((void **)&buffer, height*width*sizeof(unsigned));
  314. #ifdef STARPU_HAVE_X11
  315. if (use_x11)
  316. init_x11(width, height, buffer);
  317. #endif
  318. int block_size = height/nblocks;
  319. STARPU_ASSERT((height % nblocks) == 0);
  320. #ifdef STARPU_USE_OPENCL
  321. starpu_opencl_load_opencl_from_string(mandelbrot_opencl_src, &opencl_programs);
  322. #endif
  323. starpu_data_handle block_handles[nblocks];
  324. int iby;
  325. for (iby = 0; iby < nblocks; iby++)
  326. {
  327. unsigned *data = &buffer[iby*block_size*width];
  328. starpu_vector_data_register(&block_handles[iby], 0,
  329. (uintptr_t)data, block_size*width, sizeof(unsigned));
  330. }
  331. while (1)
  332. {
  333. double stepX = (rightX - leftX)/width;
  334. double stepY = (topY - bottomY)/height;
  335. struct timeval start, end;
  336. gettimeofday(&start, NULL);
  337. for (iby = 0; iby < nblocks; iby++)
  338. {
  339. starpu_insert_task(&mandelbrot_cl,
  340. STARPU_VALUE, &iby, sizeof(iby),
  341. STARPU_VALUE, &block_size, sizeof(block_size),
  342. STARPU_VALUE, &stepX, sizeof(stepX),
  343. STARPU_VALUE, &stepY, sizeof(stepY),
  344. STARPU_W, block_handles[iby],
  345. 0);
  346. }
  347. for (iby = 0; iby < nblocks; iby++)
  348. {
  349. starpu_data_acquire(block_handles[iby], STARPU_R);
  350. #ifdef STARPU_HAVE_X11
  351. if (use_x11)
  352. {
  353. XPutImage(dpy, win, gc, bitmap,
  354. 0, iby*block_size,
  355. 0, iby*block_size,
  356. width, block_size);
  357. }
  358. #endif
  359. starpu_data_release(block_handles[iby]);
  360. }
  361. gettimeofday(&end, NULL);
  362. double timing = (double)((end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec));
  363. fprintf(stderr, "Time to generate frame : %f ms\n", timing/1000.0);
  364. fprintf(stderr, "%14.14f:%14.14f:%14.14f:%14.14f\n", leftX, rightX, bottomY, topY);
  365. #ifdef STARPU_HAVE_X11
  366. if (demo)
  367. {
  368. /* Zoom in */
  369. double zoom_factor = 0.05;
  370. double widthX = rightX - leftX;
  371. double heightY = topY - bottomY;
  372. leftX += (zoom_factor/2)*widthX;
  373. rightX -= (zoom_factor/2)*widthX;
  374. topY -= (zoom_factor/2)*heightY;
  375. bottomY += (zoom_factor/2)*heightY;
  376. }
  377. else if (use_x11 && handle_events())
  378. break;
  379. #endif
  380. }
  381. #ifdef STARPU_HAVE_X11
  382. if (use_x11)
  383. exit_x11();
  384. #endif
  385. for (iby = 0; iby < nblocks; iby++)
  386. starpu_data_unregister(block_handles[iby]);
  387. starpu_data_free_pinned_if_possible(buffer);
  388. starpu_shutdown();
  389. return 0;
  390. }