Fix warning in kernel compilation
[babel] / babel.cc
1 /* usual C/C++ includes */
2 #include <stdio.h>
3 #include <math.h> // for ceil()
4 #include <errno.h>
5
6 /* OpenCL includes */
7 #ifdef __APPLE__
8 #include <OpenCL/cl.h>
9 #else
10 #include <CL/cl.h>
11 #endif
12
13 void check_ocl_error(const cl_int &error, const char *message) {
14         if (error != CL_SUCCESS) {
15                 fprintf(stderr, "error %d %s\n", error, message);
16                 exit(1);
17         }
18 }
19
20 char *read_file(const char *fname) {
21         size_t fsize, readsize;
22         char *buff;
23
24         FILE *fd = fopen(fname, "rb");
25         if (!fd) {
26                 fprintf(stderr, "%s not found\n", fname);
27                 return NULL;
28         }
29
30         fseek(fd, 0, SEEK_END);
31         fsize = ftell(fd);
32
33         buff = (char *)malloc(fsize+1);
34         rewind(fd);
35         readsize = fread(buff, 1, fsize, fd);
36         if (fsize != readsize) {
37                 fprintf(stderr, "could only read %lu/%lu bytes from %s\n",
38                         readsize, fsize, fname);
39                 free(buff);
40                 return NULL;
41         }
42         buff[fsize] = '\0';
43
44         printf("read %lu bytes from %s\n", fsize, fname);
45
46         return buff;
47 }
48
49 #include "babel.h"
50
51 int main(int argc, char **argv) {
52
53         /* sanity check */
54         if (SYMBOLS + 1 != sizeof(alphabet)) {
55                 fprintf(stderr, "Wrong alphabet: %s has %lu symbols, expected %d\n",
56                         alphabet, sizeof(alphabet) - 1, SYMBOLS);
57                 return 2;
58         }
59
60         char page[CHARS_PER_PAGE+1];
61         page[CHARS_PER_PAGE] = '\0';
62
63
64         /* auxiliary buffer to read platform and device info */
65         char buffer[1024];
66
67         /* platform selection */
68         cl_uint num_platforms = 0;
69         cl_platform_id *platform_list = NULL;
70         cl_platform_id platform = NULL;
71
72         clGetPlatformIDs(0, NULL, &num_platforms); // retrieve number of platform IDs
73         platform_list = (cl_platform_id *)calloc(num_platforms, sizeof(cl_platform_id));
74         cl_int error = clGetPlatformIDs(num_platforms, platform_list, NULL); // retrieve the actual platform IDs
75
76         /* a quicker way if we are only interested in the first/default platform, ID, would be to have:
77            clGetPlatformIDs(1, &platform, NULL);
78            */
79
80         check_ocl_error(error, "getting platform IDs");
81
82         printf("%d OpenCL platforms found:\n", num_platforms);
83
84         for (cl_uint i = 0; i < num_platforms; ++i) {
85                 /* last param: actual size of the query result */
86                 error = clGetPlatformInfo(platform_list[i], CL_PLATFORM_NAME, sizeof(buffer), buffer, NULL);
87                 check_ocl_error(error, "getting platform name");
88                 printf("\tplatform %u: %s ", i, buffer);
89                 error = clGetPlatformInfo(platform_list[i], CL_PLATFORM_VENDOR, sizeof(buffer), buffer, NULL);
90                 check_ocl_error(error, "getting platform vendor");
91                 printf(" (%s)\n", buffer);
92         }
93
94         cl_uint platnum = 0;
95         if (argc > 1)
96                 platnum = atoi(argv[1]);
97         platform = platform_list[platnum];
98         printf("using platform %u\n", platnum);
99
100         /* device selection */
101
102         cl_uint num_devs = 0;
103         cl_device_id *device_list = NULL;
104         cl_device_id device = NULL;
105
106         /* possible types: CPU, GPU, ACCELERATOR, DEFAULT, ALL */
107         clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, NULL, &num_devs);
108         device_list = (cl_device_id *)calloc(num_devs, sizeof(cl_device_id));
109         error = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, num_devs, device_list, NULL);
110
111         check_ocl_error(error, "getting device IDs");
112
113         printf("%d devices found:\n", num_devs);
114
115         for (cl_uint i = 0; i < num_devs; ++i) {
116                 /* last param: actual size of the query result */
117                 error = clGetDeviceInfo(device_list[i], CL_DEVICE_NAME, sizeof(buffer), buffer, NULL);
118                 check_ocl_error(error, "getting device name");
119                 printf("\tdevice %u: %s\n", i, buffer);
120         }
121
122         cl_uint devnum = 0;
123         if (argc > 2)
124                 devnum = atoi(argv[2]);
125         device = device_list[devnum];
126         printf("using device %u\n", devnum);
127
128         /* creating a context for one devices */
129
130         cl_context_properties ctx_prop[] = {
131                 CL_CONTEXT_PLATFORM, (cl_context_properties)platform,
132                 0
133         };
134
135         cl_context ctx = clCreateContext(ctx_prop, 1, &device, NULL, NULL, &error);
136         check_ocl_error(error, "creating context");
137
138         /* and a command queue to go with it */
139         cl_command_queue queue = clCreateCommandQueue(ctx, device, CL_QUEUE_PROFILING_ENABLE, &error);
140         check_ocl_error(error, "creating command queue");
141
142
143         /* allocate device memory */
144         cl_mem page_d;
145
146         page_d = clCreateBuffer(ctx, CL_MEM_WRITE_ONLY, CHARS_PER_PAGE, NULL, &error);
147         check_ocl_error(error, "allocating device page memory buffer");
148
149         /* load and build program */
150         char *prog_source = read_file("babel.cl");
151         if (prog_source == NULL)
152                 exit(1);
153
154         cl_program program = clCreateProgramWithSource(ctx, 1, (const char **)&prog_source, NULL, &error);
155         check_ocl_error(error, "creating program");
156
157         /* AMD APP doesn't include the current directory by default, apparently */
158         const char *clopts = "-I.";
159         error = clBuildProgram(program,
160                               1, &device, // device(s)
161                               clopts, // compiler options
162                               NULL, // callback
163                               NULL);
164         if (error == CL_BUILD_PROGRAM_FAILURE) {
165                 size_t logSize = 0;
166                 char *log;
167                 error = clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &logSize);
168                 check_ocl_error(error, "getting program build info size");
169                 log = (char *)malloc(logSize);
170                 error = clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, logSize, log, NULL);
171                 check_ocl_error(error, "getting program build info");
172                 fputs(log, stderr);
173                 fputs("\n", stderr);
174                 exit(1);
175         } else
176                 check_ocl_error(error, "building program");
177
178
179         /* loading the kernel */
180         cl_kernel fillpageKernel = clCreateKernel(program, "fillpage", &error);
181         check_ocl_error(error, "creating kernel");
182
183         error = clSetKernelArg(fillpageKernel, 0, sizeof(page_d), &page_d);
184         check_ocl_error(error, "setting kernel param 0");
185
186         /* group size */
187         size_t group_size = CHARS_PER_LINE;
188         if (argc > 3)
189                 group_size = atoi(argv[3]);
190
191         /* work_size must be a multiple of group_size */
192         size_t work_size = ceil(float(CHARS_PER_PAGE)/group_size)*group_size;
193
194         /* launch kernel, with an event to collect profiling info */
195         cl_ulong startTime, endTime;
196         cl_event evt;
197
198         clEnqueueNDRangeKernel(queue, fillpageKernel,
199                                1,
200                                NULL, &work_size, &group_size,
201                                0, NULL,
202                                &evt);
203
204         error = clFinish(queue); // sync on queue
205         check_ocl_error(error, "finishing queue");
206
207         clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &startTime, NULL);
208         error = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &endTime, NULL);
209         check_ocl_error(error, "getting profiling info");
210
211         printf("Kernel runtime: %gms\n", double(endTime-startTime)/1000000);
212
213         /* copy memory down */
214         error = clEnqueueReadBuffer(queue, page_d, true, 0, CHARS_PER_PAGE, page, 0, NULL, NULL);
215         check_ocl_error(error, "getting results");
216
217         clFinish(queue);
218
219         clReleaseMemObject(page_d);
220
221         clReleaseProgram(program);
222         clReleaseCommandQueue(queue);
223         clReleaseContext(ctx);
224
225         for(size_t line = 0; line < LINES_PER_PAGE; ++line) {
226                 for(size_t col = 0; col < CHARS_PER_LINE; ++col) {
227                         putchar(page[line*CHARS_PER_LINE + col]);
228                 }
229                 puts("");
230         }
231         fflush(stdout);
232 }