1 /* 2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "opencl_backend.h" 27 28 OpenCLBackend::OpenCLBuffer *OpenCLBackend::getOrCreateBuffer(BufferState *bufferState) { 29 OpenCLBuffer *openclBuffer = nullptr; 30 if (bufferState->vendorPtr == nullptr || bufferState->state == BufferState::NEW_STATE) { 31 openclBuffer = new OpenCLBuffer(this, bufferState); 32 if (config->trace) { 33 std::cout << "We allocated arg buffer " << std::endl; 34 } 35 } else { 36 if (config->trace) { 37 std::cout << "Were reusing buffer buffer " << std::endl; 38 } 39 openclBuffer = static_cast<OpenCLBuffer *>(bufferState->vendorPtr); 40 } 41 return openclBuffer; 42 } 43 44 bool OpenCLBackend::getBufferFromDeviceIfDirty(void *memorySegment, long memorySegmentLength) { 45 if (config->traceCalls) { 46 std::cout << "getBufferFromDeviceIfDirty(" << std::hex << (long) memorySegment << "," << std::dec << 47 memorySegmentLength << "){" << std::endl; 48 } 49 if (config->minimizeCopies) { 50 const BufferState *bufferState = BufferState::of(memorySegment, memorySegmentLength); 51 if (bufferState->state == BufferState::DEVICE_OWNED) { 52 queue->copyFromDevice(static_cast<Buffer *>(bufferState->vendorPtr)); 53 if (config->traceEnqueues | config->traceCopies) { 54 std::cout << "copying buffer from device (from java access) " << std::endl; 55 } 56 queue->wait(); 57 queue->release(); 58 } else { 59 std::cout << "HOW DID WE GET HERE 1 attempting to get buffer but buffer is not device dirty" << std::endl; 60 std::exit(1); 61 } 62 } else { 63 std::cerr << 64 "HOW DID WE GET HERE ? java side should avoid calling getBufferFromDeviceIfDirty as we are not minimising buffers!" 65 << std::endl; 66 std::exit(1); 67 } 68 if (config->traceCalls) { 69 std::cout << "}getBufferFromDeviceIfDirty()" << std::endl; 70 } 71 return true; 72 } 73 74 OpenCLBackend::OpenCLBackend(int configBits) 75 : Backend(new Config(configBits), new OpenCLQueue(this)) { 76 cl_int status; 77 cl_uint platformc = 0; 78 OPENCL_CHECK(clGetPlatformIDs(0, nullptr, &platformc), "clGetPlatformIDs"); 79 80 if (config->platform >= platformc) { 81 std::cerr << "We only have " << platformc << " platform" << ((platformc > 1) ? "s" : "") << 82 " (platform[0]-platform[" << (platformc - 1) << "] inclusive) you requested platform[" << config-> 83 platform << "]" << std::endl; 84 std::exit(1); 85 } 86 auto *platforms = new cl_platform_id[platformc]; 87 OPENCL_CHECK(clGetPlatformIDs(platformc, platforms, nullptr), "clGetPlatformIDs"); 88 89 cl_uint numDevices = 0; 90 platform_id = platforms[config->platform]; 91 if ((status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, 0, nullptr, &numDevices)) != CL_SUCCESS) { 92 if (status != CL_SUCCESS) { 93 std::cerr << "clGetDeviceIDs (to get count) failed " << errorMsg(status) << std::endl; 94 } 95 delete[] platforms; 96 return; 97 } 98 if (config->device >= numDevices) { 99 std::cerr << "Platform[" << config->platform << "] only has " << numDevices << " device" << ( 100 (numDevices > 1) ? "s" : "") << " (device[0]-device[" << (numDevices - 1) << 101 "] inclusive) and you requested device[" << config->device << "]" << std::endl; 102 std::cerr << "No device available " << errorMsg(CL_DEVICE_NOT_AVAILABLE) << std::endl; 103 delete[] platforms; 104 std::exit(1); 105 } 106 107 if (numDevices == 0) { 108 status = CL_DEVICE_NOT_AVAILABLE; 109 std::cerr << "No device available " << errorMsg(status) << std::endl; 110 delete[] platforms; 111 return; 112 } 113 auto *device_ids = new cl_device_id[numDevices]; // compute device id 114 if ((status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, numDevices, device_ids, nullptr)) != CL_SUCCESS) { 115 std::cerr << "clGetDeviceIDs failed " << errorMsg(status) << std::endl; 116 delete[] platforms; 117 delete[] device_ids; 118 return; 119 } 120 if ((context = clCreateContext(nullptr, 1, &device_ids[config->device], nullptr, nullptr, &status)) == nullptr || 121 status != CL_SUCCESS) { 122 std::cerr << "clCreateContext failed " << errorMsg(status) << std::endl; 123 delete[] platforms; 124 delete[] device_ids; 125 return; 126 } 127 128 cl_command_queue_properties queue_props = CL_QUEUE_PROFILING_ENABLE; 129 const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue); 130 if ((openCLQueue->command_queue = clCreateCommandQueue(context, device_ids[config->device], queue_props, &status)) 131 == nullptr || status != CL_SUCCESS) { 132 std::cerr << "clCreateCommandQueue failed " << errorMsg(status) << std::endl; 133 clReleaseContext(context); 134 delete[] platforms; 135 delete[] device_ids; 136 return; 137 } 138 139 device_id = device_ids[config->device]; 140 delete[] device_ids; 141 delete[] platforms; 142 } 143 144 OpenCLBackend::~OpenCLBackend() { 145 clReleaseContext(context); 146 } 147 148 void OpenCLBackend::computeStart() { 149 if (config->trace) { 150 std::cout << "compute start" << std::endl; 151 } 152 queue->computeStart(); 153 } 154 155 void OpenCLBackend::computeEnd() { 156 queue->computeEnd(); 157 queue->wait(); 158 159 if (config->profile) { 160 const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue); 161 openCLQueue->showEvents(100); 162 } 163 queue->release(); 164 if (config->trace) { 165 std::cout << "compute end" << std::endl; 166 } 167 } 168 169 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(OpenCLSource &openclSource) { 170 return compileProgram(&openclSource); 171 } 172 173 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(const OpenCLSource *openclSource) { 174 return compileProgram(openclSource->len, openclSource->text); 175 } 176 177 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(int len, char *text) { 178 return dynamic_cast<OpenCLProgram *>(compile(len, text)); 179 } 180 181 Backend::CompilationUnit *OpenCLBackend::compile(int len, char *source) { 182 const size_t srcLen = ::strlen(source); 183 auto src = new char[srcLen + 1]; 184 strncpy(src, source, srcLen); 185 src[srcLen] = '\0'; 186 if (config->trace) { 187 std::cout << "native compiling " << src << std::endl; 188 } 189 cl_int status; 190 cl_program program; 191 if ((program = clCreateProgramWithSource(context, 1, (const char **) &src, nullptr, &status)) == nullptr || 192 status != CL_SUCCESS) { 193 std::cerr << "clCreateProgramWithSource failed" << std::endl; 194 delete[] src; 195 return nullptr; 196 } 197 198 cl_int buildStatus = clBuildProgram(program, 0, nullptr, nullptr, nullptr, nullptr); 199 if (buildStatus != CL_SUCCESS) { 200 std::cerr << "buildStatus =failed" << std::endl; 201 } 202 size_t logLen = 0; 203 OpenCLProgram *openclProgram = nullptr; 204 if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, nullptr, &logLen)) != CL_SUCCESS) { 205 std::cerr << "clGetBuildInfo (getting log size) failed" << std::endl; 206 //openclProgram->buildInfo = new Backend::CompilationUnit::BuildInfo(openclProgram, src, nullptr, false); 207 openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program); 208 } else { 209 // cl_build_status buildStatus; 210 clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_STATUS, sizeof(buildStatus), &buildStatus, nullptr); 211 if (logLen > 0) { 212 char *log = new char[logLen + 1]; 213 if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, logLen + 1, (void *) log, 214 nullptr)) != CL_SUCCESS) { 215 std::cerr << "clGetBuildInfo (getting log) failed" << std::endl; 216 delete[] log; 217 log = nullptr; 218 } else { 219 log[logLen] = '\0'; 220 if (logLen > 2) { 221 std::cerr << "logLen = " << logLen << " log = " << log << std::endl; 222 } 223 } 224 openclProgram = new OpenCLProgram(this, src, log, buildStatus == CL_SUCCESS, program); 225 } else { 226 openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program); 227 } 228 } 229 return openclProgram; 230 } 231 232 const char *OpenCLBackend::errorMsg(cl_int status) { 233 static struct { 234 cl_int code; 235 const char *msg; 236 } error_table[] = { 237 // @formatter:off 238 {CL_SUCCESS, "success"}, 239 {CL_DEVICE_NOT_FOUND, "device not found",}, 240 {CL_DEVICE_NOT_AVAILABLE, "device not available",}, 241 {CL_COMPILER_NOT_AVAILABLE, "compiler not available",}, 242 {CL_MEM_OBJECT_ALLOCATION_FAILURE, "mem object allocation failure",}, 243 {CL_OUT_OF_RESOURCES, "out of resources",}, 244 {CL_OUT_OF_HOST_MEMORY, "out of host memory",}, 245 {CL_PROFILING_INFO_NOT_AVAILABLE, "profiling not available",}, 246 {CL_MEM_COPY_OVERLAP, "memcopy overlaps",}, 247 {CL_IMAGE_FORMAT_MISMATCH, "image format mismatch",}, 248 {CL_IMAGE_FORMAT_NOT_SUPPORTED, "image format not supported",}, 249 {CL_BUILD_PROGRAM_FAILURE, "build program failed",}, 250 {CL_MAP_FAILURE, "map failed",}, 251 {CL_INVALID_VALUE, "invalid value",}, 252 {CL_INVALID_DEVICE_TYPE, "invalid device type",}, 253 {CL_INVALID_PLATFORM, "invlaid platform",}, 254 {CL_INVALID_DEVICE, "invalid device",}, 255 {CL_INVALID_CONTEXT, "invalid context",}, 256 {CL_INVALID_QUEUE_PROPERTIES, "invalid queue properties",}, 257 {CL_INVALID_COMMAND_QUEUE, "invalid command queue",}, 258 {CL_INVALID_HOST_PTR, "invalid host ptr",}, 259 {CL_INVALID_MEM_OBJECT, "invalid mem object",}, 260 {CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, "invalid image format descriptor ",}, 261 {CL_INVALID_IMAGE_SIZE, "invalid image size",}, 262 {CL_INVALID_SAMPLER, "invalid sampler",}, 263 {CL_INVALID_BINARY, "invalid binary",}, 264 {CL_INVALID_BUILD_OPTIONS, "invalid build options",}, 265 {CL_INVALID_PROGRAM, "invalid program ",}, 266 {CL_INVALID_PROGRAM_EXECUTABLE, "invalid program executable",}, 267 {CL_INVALID_KERNEL_NAME, "invalid kernel name",}, 268 {CL_INVALID_KERNEL_DEFINITION, "invalid definition",}, 269 {CL_INVALID_KERNEL, "invalid kernel",}, 270 {CL_INVALID_ARG_INDEX, "invalid arg index",}, 271 {CL_INVALID_ARG_VALUE, "invalid arg value",}, 272 {CL_INVALID_ARG_SIZE, "invalid arg size",}, 273 {CL_INVALID_KERNEL_ARGS, "invalid kernel args",}, 274 {CL_INVALID_WORK_DIMENSION, "invalid work dimension",}, 275 {CL_INVALID_WORK_GROUP_SIZE, "invalid work group size",}, 276 {CL_INVALID_WORK_ITEM_SIZE, "invalid work item size",}, 277 {CL_INVALID_GLOBAL_OFFSET, "invalid global offset",}, 278 {CL_INVALID_EVENT_WAIT_LIST, "invalid event wait list",}, 279 {CL_INVALID_EVENT, "invalid event",}, 280 {CL_INVALID_OPERATION, "invalid operation",}, 281 {CL_INVALID_GL_OBJECT, "invalid gl object",}, 282 {CL_INVALID_BUFFER_SIZE, "invalid buffer size",}, 283 {CL_INVALID_MIP_LEVEL, "invalid mip level",}, 284 {CL_INVALID_GLOBAL_WORK_SIZE, "invalid global work size",}, 285 {-9999, "enqueueNdRangeKernel Illegal read or write to a buffer",}, 286 {0, nullptr}, 287 // @formatter:on 288 }; 289 for (int i = 0; error_table[i].msg != nullptr; i++) { 290 if (error_table[i].code == status) { 291 //std::cerr << " clerror '" << error_table[i].msg << "'" << std::endl; 292 return error_table[i].msg; 293 } 294 } 295 static char unknown[256]; 296 #if defined (_WIN32) 297 _snprintf 298 #else 299 snprintf 300 #endif 301 (unknown, sizeof(unknown), "unmapped string for error %d", status); 302 return unknown; 303 } 304 305 extern "C" long getBackend(int configBits) { 306 std::cerr << "Opencl Driver =" << std::hex << configBits << std::dec << std::endl; 307 return reinterpret_cast<long>(new OpenCLBackend(configBits)); 308 } 309 310 void __checkOpenclErrors(cl_int status, const char *functionName, const char *file, const int line) { 311 if (CL_SUCCESS != status) { 312 std::cerr << "Opencl Error ( " << functionName << ") with error code: " << status << " from file " << file << 313 " line " << line << std::endl; 314 exit(-1); 315 } 316 } 317 318 OpenCLSource::OpenCLSource() 319 : Text(0L) { 320 } 321 322 OpenCLSource::OpenCLSource(const size_t len) 323 : Text(len) { 324 } 325 326 OpenCLSource::OpenCLSource(char *text) 327 : Text(text, false) { 328 }