Mike Klein | f9ae670 | 2018-06-20 14:05:05 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | // This is a simple OpenCL Hello World that tests you have a functioning OpenCL setup. |
| 9 | |
| 10 | #include <CL/cl.hpp> |
| 11 | #include <initializer_list> |
| 12 | |
| 13 | extern "C" { |
| 14 | #include "cl/assert_cl.h" // for cl(), cl_ok() macros |
| 15 | #include "cl/find_cl.h" // for clFindIdsByName |
| 16 | } |
| 17 | |
| 18 | int main(int argc, char** argv) { |
| 19 | // Find any OpenCL platform+device with these substrings. |
| 20 | const char* platform_match = argc > 1 ? argv[1] : ""; |
| 21 | const char* device_match = argc > 2 ? argv[2] : ""; |
| 22 | |
| 23 | cl_platform_id platform_id; |
| 24 | cl_device_id device_id; |
| 25 | |
| 26 | char device_name[256]; |
| 27 | size_t device_name_len; |
| 28 | |
| 29 | // clFindIdsByName will narrate what it's doing when this is set. |
| 30 | bool verbose = true; |
| 31 | |
| 32 | // The cl() macro prepends cl to its argument, calls it, and asserts that it succeeded, |
| 33 | // printing out the file, line, and somewhat readable version of the error code on failure. |
| 34 | // |
| 35 | // It's generally used to call OpenCL APIs, but here we've written clFindIdsByName to match |
| 36 | // the convention, as its error conditions are just going to be passed along from OpenCL. |
| 37 | cl(FindIdsByName(platform_match, device_match, |
| 38 | &platform_id, &device_id, |
| 39 | sizeof(device_name), device_name, &device_name_len, |
| 40 | verbose)); |
| 41 | |
| 42 | printf("picked %.*s\n", (int)device_name_len, device_name); |
| 43 | |
| 44 | // Allan's code is all C using OpenCL's C API, |
| 45 | // but we can mix that freely with the C++ API found in cl.hpp. |
| 46 | // cl_ok() comes in handy here, which is cl() without the extra cl- prefix. |
| 47 | |
| 48 | cl::Device device(device_id); |
| 49 | |
| 50 | std::string name, |
| 51 | vendor, |
| 52 | extensions; |
| 53 | cl_ok(device.getInfo(CL_DEVICE_NAME, &name)); |
| 54 | cl_ok(device.getInfo(CL_DEVICE_VENDOR, &vendor)); |
| 55 | cl_ok(device.getInfo(CL_DEVICE_EXTENSIONS, &extensions)); |
| 56 | |
| 57 | printf("name %s, vendor %s, extensions:\n%s\n", |
| 58 | name.c_str(), vendor.c_str(), extensions.c_str()); |
| 59 | |
| 60 | std::vector<cl::Device> devices = { device }; |
| 61 | |
| 62 | // Some APIs can't return their cl_int error but might still fail, |
| 63 | // so they take a pointer. cl_ok() is really handy here too. |
| 64 | cl_int ok; |
| 65 | cl::Context ctx(devices, |
| 66 | nullptr/*optional cl_context_properties*/, |
| 67 | nullptr/*optional error reporting callback*/, |
| 68 | nullptr/*context arguement for error reporting callback*/, |
| 69 | &ok); |
| 70 | cl_ok(ok); |
| 71 | |
| 72 | cl::Program program(ctx, |
| 73 | "__kernel void mul(__global const float* a, " |
| 74 | " __global const float* b, " |
| 75 | " __global float* dst) {" |
| 76 | " int i = get_global_id(0); " |
| 77 | " dst[i] = a[i] * b[i]; " |
| 78 | "} ", |
| 79 | /*and build now*/true, |
| 80 | &ok); |
| 81 | cl_ok(ok); |
| 82 | |
| 83 | std::vector<float> a,b,p; |
| 84 | for (int i = 0; i < 1000; i++) { |
| 85 | a.push_back(+i); |
| 86 | b.push_back(-i); |
| 87 | p.push_back( 0); |
| 88 | } |
| 89 | |
| 90 | cl::Buffer A(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR , sizeof(float)*a.size(), a.data()), |
| 91 | B(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR , sizeof(float)*b.size(), b.data()), |
| 92 | P(ctx, CL_MEM_WRITE_ONLY| CL_MEM_HOST_READ_ONLY, sizeof(float)*p.size()); |
| 93 | |
| 94 | cl::Kernel mul(program, "mul", &ok); |
| 95 | cl_ok(ok); |
| 96 | cl_ok(mul.setArg(0, A)); |
| 97 | cl_ok(mul.setArg(1, B)); |
| 98 | cl_ok(mul.setArg(2, P)); |
| 99 | |
| 100 | cl::CommandQueue queue(ctx, device); |
| 101 | |
| 102 | cl_ok(queue.enqueueNDRangeKernel(mul, cl::NDRange(0) /*offset*/ |
| 103 | , cl::NDRange(1000) /*size*/)); |
| 104 | |
| 105 | cl_ok(queue.enqueueReadBuffer(P, true/*block until read is done*/ |
| 106 | , 0 /*offset in bytes*/ |
| 107 | , sizeof(float)*p.size() /*size in bytes*/ |
| 108 | , p.data())); |
| 109 | |
| 110 | for (int i = 0; i < 1000; i++) { |
| 111 | if (p[i] != a[i]*b[i]) { |
| 112 | return 1; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | printf("OpenCL sez: %g x %g = %g\n", a[42], b[42], p[42]); |
| 117 | return 0; |
| 118 | } |