runcl

A minimal OpenCL harness for rendering images
git clone git://pollux.codes/git/runcl.git
Log | Files | Refs | README | LICENSE

runcl.c (8612B)


      1 #define CL_TARGET_OPENCL_VERSION 300
      2 #define USAGE                                                                  \
      3   "usage: runcl -p <platform-index> -w <image-width> -h <image-height> "       \
      4   "input-opencl-file output-png-file"
      5 
      6 #define DEF_WIDTH 640
      7 #define DEF_HEIGHT 480
      8 #define MAX_SRC_SIZE 1048576
      9 #define MAX_PLATFORM_COUNT 16
     10 #define MAX_PLATFORM_NAME_SIZE 256
     11 
     12 #define STR_LITERAL(x) #x
     13 #define STRINGIFY(x) STR_LITERAL(x)
     14 
     15 #include <CL/cl.h>
     16 #include <getopt.h>
     17 #include <png.h>
     18 #include <stdio.h>
     19 #include <unistd.h>
     20 
     21 struct Args {
     22 	int             img_width;
     23 	int             img_height;
     24 	int             platform_index;
     25 	char           *source_file;
     26 	char           *out_file;
     27 };
     28 
     29 void
     30 print_help() {
     31 	printf(USAGE "\n");
     32 }
     33 
     34 void
     35 print_version() {
     36 	printf(STRINGIFY(PROG_NAME) " " STRINGIFY(VERSION) "\n");
     37 	printf("Build:   " STRINGIFY(BUILD_TYPE) "\n");
     38 	printf("CFLAGS:  " STRINGIFY(CFLAGS) "\n");
     39 	printf("LDFLAGS: " STRINGIFY(LDFLAGS) "\n");
     40 }
     41 
     42 int
     43 parse_args(int argc, char **argv, struct Args *out) {
     44 
     45 	int             arg;
     46 
     47 	out->img_width = DEF_WIDTH;
     48 	out->img_height = DEF_HEIGHT;
     49 	out->platform_index = 0;
     50 
     51 	while((arg = getopt(argc, argv, "p:w:h:Hv")) != -1) {
     52 		switch (arg) {
     53 		case 'p':
     54 			out->platform_index = atoi(optarg);
     55 			break;
     56 		case 'w':
     57 			out->img_width = atoi(optarg);
     58 			break;
     59 		case 'h':
     60 			out->img_height = atoi(optarg);
     61 			break;
     62 		case 'H':
     63 			print_help();
     64 			exit(0);
     65 		case 'v':
     66 			print_version();
     67 			exit(0);
     68 		case '?':
     69 			return -1;
     70 		}
     71 	}
     72 
     73 	if(argc < optind + 2)
     74 		return -1;
     75 
     76 	out->source_file = argv[optind];
     77 	out->out_file = argv[optind + 1];
     78 
     79 	return 0;
     80 }
     81 
     82 int
     83 read_source(char *path, char *out, size_t max_size, size_t *source_size) {
     84 
     85 	FILE           *fp;
     86 
     87 	fp = fopen(path, "r");
     88 	if(!fp) {
     89 		return -1;
     90 	}
     91 
     92 	*source_size = fread(out, 1, max_size, fp);
     93 
     94 	if(!feof(fp)) {
     95 		return -1;
     96 	}
     97 
     98 	fclose(fp);
     99 
    100 	return 0;
    101 }
    102 
    103 int
    104 get_device(int platform_index, cl_device_id *device) {
    105 
    106 	cl_platform_id  platforms[MAX_PLATFORM_COUNT];
    107 	cl_uint         num_platforms;
    108 	cl_platform_id  platform_id;
    109 
    110 	char            name[MAX_PLATFORM_NAME_SIZE];
    111 
    112 	int             res;
    113 
    114 	res = clGetPlatformIDs(MAX_PLATFORM_COUNT, platforms, &num_platforms);
    115 
    116 	if(platform_index >= (int)num_platforms) {
    117 		fprintf(stderr,
    118 		        "Platform index exceeds number of platforms...\n");
    119 		return -1;
    120 	}
    121 
    122 	platform_id = platforms[platform_index];
    123 
    124 	res = clGetPlatformInfo(platform_id, CL_PLATFORM_NAME,
    125 	                        MAX_PLATFORM_NAME_SIZE, name, NULL);
    126 
    127 	if(res == CL_OUT_OF_HOST_MEMORY) {
    128 		fprintf(stderr, "Insufficient resources...\n");
    129 		return -1;
    130 	}
    131 
    132 	printf("Platform in Use: %s\n", name);
    133 
    134 	res = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, device, NULL);
    135 
    136 	if(res == CL_DEVICE_NOT_FOUND) {
    137 		fprintf(stderr,
    138 		        "Specified platform does not provide a GPU...\n");
    139 		return -1;
    140 	}
    141 
    142 	if(res == CL_OUT_OF_RESOURCES || res == CL_OUT_OF_HOST_MEMORY) {
    143 		fprintf(stderr, "Insufficient resources...\n");
    144 		return -1;
    145 	}
    146 
    147 	return 0;
    148 }
    149 
    150 int
    151 write_image(char *out_file, int width, int height, float *raster) {
    152 
    153 	FILE           *fp;
    154 
    155 	png_structp     png_ptr;
    156 	png_infop       info_ptr;
    157 	png_bytep       row;
    158 
    159 	row = (png_bytep) malloc(3 * width * sizeof(png_byte));
    160 
    161 	fp = fopen(out_file, "wb");
    162 
    163 	if(!fp) {
    164 		printf("Couldn't open output file\n");
    165 		free(row);
    166 		return -1;
    167 	}
    168 
    169 	png_ptr =
    170 	        png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
    171 	                                NULL);
    172 
    173 	info_ptr = png_create_info_struct(png_ptr);
    174 
    175 	png_init_io(png_ptr, fp);
    176 
    177 	png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
    178 	             PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
    179 	             PNG_FILTER_TYPE_BASE);
    180 
    181 	png_write_info(png_ptr, info_ptr);
    182 
    183 	int             x, y;
    184 
    185 	for(y = 0; y < height; y++) {
    186 		for(x = 0; x < width; x++) {
    187 			row[x * 3 + 0] =
    188 			        (int)(255 * raster[3 * (y * width + x) + 0]);
    189 			row[x * 3 + 1] =
    190 			        (int)(255 * raster[3 * (y * width + x) + 1]);
    191 			row[x * 3 + 2] =
    192 			        (int)(255 * raster[3 * (y * width + x) + 2]);
    193 		}
    194 		png_write_row(png_ptr, row);
    195 	}
    196 
    197 	png_write_end(png_ptr, NULL);
    198 
    199 	fclose(fp);
    200 	free(row);
    201 
    202 	return 0;
    203 }
    204 
    205 int
    206 main(int argc, char **argv) {
    207 
    208 	struct Args     args;
    209 	int             res;
    210 
    211 	float          *data_out;
    212 	int             data_out_size;
    213 
    214 	cl_device_id    device_id;
    215 
    216 	cl_mem          out_buff;
    217 
    218 	cl_context      context = NULL;
    219 	cl_command_queue command_queue = NULL;
    220 	cl_kernel       kernel = NULL;
    221 	cl_program      program = NULL;
    222 
    223 	char           *source_str;
    224 	size_t          source_size;
    225 
    226 	size_t          g_work_size, l_work_size;
    227 
    228 	res = parse_args(argc, argv, &args);
    229 
    230 	if(res) {
    231 		fprintf(stderr, "Error parsing command-line arguments...\n");
    232 
    233 		// This is before anything has been allocated, so no cleanup is needed.
    234 		exit(1);
    235 	}
    236 
    237 	data_out_size = sizeof(float) * args.img_width * args.img_height * 3;
    238 
    239 	data_out = (float *)malloc(data_out_size);
    240 	source_str = (char *)malloc(MAX_SRC_SIZE * sizeof(char));
    241 
    242 	res = read_source(args.source_file, source_str, MAX_SRC_SIZE,
    243 	                  &source_size);
    244 
    245 	if(res) {
    246 		fprintf(stderr, "Error reading source file: %s",
    247 		        args.source_file);
    248 		goto error;
    249 	}
    250 	// Get Platform & Device
    251 	res = get_device(args.platform_index, &device_id);
    252 
    253 	if(res) {
    254 		goto error;
    255 	}
    256 	// Create Context & Queue
    257 	context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &res);
    258 
    259 	if(res == CL_DEVICE_NOT_AVAILABLE) {
    260 		fprintf(stderr, "Selected platform/device is not available...");
    261 		goto error;
    262 	}
    263 
    264 	if(res == CL_OUT_OF_HOST_MEMORY) {
    265 		fprintf(stderr, "Insufficient resources...\n");
    266 		goto error;
    267 	}
    268 
    269 	command_queue =
    270 	        clCreateCommandQueueWithProperties(context, device_id, NULL,
    271 	                                           &res);
    272 
    273 	if(res == CL_OUT_OF_RESOURCES || res == CL_OUT_OF_HOST_MEMORY) {
    274 		fprintf(stderr, "Insufficient resources...\n");
    275 		goto error;
    276 	}
    277 	// Create Buffers
    278 	out_buff =
    279 	        clCreateBuffer(context, CL_MEM_WRITE_ONLY, data_out_size, NULL,
    280 	                       &res);
    281 
    282 	if(res == CL_INVALID_BUFFER_SIZE) {
    283 		fprintf(stderr, "Invalid buffer size: %d...", data_out_size);
    284 	}
    285 
    286 	if(res == CL_MEM_OBJECT_ALLOCATION_FAILURE || res == CL_OUT_OF_RESOURCES
    287 	   || res == CL_OUT_OF_HOST_MEMORY) {
    288 		fprintf(stderr, "Insufficient resources...\n");
    289 		goto error;
    290 	}
    291 
    292 	program =
    293 	        clCreateProgramWithSource(context, 1,
    294 	                                  (const char **)&source_str,
    295 	                                  &source_size, &res);
    296 
    297 	if(res == CL_OUT_OF_RESOURCES || res == CL_OUT_OF_HOST_MEMORY) {
    298 		fprintf(stderr, "Insufficient resources...\n");
    299 		goto error;
    300 	}
    301 
    302 	res = clBuildProgram(program, 1, &device_id, NULL, NULL, NULL);
    303 
    304 	if(res != CL_SUCCESS) {
    305 		fprintf(stderr, "Failed to build program!\n");
    306 		char            build_log[16348];
    307 
    308 		clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG,
    309 		                      sizeof(build_log), build_log, NULL);
    310 		fprintf(stderr, "Build Error: %s\n", build_log);
    311 		goto error;
    312 	}
    313 
    314 	kernel = clCreateKernel(program, "render", &res);
    315 
    316 	if(res != CL_SUCCESS) {
    317 		fprintf(stderr, "Error creating kernel: %d\n", res);
    318 		goto error;
    319 	}
    320 
    321 	res = clSetKernelArg(kernel, 0, sizeof(cl_int),
    322 	                     (void *)&args.img_width);
    323 
    324 	if(res != CL_SUCCESS) {
    325 		fprintf(stderr, "Error setting arguments...");
    326 		goto error;
    327 	}
    328 
    329 	res = clSetKernelArg(kernel, 1, sizeof(cl_int),
    330 	                     (void *)&args.img_height);
    331 
    332 	if(res != CL_SUCCESS) {
    333 		fprintf(stderr, "Error setting arguments...");
    334 		goto error;
    335 	}
    336 
    337 	res = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *)&out_buff);
    338 
    339 	if(res != CL_SUCCESS) {
    340 		fprintf(stderr, "Error setting arguments...");
    341 		goto error;
    342 	}
    343 
    344 	l_work_size = 128;
    345 	g_work_size =
    346 	        (args.img_width * args.img_height / l_work_size +
    347 	         1) * l_work_size;
    348 
    349 	res = clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL,
    350 	                             &g_work_size, &l_work_size, 0, NULL, NULL);
    351 
    352 	if(res != CL_SUCCESS) {
    353 		printf("Error executing kernel: %d\n", (int)res);
    354 		goto error;
    355 	}
    356 
    357 	res = clEnqueueReadBuffer(command_queue, out_buff, CL_TRUE, 0,
    358 	                          data_out_size, (void *)data_out, 0, NULL,
    359 	                          NULL);
    360 
    361 	if(res != CL_SUCCESS) {
    362 		printf("Error reading result: %d\n", (int)res);
    363 		goto error;
    364 	}
    365 	// Write PNG
    366 	write_image(args.out_file, args.img_width, args.img_height, data_out);
    367 
    368 	error:
    369 
    370 	clFlush(command_queue);
    371 	clFinish(command_queue);
    372 
    373 	clReleaseMemObject(out_buff);
    374 
    375 	clReleaseCommandQueue(command_queue);
    376 	clReleaseContext(context);
    377 
    378 	free(data_out);
    379 	free(source_str);
    380 
    381 	return 0;
    382 }