diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cl/spirt.cl | 33 | ||||
-rw-r--r-- | src/farbfeld.c | 40 | ||||
-rw-r--r-- | src/farbfeld.h | 20 | ||||
-rw-r--r-- | src/spirt.c | 357 | ||||
-rw-r--r-- | src/util.c | 201 | ||||
-rw-r--r-- | src/util.h | 24 |
6 files changed, 675 insertions, 0 deletions
diff --git a/src/cl/spirt.cl b/src/cl/spirt.cl new file mode 100644 index 0000000..300f552 --- /dev/null +++ b/src/cl/spirt.cl @@ -0,0 +1,33 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__kernel void +hello(__global ushort *const canvas) +{ + size_t idx, w, h, i, j; + + w = get_global_size(0); + h = get_global_size(1); + + i = get_global_id(0); + j = get_global_id(1); + + idx = (w * j + i) * 4; + + canvas[idx] = (ushort)((65535 * i) / (w - 1)); + canvas[idx + 1] = (ushort)((65535 * j) / (h - 1)); + canvas[idx + 2] = 16383; + canvas[idx + 3] = 65535; +} diff --git a/src/farbfeld.c b/src/farbfeld.c new file mode 100644 index 0000000..71903b1 --- /dev/null +++ b/src/farbfeld.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "util.h" + +#include <arpa/inet.h> +#include <stdint.h> +#include <stdio.h> + +/* write farbfeld image to fd + * - canvas will be mutated unless host architecture is big endian */ +void +farbfeld_write(FILE *const fd, uint16_t *const canvas, const size_t canvas_size, + uint32_t width, uint32_t height) +{ + size_t i; + + width = htonl(width); + height = htonl(height); + + for (i = 0; i < canvas_size; i++) + canvas[i] = htons(canvas[i]); + + efwrite("farbfeld", sizeof(char), 8, fd); + efwrite(&width, sizeof(uint32_t), 1, fd); + efwrite(&height, sizeof(uint32_t), 1, fd); + efwrite(canvas, sizeof(uint16_t), canvas_size, fd); +} diff --git a/src/farbfeld.h b/src/farbfeld.h new file mode 100644 index 0000000..295c2a1 --- /dev/null +++ b/src/farbfeld.h @@ -0,0 +1,20 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdint.h> +#include <stdio.h> + +void farbfeld_write(FILE *fd, uint16_t *canvas, size_t canvas_size, + uint32_t width, uint32_t height); diff --git a/src/spirt.c b/src/spirt.c new file mode 100644 index 0000000..d1e8399 --- /dev/null +++ b/src/spirt.c @@ -0,0 +1,357 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "farbfeld.h" +#include "util.h" + +#include <CL/cl.h> +#include <CL/cl_platform.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef SPIRV_START +# define SPIRV_START _binary_src_cl_spirt_spv_start +#endif +#ifndef SPIRV_END +# define SPIRV_END _binary_src_cl_spirt_spv_end +#endif +#define SPIRV_SIZE ((size_t)SPIRV_END - (size_t)SPIRV_START) + +struct ClRuntime { + cl_platform_id platform; + cl_device_id device; + cl_context context; + cl_command_queue command_queue; + cl_program program; + cl_kernel k_hello; +}; + +static cl_platform_id *get_platforms(cl_uint *num_platforms); +static cl_device_id *get_devices(cl_platform_id platform, cl_uint *num_devices); +static cl_program compile_spirv_program(cl_context context, cl_device_id device, + const void *spirv_start, + size_t spirv_size); + +static struct ClRuntime init(size_t platidx, size_t devidx); +static uint16_t *run_k_hello(struct ClRuntime *runtime, size_t *canvas_size_ret, + size_t image_width, size_t image_height); +static void clean(struct ClRuntime *runtime); + +static long argtonum(const char *arg, long minval, long maxval); + +extern const char *SPIRV_START[]; +extern const char *SPIRV_END[]; + +static cl_platform_id * +get_platforms(cl_uint *const num_platforms) +{ + cl_int err; + + cl_platform_id *platforms; + cl_uint len; + + err = clGetPlatformIDs(0, NULL, &len); + if (err != CL_SUCCESS) + die("clGetPlatformIDs: %s\n", cl_strerror(err)); + warn("number of platforms = %d\n", len); + if (len < 1) + die("clGetPlatformIDs: No OpenCL platforms\n"); + + platforms = calloc(len, sizeof(*platforms)); + if (!platforms) + die("calloc: Out of memory\n"); + err = clGetPlatformIDs(len, platforms, NULL); + if (err != CL_SUCCESS) + die("clGetPlatformIDs: %s\n", cl_strerror(err)); + + *num_platforms = len; + return platforms; +} + +static cl_device_id * +get_devices(const cl_platform_id platform, cl_uint *const num_devices) +{ + cl_int err; + + cl_device_id *devices; + cl_uint len; + + err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, NULL, &len); + if (err != CL_SUCCESS) + die("clGetDeviceIDs: %s\n", cl_strerror(err)); + warn("number of devices in platform = %d\n", len); + if (len < 1) + die("clGetDeviceIDs: No OpenCL devices in platform\n"); + + devices = calloc(len, sizeof(*devices)); + if (!devices) + die("calloc: Out of memory\n"); + err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, len, devices, NULL); + if (err != CL_SUCCESS) + die("clGetDeviceIDs: %s\n", cl_strerror(err)); + + *num_devices = len; + return devices; +} + +static cl_program +compile_spirv_program(const cl_context context, const cl_device_id device, + const void *const spirv_start, const size_t spirv_size) +{ + cl_int err, build_err; + cl_program program; + + program = clCreateProgramWithIL(context, spirv_start, spirv_size, &err); + if (err != CL_SUCCESS) + die("clCreateProgramWithIL: %s\n", cl_strerror(err)); + + build_err = clBuildProgram(program, 1, &device, NULL, NULL, NULL); + if (build_err != CL_SUCCESS) { + cl_char *log; + size_t buffer_size; + + err = clGetProgramBuildInfo(program, device, + CL_PROGRAM_BUILD_LOG, 0, NULL, + &buffer_size); + if (err != CL_SUCCESS) + die("clGetProgramBuildInfo: %s\n", cl_strerror(err)); + if (buffer_size < 1) + die("clGetProgramBuildInfo: " + "Build log buffer is empty\n"); + + log = calloc(buffer_size, sizeof(*log)); + if (!log) + die("calloc: Out of memory\n"); + err = clGetProgramBuildInfo( + program, device, CL_PROGRAM_BUILD_LOG, + sizeof(*log) * buffer_size, log, NULL); + if (err != CL_SUCCESS) + die("clGetProgramBuildInfo: %s\n", cl_strerror(err)); + + efwrite(log, sizeof(*log), buffer_size, stderr); + + free(log); + die("\nclBuildProgram: %s\n", cl_strerror(build_err)); + } + + return program; +} + +static struct ClRuntime +init(const size_t platidx, const size_t devidx) +{ + cl_int err; + + cl_platform_id *platforms; + cl_device_id *devices; + cl_uint num_platforms; + cl_uint num_devices; + + struct ClRuntime runtime; + + platforms = get_platforms(&num_platforms); + if (platidx >= num_platforms) + die("get_platforms: Index out of range\n"); + runtime.platform = platforms[platidx]; + free(platforms); + + devices = get_devices(runtime.platform, &num_devices); + if (devidx >= num_devices) + die("get_devices: Index out of range\n"); + runtime.device = devices[devidx]; + free(devices); + + runtime.context = + clCreateContext(NULL, 1, &runtime.device, NULL, NULL, &err); + if (err != CL_SUCCESS) + die("clCreateContext: %s\n", cl_strerror(err)); + + runtime.command_queue = clCreateCommandQueueWithProperties( + runtime.context, runtime.device, NULL, &err); + if (err != CL_SUCCESS) + die("clCreateCommandQueueWithProperties: %s\n", + cl_strerror(err)); + + runtime.program = compile_spirv_program(runtime.context, runtime.device, + SPIRV_START, SPIRV_SIZE); + + runtime.k_hello = clCreateKernel(runtime.program, "hello", &err); + if (err != CL_SUCCESS) + die("clCreateKernel: %s\n", cl_strerror(err)); + + return runtime; +} + +static uint16_t * +run_k_hello(struct ClRuntime *const runtime, size_t *const canvas_size_ret, + const size_t image_width, const size_t image_height) +{ + cl_int err; + + size_t global_item_size[2]; + + uint16_t *h_canvas; + cl_mem d_canvas; + size_t canvas_size; + + canvas_size = (sizeof("RGBA") - 1) * image_width * image_height; + + h_canvas = calloc(canvas_size, sizeof(*h_canvas)); + if (!h_canvas) + die("calloc: Out of memory\n"); + + d_canvas = clCreateBuffer(runtime->context, CL_MEM_WRITE_ONLY, + sizeof(*h_canvas) * canvas_size, NULL, &err); + if (err != CL_SUCCESS) + die("clCreateBuffer: %s\n", cl_strerror(err)); + + err = clSetKernelArg(runtime->k_hello, 0, sizeof(d_canvas), &d_canvas); + if (err != CL_SUCCESS) + die("clSetKernelArg: %s\n", cl_strerror(err)); + + global_item_size[0] = image_width; + global_item_size[1] = image_height; + err = clEnqueueNDRangeKernel(runtime->command_queue, runtime->k_hello, + 2, NULL, global_item_size, NULL, 0, NULL, + NULL); + if (err != CL_SUCCESS) + die("clEnqueueNDRangeKernel: %s\n", cl_strerror(err)); + + err = clEnqueueReadBuffer(runtime->command_queue, d_canvas, CL_TRUE, 0, + sizeof(*h_canvas) * canvas_size, h_canvas, 0, + NULL, NULL); + if (err != CL_SUCCESS) + die("clEnqueueReadBuffer: %s\n", cl_strerror(err)); + + clReleaseMemObject(d_canvas); + + *canvas_size_ret = canvas_size; + return h_canvas; +} + +static void +clean(struct ClRuntime *const runtime) +{ + clFlush(runtime->command_queue); + clFinish(runtime->command_queue); + + clReleaseKernel(runtime->k_hello); + clReleaseProgram(runtime->program); + clReleaseCommandQueue(runtime->command_queue); + clReleaseContext(runtime->context); +} + +static long +argtonum(const char *const arg, const long minval, const long maxval) +{ + long ret; + char *endptr; + + if (minval > maxval) + die("argtonum: Invalid range\n"); + if (!arg) + die("argtonum: Missing argument\n"); + + errno = 0; + ret = strtol(arg, &endptr, 10); + if (errno) + die("strtol: %s\n", strerror(errno)); + if (arg == endptr || *endptr) + die("strtol: Invalid integer\n"); + + if (ret < minval || ret > maxval) + die("argtonum: Argument out of range\n"); + + return ret; +} + +int +main(int argc, char *argv[]) +{ + long opt_platidx, opt_devidx, opt_width, opt_height; + FILE *fd; + + struct ClRuntime runtime; + + uint16_t *h_canvas; + size_t canvas_size; + + opt_platidx = 0; + opt_devidx = 0; + opt_width = 256; + opt_height = 256; + + if (argv[0]) + argv++, argc--; + + for (; argv[0] && argv[0][0] == '-' && argv[0][1]; argv++, argc--) { + char flag; + + if (argv[0][2]) + die("arg_parse: Invalid flag '%s'\n", argv[0]); + + flag = argv[0][1]; + argv++, argc--; + + switch (flag) { + case 'p': + opt_platidx = argtonum(argv[0], 0, LONG_MAX); + break; + case 'd': + opt_devidx = argtonum(argv[0], 0, LONG_MAX); + break; + case 'w': + opt_width = argtonum(argv[0], 1, LONG_MAX); + break; + case 'h': + opt_height = argtonum(argv[0], 1, LONG_MAX); + break; + case '-': + goto end_flags; + default: + die("arg_parse: Unknown flag '-%c'\n", flag); + } + } + +end_flags: + if (argc > 1) + die("arg_parse: Unexpected argument\n"); + + fd = stdout; + if (argv[0]) { + errno = 0; + fd = fopen(argv[0], "w"); + if (errno) + die("fopen: %s\n", strerror(errno)); + if (!fd) + die("fopen: Returned NULL\n"); + } + + runtime = init(opt_platidx, opt_devidx); + + h_canvas = run_k_hello(&runtime, &canvas_size, opt_width, opt_height); + + farbfeld_write(fd, h_canvas, canvas_size, opt_width, opt_height); + fclose(fd); + + free(h_canvas); + clean(&runtime); + + return 0; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..bf06bca --- /dev/null +++ b/src/util.c @@ -0,0 +1,201 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <CL/cl.h> +#include <CL/cl_platform.h> +#include <CL/cl_version.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +void warn(const char *fmt, ...); +void die(const char *fmt, ...); + +void efwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); + +const char *cl_strerror(cl_int err); + +void +warn(const char *const fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void +die(const char *const fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +void +efwrite(const void *const ptr, const size_t size, const size_t nmemb, + FILE *const stream) +{ + if (fwrite(ptr, size, nmemb, stream) != nmemb) + die("fwrite: Write failed\n"); +} + +const char * +cl_strerror(const cl_int err) +{ + switch (err) { + case CL_SUCCESS: + return "CL_SUCCESS"; + case CL_DEVICE_NOT_FOUND: + return "CL_DEVICE_NOT_FOUND"; + case CL_DEVICE_NOT_AVAILABLE: + return "CL_DEVICE_NOT_AVAILABLE"; + case CL_COMPILER_NOT_AVAILABLE: + return "CL_COMPILER_NOT_AVAILABLE"; + case CL_MEM_OBJECT_ALLOCATION_FAILURE: + return "CL_MEM_OBJECT_ALLOCATION_FAILURE"; + case CL_OUT_OF_RESOURCES: + return "CL_OUT_OF_RESOURCES"; + case CL_OUT_OF_HOST_MEMORY: + return "CL_OUT_OF_HOST_MEMORY"; + case CL_PROFILING_INFO_NOT_AVAILABLE: + return "CL_PROFILING_INFO_NOT_AVAILABLE"; + case CL_MEM_COPY_OVERLAP: + return "CL_MEM_COPY_OVERLAP"; + case CL_IMAGE_FORMAT_MISMATCH: + return "CL_IMAGE_FORMAT_MISMATCH"; + case CL_IMAGE_FORMAT_NOT_SUPPORTED: + return "CL_IMAGE_FORMAT_NOT_SUPPORTED"; + case CL_BUILD_PROGRAM_FAILURE: + return "CL_BUILD_PROGRAM_FAILURE"; + case CL_MAP_FAILURE: + return "CL_MAP_FAILURE"; + case CL_INVALID_VALUE: + return "CL_INVALID_VALUE"; + case CL_INVALID_DEVICE_TYPE: + return "CL_INVALID_DEVICE_TYPE"; + case CL_INVALID_PLATFORM: + return "CL_INVALID_PLATFORM"; + case CL_INVALID_DEVICE: + return "CL_INVALID_DEVICE"; + case CL_INVALID_CONTEXT: + return "CL_INVALID_CONTEXT"; + case CL_INVALID_QUEUE_PROPERTIES: + return "CL_INVALID_QUEUE_PROPERTIES"; + case CL_INVALID_COMMAND_QUEUE: + return "CL_INVALID_COMMAND_QUEUE"; + case CL_INVALID_HOST_PTR: + return "CL_INVALID_HOST_PTR"; + case CL_INVALID_MEM_OBJECT: + return "CL_INVALID_MEM_OBJECT"; + case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: + return "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"; + case CL_INVALID_IMAGE_SIZE: + return "CL_INVALID_IMAGE_SIZE"; + case CL_INVALID_SAMPLER: + return "CL_INVALID_SAMPLER"; + case CL_INVALID_BINARY: + return "CL_INVALID_BINARY"; + case CL_INVALID_BUILD_OPTIONS: + return "CL_INVALID_BUILD_OPTIONS"; + case CL_INVALID_PROGRAM: + return "CL_INVALID_PROGRAM"; + case CL_INVALID_PROGRAM_EXECUTABLE: + return "CL_INVALID_PROGRAM_EXECUTABLE"; + case CL_INVALID_KERNEL_NAME: + return "CL_INVALID_KERNEL_NAME"; + case CL_INVALID_KERNEL_DEFINITION: + return "CL_INVALID_KERNEL_DEFINITION"; + case CL_INVALID_KERNEL: + return "CL_INVALID_KERNEL"; + case CL_INVALID_ARG_INDEX: + return "CL_INVALID_ARG_INDEX"; + case CL_INVALID_ARG_VALUE: + return "CL_INVALID_ARG_VALUE"; + case CL_INVALID_ARG_SIZE: + return "CL_INVALID_ARG_SIZE"; + case CL_INVALID_KERNEL_ARGS: + return "CL_INVALID_KERNEL_ARGS"; + case CL_INVALID_WORK_DIMENSION: + return "CL_INVALID_WORK_DIMENSION"; + case CL_INVALID_WORK_GROUP_SIZE: + return "CL_INVALID_WORK_GROUP_SIZE"; + case CL_INVALID_WORK_ITEM_SIZE: + return "CL_INVALID_WORK_ITEM_SIZE"; + case CL_INVALID_GLOBAL_OFFSET: + return "CL_INVALID_GLOBAL_OFFSET"; + case CL_INVALID_EVENT_WAIT_LIST: + return "CL_INVALID_EVENT_WAIT_LIST"; + case CL_INVALID_EVENT: + return "CL_INVALID_EVENT"; + case CL_INVALID_OPERATION: + return "CL_INVALID_OPERATION"; + case CL_INVALID_GL_OBJECT: + return "CL_INVALID_GL_OBJECT"; + case CL_INVALID_BUFFER_SIZE: + return "CL_INVALID_BUFFER_SIZE"; + case CL_INVALID_MIP_LEVEL: + return "CL_INVALID_MIP_LEVEL"; + case CL_INVALID_GLOBAL_WORK_SIZE: + return "CL_INVALID_GLOBAL_WORK_SIZE"; +#ifdef CL_VERSION_1_1 + case CL_MISALIGNED_SUB_BUFFER_OFFSET: + return "CL_MISALIGNED_SUB_BUFFER_OFFSET"; + case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: + return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST"; + case CL_INVALID_PROPERTY: + return "CL_INVALID_PROPERTY"; +#endif +#ifdef CL_VERSION_1_2 + case CL_COMPILE_PROGRAM_FAILURE: + return "CL_COMPILE_PROGRAM_FAILURE"; + case CL_LINKER_NOT_AVAILABLE: + return "CL_LINKER_NOT_AVAILABLE"; + case CL_LINK_PROGRAM_FAILURE: + return "CL_LINK_PROGRAM_FAILURE"; + case CL_DEVICE_PARTITION_FAILED: + return "CL_DEVICE_PARTITION_FAILED"; + case CL_KERNEL_ARG_INFO_NOT_AVAILABLE: + return "CL_KERNEL_ARG_INFO_NOT_AVAILABLE"; + case CL_INVALID_IMAGE_DESCRIPTOR: + return "CL_INVALID_IMAGE_DESCRIPTOR"; + case CL_INVALID_COMPILER_OPTIONS: + return "CL_INVALID_COMPILER_OPTIONS"; + case CL_INVALID_LINKER_OPTIONS: + return "CL_INVALID_LINKER_OPTIONS"; + case CL_INVALID_DEVICE_PARTITION_COUNT: + return "CL_INVALID_DEVICE_PARTITION_COUNT"; +#endif +#ifdef CL_VERSION_2_0 + case CL_INVALID_PIPE_SIZE: + return "CL_INVALID_PIPE_SIZE"; + case CL_INVALID_DEVICE_QUEUE: + return "CL_INVALID_DEVICE_QUEUE"; +#endif +#ifdef CL_VERSION_2_2 + case CL_INVALID_SPEC_ID: + return "CL_INVALID_SPEC_ID"; + case CL_MAX_SIZE_RESTRICTION_EXCEEDED: + return "CL_MAX_SIZE_RESTRICTION_EXCEEDED"; +#endif + default: + return "OpenCL unknown error"; + } +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..d36746c --- /dev/null +++ b/src/util.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2024 Aiden Gall + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <CL/cl_platform.h> +#include <stdio.h> + +void warn(const char *fmt, ...); +void die(const char *fmt, ...); + +void efwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); + +const char *cl_strerror(cl_int err); |