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;
30 if (bufferState->vendorPtr == nullptr || bufferState->state == BufferState::NEW_STATE) {
31 openclBuffer = new OpenCLBuffer(this, bufferState);
32 if (config->trace) {
33 std::cout << "[native] Device Buffer with size " << bufferState->length << " allocated" << std::endl;
34 }
35 bufferState->state = BufferState::NEW_STATE;
36 } else {
37 if (config->trace) {
38 std::cout << "[native] Reusing a device-buffer " << std::hex << bufferState->vendorPtr << std::dec << std::endl;
39 }
40 openclBuffer = static_cast<OpenCLBuffer *>(bufferState->vendorPtr);
41 }
42 return openclBuffer;
43 }
44
45 bool OpenCLBackend::getBufferFromDeviceIfDirty(void *memorySegment, long memorySegmentLength) {
46 if (config->traceCalls) {
47 std::cout << "getBufferFromDeviceIfDirty(" << std::hex << (long) memorySegment << "," << std::dec <<
48 memorySegmentLength << "){" << std::endl;
49 }
50 if (config->minimizeCopies) {
51 const BufferState *bufferState = BufferState::of(memorySegment, memorySegmentLength);
52 if (bufferState->state == BufferState::DEVICE_OWNED) {
53 queue->copyFromDevice(static_cast<Buffer *>(bufferState->vendorPtr));
54 if (config->traceEnqueues | config->traceCopies) {
55 std::cout << "copying buffer from device (from java access) " << std::endl;
56 }
57 queue->wait();
58 queue->release();
59 bufferState->state = BufferState::HOST_OWNED;
60 } else {
61 std::cout << "HOW DID WE GET HERE 1 attempting to get buffer but buffer is not device dirty" << std::endl;
62 std::exit(1);
63 }
64 } else {
65 std::cerr <<
66 "HOW DID WE GET HERE ? java side should avoid calling getBufferFromDeviceIfDirty as we are not minimising buffers!"
67 << std::endl;
68 std::exit(1);
69 }
70 if (config->traceCalls) {
71 std::cout << "}getBufferFromDeviceIfDirty()" << std::endl;
72 }
73 return true;
74 }
75
76 OpenCLBackend::OpenCLBackend(int configBits)
77 : Backend(new Config(configBits), new OpenCLQueue(this)) {
78
79 if (config->info) {
80 std::cerr << "[INFO] Config Bits = " << std::hex << configBits << std::dec << std::endl;
81 }
82
83 cl_int status;
84 cl_uint platformc = 0;
85 OPENCL_CHECK(clGetPlatformIDs(0, nullptr, &platformc), "clGetPlatformIDs");
86
87 if (config->platform >= platformc) {
88 std::cerr << "We only have " << platformc << " platform" << ((platformc > 1) ? "s" : "") <<
89 " (platform[0]-platform[" << (platformc - 1) << "] inclusive) you requested platform[" << config->
90 platform << "]" << std::endl;
91 std::exit(1);
92 }
93 auto *platforms = new cl_platform_id[platformc];
94 OPENCL_CHECK(clGetPlatformIDs(platformc, platforms, nullptr), "clGetPlatformIDs");
95
96 cl_uint numDevices = 0;
97 platform_id = platforms[config->platform];
98 status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, 0, nullptr, &numDevices);
99 if (status != CL_SUCCESS) {
100 std::cerr << "clGetDeviceIDs (to get count) failed " << errorMsg(status) << std::endl;
101 delete[] platforms;
102 return;
103 }
104 if (config->device >= numDevices) {
105 std::cerr << "Platform[" << config->platform << "] only has " << numDevices << " device" << (
106 (numDevices > 1) ? "s" : "") << " (device[0]-device[" << (numDevices - 1) <<
107 "] inclusive) and you requested device[" << config->device << "]" << std::endl;
108 std::cerr << "No device available " << errorMsg(CL_DEVICE_NOT_AVAILABLE) << std::endl;
109 delete[] platforms;
110 std::exit(1);
111 }
112
113 if (numDevices == 0) {
114 status = CL_DEVICE_NOT_AVAILABLE;
115 std::cerr << "No device available " << errorMsg(status) << std::endl;
116 delete[] platforms;
117 return;
118 }
119 auto *device_ids = new cl_device_id[numDevices]; // compute device id
120 if ((status = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_ALL, numDevices, device_ids, nullptr)) != CL_SUCCESS) {
121 std::cerr << "clGetDeviceIDs failed " << errorMsg(status) << std::endl;
122 delete[] platforms;
123 delete[] device_ids;
124 return;
125 }
126 if ((context = clCreateContext(nullptr, 1, &device_ids[config->device], nullptr, nullptr, &status)) == nullptr ||
127 status != CL_SUCCESS) {
128 std::cerr << "clCreateContext failed " << errorMsg(status) << std::endl;
129 delete[] platforms;
130 delete[] device_ids;
131 return;
132 }
133
134 cl_command_queue_properties queue_props = 0;
135 char vendor[256] = {0};
136 status = clGetDeviceInfo(device_ids[config->device], CL_DEVICE_VENDOR, sizeof(vendor), &vendor, nullptr);
137 if (status != CL_SUCCESS) {
138 std::cerr << "clGetDeviceInfo failed " << errorMsg(status) << std::endl;
139 delete[] platforms;
140 delete[] device_ids;
141 return;
142 }
143
144 // We set the profile on if platform is Apple
145 if (std::strstr(vendor, "Apple") != nullptr) {
146 isPlatformProfileOn = true;
147 } else {
148 isPlatformProfileOn = false;
149
150 }
151 if (config->profile || isPlatformProfileOn) {
152 queue_props = CL_QUEUE_PROFILING_ENABLE;
153 }
154 if (config->outOfOrder) {
155 // Check if the device supports Out Or Order first
156 cl_command_queue_properties queue_properties = 0;
157 status = clGetDeviceInfo(device_ids[config->device],
158 CL_DEVICE_QUEUE_PROPERTIES,
159 sizeof(queue_properties), &queue_properties,
160 nullptr);
161 if (queue_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) {
162 queue_props |= CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE;
163 }
164 }
165 const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue);
166 if ((openCLQueue->command_queue = clCreateCommandQueue(context, device_ids[config->device], queue_props, &status))
167 == nullptr || status != CL_SUCCESS) {
168 std::cerr << "clCreateCommandQueue failed " << errorMsg(status) << std::endl;
169 clReleaseContext(context);
170 delete[] platforms;
171 delete[] device_ids;
172 return;
173 }
174 device_id = device_ids[config->device];
175 _platform_info = std::make_unique<PlatformInfo>(this);
176 delete[] device_ids;
177 delete[] platforms;
178 }
179
180 OpenCLBackend::~OpenCLBackend() {
181 clReleaseContext(context);
182 }
183
184 void OpenCLBackend::computeStart() {
185 if (config->trace) {
186 std::cout << "compute start" << std::endl;
187 }
188 queue->computeStart();
189 }
190
191 void OpenCLBackend::computeEnd() {
192 queue->computeEnd();
193 queue->wait();
194
195 if (config->profile) {
196 const auto openCLQueue = dynamic_cast<OpenCLQueue *>(queue);
197 openCLQueue->showEvents(100);
198 }
199 queue->release();
200 if (config->trace) {
201 std::cout << "compute end" << std::endl;
202 }
203 }
204
205 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(OpenCLSource &openclSource) {
206 return compileProgram(&openclSource);
207 }
208
209 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(const OpenCLSource *openclSource) {
210 return compileProgram(openclSource->len, openclSource->text);
211 }
212
213 OpenCLBackend::OpenCLProgram *OpenCLBackend::compileProgram(int len, char *text) {
214 return dynamic_cast<OpenCLProgram *>(compile(len, text, 0));
215 }
216
217 Backend::CompilationUnit *OpenCLBackend::compile(int len, char *source, int typeModel) {
218 const size_t srcLen = ::strlen(source);
219 auto src = new char[srcLen + 1];
220 strncpy(src, source, srcLen);
221 src[srcLen] = '\0';
222 if (config->trace) {
223 std::cout << "native compiling " << src << std::endl;
224 }
225 cl_int status;
226 cl_program program;
227 if ((program = clCreateProgramWithSource(context, 1, (const char **) &src, nullptr, &status)) == nullptr ||
228 status != CL_SUCCESS) {
229 std::cerr << "clCreateProgramWithSource failed" << std::endl;
230 delete[] src;
231 return nullptr;
232 }
233
234 cl_int buildStatus = clBuildProgram(program, 0, nullptr, nullptr, nullptr, nullptr);
235 if (buildStatus != CL_SUCCESS) {
236 std::cerr << "buildStatus =failed" << std::endl;
237 }
238 size_t logLen = 0;
239 OpenCLProgram *openclProgram = nullptr;
240 if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, 0, nullptr, &logLen)) != CL_SUCCESS) {
241 std::cerr << "clGetBuildInfo (getting log size) failed" << std::endl;
242 openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program);
243 } else {
244 clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_STATUS, sizeof(buildStatus), &buildStatus, nullptr);
245 if (config -> info && logLen > 0) {
246 char *log = new char[logLen + 1];
247 if ((status = clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, logLen + 1, log, nullptr)) != CL_SUCCESS) {
248 std::cerr << "clGetBuildInfo (getting log) failed" << std::endl;
249 delete[] log;
250 log = nullptr;
251 } else {
252 log[logLen] = '\0';
253 if (logLen > 2) {
254 std::cerr << "logLen = " << logLen << " log = " << log << std::endl;
255 }
256 }
257 openclProgram = new OpenCLProgram(this, src, log, buildStatus == CL_SUCCESS, program);
258 } else {
259 openclProgram = new OpenCLProgram(this, src, nullptr, buildStatus == CL_SUCCESS, program);
260 }
261 }
262 return openclProgram;
263 }
264
265 const char *OpenCLBackend::errorMsg(cl_int status) {
266 static struct {
267 cl_int code;
268 const char *msg;
269 } error_table[] = {
270 // @formatter:off
271 {CL_SUCCESS, "success"},
272 {CL_DEVICE_NOT_FOUND, "device not found",},
273 {CL_DEVICE_NOT_AVAILABLE, "device not available",},
274 {CL_COMPILER_NOT_AVAILABLE, "compiler not available",},
275 {CL_MEM_OBJECT_ALLOCATION_FAILURE, "mem object allocation failure",},
276 {CL_OUT_OF_RESOURCES, "out of resources",},
277 {CL_OUT_OF_HOST_MEMORY, "out of host memory",},
278 {CL_PROFILING_INFO_NOT_AVAILABLE, "profiling not available",},
279 {CL_MEM_COPY_OVERLAP, "memcopy overlaps",},
280 {CL_IMAGE_FORMAT_MISMATCH, "image format mismatch",},
281 {CL_IMAGE_FORMAT_NOT_SUPPORTED, "image format not supported",},
282 {CL_BUILD_PROGRAM_FAILURE, "build program failed",},
283 {CL_MAP_FAILURE, "map failed",},
284 {CL_INVALID_VALUE, "invalid value",},
285 {CL_INVALID_DEVICE_TYPE, "invalid device type",},
286 {CL_INVALID_PLATFORM, "invlaid platform",},
287 {CL_INVALID_DEVICE, "invalid device",},
288 {CL_INVALID_CONTEXT, "invalid context",},
289 {CL_INVALID_QUEUE_PROPERTIES, "invalid queue properties",},
290 {CL_INVALID_COMMAND_QUEUE, "invalid command queue",},
291 {CL_INVALID_HOST_PTR, "invalid host ptr",},
292 {CL_INVALID_MEM_OBJECT, "invalid mem object",},
293 {CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, "invalid image format descriptor ",},
294 {CL_INVALID_IMAGE_SIZE, "invalid image size",},
295 {CL_INVALID_SAMPLER, "invalid sampler",},
296 {CL_INVALID_BINARY, "invalid binary",},
297 {CL_INVALID_BUILD_OPTIONS, "invalid build options",},
298 {CL_INVALID_PROGRAM, "invalid program ",},
299 {CL_INVALID_PROGRAM_EXECUTABLE, "invalid program executable",},
300 {CL_INVALID_KERNEL_NAME, "invalid kernel name",},
301 {CL_INVALID_KERNEL_DEFINITION, "invalid definition",},
302 {CL_INVALID_KERNEL, "invalid kernel",},
303 {CL_INVALID_ARG_INDEX, "invalid arg index",},
304 {CL_INVALID_ARG_VALUE, "invalid arg value",},
305 {CL_INVALID_ARG_SIZE, "invalid arg size",},
306 {CL_INVALID_KERNEL_ARGS, "invalid kernel args",},
307 {CL_INVALID_WORK_DIMENSION, "invalid work dimension",},
308 {CL_INVALID_WORK_GROUP_SIZE, "invalid work group size",},
309 {CL_INVALID_WORK_ITEM_SIZE, "invalid work item size",},
310 {CL_INVALID_GLOBAL_OFFSET, "invalid global offset",},
311 {CL_INVALID_EVENT_WAIT_LIST, "invalid event wait list",},
312 {CL_INVALID_EVENT, "invalid event",},
313 {CL_INVALID_OPERATION, "invalid operation",},
314 {CL_INVALID_GL_OBJECT, "invalid gl object",},
315 {CL_INVALID_BUFFER_SIZE, "invalid buffer size",},
316 {CL_INVALID_MIP_LEVEL, "invalid mip level",},
317 {CL_INVALID_GLOBAL_WORK_SIZE, "invalid global work size",},
318 {-9999, "enqueueNdRangeKernel Illegal read or write to a buffer",},
319 {0, nullptr},
320 // @formatter:on
321 };
322 for (int i = 0; error_table[i].msg != nullptr; i++) {
323 if (error_table[i].code == status) {
324 //std::cerr << " clerror '" << error_table[i].msg << "'" << std::endl;
325 return error_table[i].msg;
326 }
327 }
328 static char unknown[256];
329 #if defined (_WIN32)
330 _snprintf
331 #else
332 snprintf
333 #endif
334 (unknown, sizeof(unknown), "unmapped string for error %d", status);
335 return unknown;
336 }
337
338 extern "C" long getBackend(int configBits) {
339 return reinterpret_cast<long>(new OpenCLBackend(configBits));
340 }
341
342 void __checkOpenclErrors(cl_int status, const char *functionName, const char *file, const int line) {
343 if (CL_SUCCESS != status) {
344 std::cerr << "Opencl Error ( " << functionName << ") with error code: " << status << " from file " << file <<
345 " line " << line << std::endl;
346 exit(-1);
347 }
348 }
349
350 OpenCLSource::OpenCLSource()
351 : Text(0L) {
352 }
353
354 OpenCLSource::OpenCLSource(const size_t len)
355 : Text(len) {
356 }
357
358 OpenCLSource::OpenCLSource(char *text)
359 : Text(text, false) {
360 }