1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // CLPlatformCL.cpp: Implements the class methods for CLPlatformCL.
7
8 #include "libANGLE/renderer/cl/CLPlatformCL.h"
9
10 #include "common/angle_version_info.h"
11 #include "libANGLE/CLContext.h"
12 #include "libANGLE/CLDevice.h"
13 #include "libANGLE/CLPlatform.h"
14 #include "libANGLE/renderer/cl/CLContextCL.h"
15 #include "libANGLE/renderer/cl/CLDeviceCL.h"
16 #include "libANGLE/renderer/cl/cl_util.h"
17
18 extern "C" {
19 #include "icd.h"
20 } // extern "C"
21
22 namespace rx
23 {
24
25 namespace
26 {
27
GetPlatformString(cl_platform_id platform,cl::PlatformInfo name)28 std::string GetPlatformString(cl_platform_id platform, cl::PlatformInfo name)
29 {
30 size_t size = 0u;
31 if (platform->getDispatch().clGetPlatformInfo(platform, cl::ToCLenum(name), 0u, nullptr,
32 &size) == CL_SUCCESS)
33 {
34 std::vector<char> str(size, '\0');
35 if (platform->getDispatch().clGetPlatformInfo(platform, cl::ToCLenum(name), size,
36 str.data(), nullptr) == CL_SUCCESS)
37 {
38 return std::string(str.data());
39 }
40 }
41 ERR() << "Failed to query CL platform info for " << name;
42 return std::string{};
43 }
44
45 } // namespace
46
47 CLPlatformCL::~CLPlatformCL() = default;
48
createInfo() const49 CLPlatformImpl::Info CLPlatformCL::createInfo() const
50 {
51 // Verify that the platform is valid
52 if (mNative == nullptr || mNative->getDispatch().clGetPlatformIDs == nullptr ||
53 mNative->getDispatch().clGetPlatformInfo == nullptr ||
54 mNative->getDispatch().clGetDeviceIDs == nullptr ||
55 mNative->getDispatch().clGetDeviceInfo == nullptr ||
56 mNative->getDispatch().clCreateContext == nullptr ||
57 mNative->getDispatch().clCreateContextFromType == nullptr ||
58 mNative->getDispatch().clRetainContext == nullptr ||
59 mNative->getDispatch().clReleaseContext == nullptr ||
60 mNative->getDispatch().clGetContextInfo == nullptr ||
61 mNative->getDispatch().clCreateCommandQueue == nullptr ||
62 mNative->getDispatch().clRetainCommandQueue == nullptr ||
63 mNative->getDispatch().clReleaseCommandQueue == nullptr ||
64 mNative->getDispatch().clGetCommandQueueInfo == nullptr ||
65 mNative->getDispatch().clSetCommandQueueProperty == nullptr ||
66 mNative->getDispatch().clCreateBuffer == nullptr ||
67 mNative->getDispatch().clCreateImage2D == nullptr ||
68 mNative->getDispatch().clCreateImage3D == nullptr ||
69 mNative->getDispatch().clRetainMemObject == nullptr ||
70 mNative->getDispatch().clReleaseMemObject == nullptr ||
71 mNative->getDispatch().clGetSupportedImageFormats == nullptr ||
72 mNative->getDispatch().clGetMemObjectInfo == nullptr ||
73 mNative->getDispatch().clGetImageInfo == nullptr ||
74 mNative->getDispatch().clCreateSampler == nullptr ||
75 mNative->getDispatch().clRetainSampler == nullptr ||
76 mNative->getDispatch().clReleaseSampler == nullptr ||
77 mNative->getDispatch().clGetSamplerInfo == nullptr ||
78 mNative->getDispatch().clCreateProgramWithSource == nullptr ||
79 mNative->getDispatch().clCreateProgramWithBinary == nullptr ||
80 mNative->getDispatch().clRetainProgram == nullptr ||
81 mNative->getDispatch().clReleaseProgram == nullptr ||
82 mNative->getDispatch().clBuildProgram == nullptr ||
83 mNative->getDispatch().clUnloadCompiler == nullptr ||
84 mNative->getDispatch().clGetProgramInfo == nullptr ||
85 mNative->getDispatch().clGetProgramBuildInfo == nullptr ||
86 mNative->getDispatch().clCreateKernel == nullptr ||
87 mNative->getDispatch().clCreateKernelsInProgram == nullptr ||
88 mNative->getDispatch().clRetainKernel == nullptr ||
89 mNative->getDispatch().clReleaseKernel == nullptr ||
90 mNative->getDispatch().clSetKernelArg == nullptr ||
91 mNative->getDispatch().clGetKernelInfo == nullptr ||
92 mNative->getDispatch().clGetKernelWorkGroupInfo == nullptr ||
93 mNative->getDispatch().clWaitForEvents == nullptr ||
94 mNative->getDispatch().clGetEventInfo == nullptr ||
95 mNative->getDispatch().clRetainEvent == nullptr ||
96 mNative->getDispatch().clReleaseEvent == nullptr ||
97 mNative->getDispatch().clGetEventProfilingInfo == nullptr ||
98 mNative->getDispatch().clFlush == nullptr || mNative->getDispatch().clFinish == nullptr ||
99 mNative->getDispatch().clEnqueueReadBuffer == nullptr ||
100 mNative->getDispatch().clEnqueueWriteBuffer == nullptr ||
101 mNative->getDispatch().clEnqueueCopyBuffer == nullptr ||
102 mNative->getDispatch().clEnqueueReadImage == nullptr ||
103 mNative->getDispatch().clEnqueueWriteImage == nullptr ||
104 mNative->getDispatch().clEnqueueCopyImage == nullptr ||
105 mNative->getDispatch().clEnqueueCopyImageToBuffer == nullptr ||
106 mNative->getDispatch().clEnqueueCopyBufferToImage == nullptr ||
107 mNative->getDispatch().clEnqueueMapBuffer == nullptr ||
108 mNative->getDispatch().clEnqueueMapImage == nullptr ||
109 mNative->getDispatch().clEnqueueUnmapMemObject == nullptr ||
110 mNative->getDispatch().clEnqueueNDRangeKernel == nullptr ||
111 mNative->getDispatch().clEnqueueTask == nullptr ||
112 mNative->getDispatch().clEnqueueNativeKernel == nullptr ||
113 mNative->getDispatch().clEnqueueMarker == nullptr ||
114 mNative->getDispatch().clEnqueueWaitForEvents == nullptr ||
115 mNative->getDispatch().clEnqueueBarrier == nullptr ||
116 mNative->getDispatch().clGetExtensionFunctionAddress == nullptr)
117 {
118 ERR() << "Missing entry points for OpenCL 1.0";
119 return Info{};
120 }
121
122 // Fetch common platform info
123 Info info;
124 const std::string vendor = GetPlatformString(mNative, cl::PlatformInfo::Vendor);
125 info.profile = GetPlatformString(mNative, cl::PlatformInfo::Profile);
126 info.versionStr = GetPlatformString(mNative, cl::PlatformInfo::Version);
127 info.name = GetPlatformString(mNative, cl::PlatformInfo::Name);
128 std::string extensionStr = GetPlatformString(mNative, cl::PlatformInfo::Extensions);
129
130 if (vendor.empty() || info.profile.empty() || info.versionStr.empty() || info.name.empty() ||
131 extensionStr.empty())
132 {
133 return Info{};
134 }
135
136 // Skip ANGLE CL implementation to prevent passthrough loop
137 if (vendor.compare(cl::Platform::GetVendor()) == 0)
138 {
139 ERR() << "Tried to create CL pass-through back end for ANGLE library";
140 return Info{};
141 }
142
143 // TODO(jplate) Remove workaround after bug is fixed http://anglebug.com/6053
144 if (info.versionStr.compare(0u, 15u, "OpenCL 3.0 CUDA", 15u) == 0)
145 {
146 extensionStr.append(" cl_khr_depth_images cl_khr_image2d_from_buffer");
147 }
148
149 // Limit version number to supported version
150 if (info.versionStr[7] != '1')
151 {
152 info.versionStr[7] = '1';
153 info.versionStr[9] = '2';
154 }
155
156 const cl_version version = ExtractCLVersion(info.versionStr);
157 if (version == 0u)
158 {
159 return Info{};
160 }
161
162 // Remove unsupported and initialize extensions
163 RemoveUnsupportedCLExtensions(extensionStr);
164 info.initializeExtensions(std::move(extensionStr));
165
166 // Skip platform if it is not ICD compatible
167 if (!info.khrICD)
168 {
169 WARN() << "CL platform is not ICD compatible";
170 return Info{};
171 }
172
173 // Customize version string and name
174 info.versionStr += std::string(" (ANGLE ") + angle::GetANGLEVersionString() + ")";
175 info.name.insert(0u, "ANGLE pass-through -> ");
176
177 if (version >= CL_MAKE_VERSION(2, 1, 0) &&
178 mNative->getDispatch().clGetPlatformInfo(mNative, CL_PLATFORM_HOST_TIMER_RESOLUTION,
179 sizeof(info.hostTimerRes), &info.hostTimerRes,
180 nullptr) != CL_SUCCESS)
181 {
182 ERR() << "Failed to query CL platform info for CL_PLATFORM_HOST_TIMER_RESOLUTION";
183 return Info{};
184 }
185
186 if (version < CL_MAKE_VERSION(3, 0, 0))
187 {
188 info.version = version;
189 }
190 else
191 {
192 if (mNative->getDispatch().clGetPlatformInfo(mNative, CL_PLATFORM_NUMERIC_VERSION,
193 sizeof(info.version), &info.version,
194 nullptr) != CL_SUCCESS)
195 {
196 ERR() << "Failed to query CL platform info for CL_PLATFORM_NUMERIC_VERSION";
197 return Info{};
198 }
199 else if (CL_VERSION_MAJOR(info.version) != CL_VERSION_MAJOR(version) ||
200 CL_VERSION_MINOR(info.version) != CL_VERSION_MINOR(version))
201 {
202 WARN() << "CL_PLATFORM_NUMERIC_VERSION = " << CL_VERSION_MAJOR(info.version) << '.'
203 << CL_VERSION_MINOR(info.version)
204 << " does not match version string: " << info.versionStr;
205 }
206
207 size_t valueSize = 0u;
208 if (mNative->getDispatch().clGetPlatformInfo(mNative, CL_PLATFORM_EXTENSIONS_WITH_VERSION,
209 0u, nullptr, &valueSize) != CL_SUCCESS ||
210 (valueSize % sizeof(decltype(info.extensionsWithVersion)::value_type)) != 0u)
211 {
212 ERR() << "Failed to query CL platform info for CL_PLATFORM_EXTENSIONS_WITH_VERSION";
213 return Info{};
214 }
215 info.extensionsWithVersion.resize(valueSize /
216 sizeof(decltype(info.extensionsWithVersion)::value_type));
217 if (mNative->getDispatch().clGetPlatformInfo(mNative, CL_PLATFORM_EXTENSIONS_WITH_VERSION,
218 valueSize, info.extensionsWithVersion.data(),
219 nullptr) != CL_SUCCESS)
220 {
221 ERR() << "Failed to query CL platform info for CL_PLATFORM_EXTENSIONS_WITH_VERSION";
222 return Info{};
223 }
224 RemoveUnsupportedCLExtensions(info.extensionsWithVersion);
225 }
226
227 if (info.version >= CL_MAKE_VERSION(1, 1, 0) &&
228 (mNative->getDispatch().clSetEventCallback == nullptr ||
229 mNative->getDispatch().clCreateSubBuffer == nullptr ||
230 mNative->getDispatch().clSetMemObjectDestructorCallback == nullptr ||
231 mNative->getDispatch().clCreateUserEvent == nullptr ||
232 mNative->getDispatch().clSetUserEventStatus == nullptr ||
233 mNative->getDispatch().clEnqueueReadBufferRect == nullptr ||
234 mNative->getDispatch().clEnqueueWriteBufferRect == nullptr ||
235 mNative->getDispatch().clEnqueueCopyBufferRect == nullptr))
236 {
237 ERR() << "Missing entry points for OpenCL 1.1";
238 return Info{};
239 }
240
241 if (info.version >= CL_MAKE_VERSION(1, 2, 0) &&
242 (mNative->getDispatch().clCreateSubDevices == nullptr ||
243 mNative->getDispatch().clRetainDevice == nullptr ||
244 mNative->getDispatch().clReleaseDevice == nullptr ||
245 mNative->getDispatch().clCreateImage == nullptr ||
246 mNative->getDispatch().clCreateProgramWithBuiltInKernels == nullptr ||
247 mNative->getDispatch().clCompileProgram == nullptr ||
248 mNative->getDispatch().clLinkProgram == nullptr ||
249 mNative->getDispatch().clUnloadPlatformCompiler == nullptr ||
250 mNative->getDispatch().clGetKernelArgInfo == nullptr ||
251 mNative->getDispatch().clEnqueueFillBuffer == nullptr ||
252 mNative->getDispatch().clEnqueueFillImage == nullptr ||
253 mNative->getDispatch().clEnqueueMigrateMemObjects == nullptr ||
254 mNative->getDispatch().clEnqueueMarkerWithWaitList == nullptr ||
255 mNative->getDispatch().clEnqueueBarrierWithWaitList == nullptr ||
256 mNative->getDispatch().clGetExtensionFunctionAddressForPlatform == nullptr))
257 {
258 ERR() << "Missing entry points for OpenCL 1.2";
259 return Info{};
260 }
261
262 if (info.version >= CL_MAKE_VERSION(2, 0, 0) &&
263 (mNative->getDispatch().clCreateCommandQueueWithProperties == nullptr ||
264 mNative->getDispatch().clCreatePipe == nullptr ||
265 mNative->getDispatch().clGetPipeInfo == nullptr ||
266 mNative->getDispatch().clSVMAlloc == nullptr ||
267 mNative->getDispatch().clSVMFree == nullptr ||
268 mNative->getDispatch().clEnqueueSVMFree == nullptr ||
269 mNative->getDispatch().clEnqueueSVMMemcpy == nullptr ||
270 mNative->getDispatch().clEnqueueSVMMemFill == nullptr ||
271 mNative->getDispatch().clEnqueueSVMMap == nullptr ||
272 mNative->getDispatch().clEnqueueSVMUnmap == nullptr ||
273 mNative->getDispatch().clCreateSamplerWithProperties == nullptr ||
274 mNative->getDispatch().clSetKernelArgSVMPointer == nullptr ||
275 mNative->getDispatch().clSetKernelExecInfo == nullptr))
276 {
277 ERR() << "Missing entry points for OpenCL 2.0";
278 return Info{};
279 }
280
281 if (info.version >= CL_MAKE_VERSION(2, 1, 0) &&
282 (mNative->getDispatch().clCloneKernel == nullptr ||
283 mNative->getDispatch().clCreateProgramWithIL == nullptr ||
284 mNative->getDispatch().clEnqueueSVMMigrateMem == nullptr ||
285 mNative->getDispatch().clGetDeviceAndHostTimer == nullptr ||
286 mNative->getDispatch().clGetHostTimer == nullptr ||
287 mNative->getDispatch().clGetKernelSubGroupInfo == nullptr ||
288 mNative->getDispatch().clSetDefaultDeviceCommandQueue == nullptr))
289 {
290 ERR() << "Missing entry points for OpenCL 2.1";
291 return Info{};
292 }
293
294 if (info.version >= CL_MAKE_VERSION(2, 2, 0) &&
295 (mNative->getDispatch().clSetProgramReleaseCallback == nullptr ||
296 mNative->getDispatch().clSetProgramSpecializationConstant == nullptr))
297 {
298 ERR() << "Missing entry points for OpenCL 2.2";
299 return Info{};
300 }
301
302 if (info.version >= CL_MAKE_VERSION(3, 0, 0) &&
303 (mNative->getDispatch().clCreateBufferWithProperties == nullptr ||
304 mNative->getDispatch().clCreateImageWithProperties == nullptr ||
305 mNative->getDispatch().clSetContextDestructorCallback == nullptr))
306 {
307 ERR() << "Missing entry points for OpenCL 3.0";
308 return Info{};
309 }
310
311 return info;
312 }
313
createDevices() const314 CLDeviceImpl::CreateDatas CLPlatformCL::createDevices() const
315 {
316 CLDeviceImpl::CreateDatas createDatas;
317
318 // Fetch all regular devices. This does not include CL_DEVICE_TYPE_CUSTOM, which are not
319 // supported by the CL pass-through back end because they have no standard feature set.
320 // This makes them unreliable for the purpose of this back end.
321 cl_uint numDevices = 0u;
322 if (mNative->getDispatch().clGetDeviceIDs(mNative, CL_DEVICE_TYPE_ALL, 0u, nullptr,
323 &numDevices) == CL_SUCCESS)
324 {
325 std::vector<cl_device_id> nativeDevices(numDevices, nullptr);
326 if (mNative->getDispatch().clGetDeviceIDs(mNative, CL_DEVICE_TYPE_ALL, numDevices,
327 nativeDevices.data(), nullptr) == CL_SUCCESS)
328 {
329 // Fetch all device types for front end initialization, and find the default device.
330 // If none exists declare first device as default.
331 std::vector<cl::DeviceType> types(nativeDevices.size());
332 size_t defaultIndex = 0u;
333 for (size_t index = 0u; index < nativeDevices.size(); ++index)
334 {
335 if (nativeDevices[index]->getDispatch().clGetDeviceInfo(
336 nativeDevices[index], CL_DEVICE_TYPE, sizeof(cl_device_type), &types[index],
337 nullptr) == CL_SUCCESS)
338 {
339 // If default device found, select it
340 if (types[index].isSet(CL_DEVICE_TYPE_DEFAULT))
341 {
342 defaultIndex = index;
343 }
344 }
345 else
346 {
347 types.clear();
348 nativeDevices.clear();
349 }
350 }
351
352 for (size_t index = 0u; index < nativeDevices.size(); ++index)
353 {
354 // Make sure the default bit is set in exactly one device
355 if (index == defaultIndex)
356 {
357 types[index].set(CL_DEVICE_TYPE_DEFAULT);
358 }
359 else
360 {
361 types[index].clear(CL_DEVICE_TYPE_DEFAULT);
362 }
363
364 cl_device_id nativeDevice = nativeDevices[index];
365 createDatas.emplace_back(types[index], [nativeDevice](const cl::Device &device) {
366 return CLDeviceCL::Ptr(new CLDeviceCL(device, nativeDevice));
367 });
368 }
369 }
370 }
371
372 if (createDatas.empty())
373 {
374 ERR() << "Failed to query CL devices";
375 }
376 return createDatas;
377 }
378
createContext(cl::Context & context,const cl::DevicePtrs & devices,bool userSync,cl_int & errorCode)379 CLContextImpl::Ptr CLPlatformCL::createContext(cl::Context &context,
380 const cl::DevicePtrs &devices,
381 bool userSync,
382 cl_int &errorCode)
383 {
384 cl_context_properties properties[] = {
385 CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(mNative),
386 userSync && mPlatform.isVersionOrNewer(1u, 2u) ? CL_CONTEXT_INTEROP_USER_SYNC : 0, CL_TRUE,
387 0};
388
389 std::vector<cl_device_id> nativeDevices;
390 for (const cl::DevicePtr &device : devices)
391 {
392 nativeDevices.emplace_back(device->getImpl<CLDeviceCL>().getNative());
393 }
394
395 cl_context nativeContext = mNative->getDispatch().clCreateContext(
396 properties, static_cast<cl_uint>(nativeDevices.size()), nativeDevices.data(),
397 cl::Context::ErrorCallback, &context, &errorCode);
398 return CLContextImpl::Ptr(nativeContext != nullptr ? new CLContextCL(context, nativeContext)
399 : nullptr);
400 }
401
createContextFromType(cl::Context & context,cl::DeviceType deviceType,bool userSync,cl_int & errorCode)402 CLContextImpl::Ptr CLPlatformCL::createContextFromType(cl::Context &context,
403 cl::DeviceType deviceType,
404 bool userSync,
405 cl_int &errorCode)
406 {
407 cl_context_properties properties[] = {
408 CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(mNative),
409 userSync && mPlatform.isVersionOrNewer(1u, 2u) ? CL_CONTEXT_INTEROP_USER_SYNC : 0, CL_TRUE,
410 0};
411 cl_context nativeContext = mNative->getDispatch().clCreateContextFromType(
412 properties, deviceType.get(), cl::Context::ErrorCallback, &context, &errorCode);
413 return CLContextImpl::Ptr(nativeContext != nullptr ? new CLContextCL(context, nativeContext)
414 : nullptr);
415 }
416
unloadCompiler()417 cl_int CLPlatformCL::unloadCompiler()
418 {
419 return mNative->getDispatch().clUnloadPlatformCompiler(mNative);
420 }
421
Initialize(CreateFuncs & createFuncs,bool isIcd)422 void CLPlatformCL::Initialize(CreateFuncs &createFuncs, bool isIcd)
423 {
424 // Using khrIcdInitialize() of the third party Khronos OpenCL ICD Loader to
425 // enumerate the available OpenCL implementations on the system. They will be
426 // stored in the singly linked list khrIcdVendors of the C struct KHRicdVendor.
427 khrIcdInitialize();
428
429 // The ICD loader will also enumerate ANGLE's OpenCL library if it is registered. Our
430 // OpenCL entry points for the ICD enumeration are reentrant, but at this point of the
431 // initialization there are no platforms available, so our platforms will not be found.
432 // This is intended as this back end should only enumerate non-ANGLE implementations.
433
434 // Iterating through the singly linked list khrIcdVendors to create
435 // an ANGLE CL pass-through platform for each found ICD platform.
436 for (KHRicdVendor *vendorIt = khrIcdVendors; vendorIt != nullptr; vendorIt = vendorIt->next)
437 {
438 cl_platform_id nativePlatform = vendorIt->platform;
439 createFuncs.emplace_back([nativePlatform](const cl::Platform &platform) {
440 return Ptr(new CLPlatformCL(platform, nativePlatform));
441 });
442 }
443 }
444
CLPlatformCL(const cl::Platform & platform,cl_platform_id native)445 CLPlatformCL::CLPlatformCL(const cl::Platform &platform, cl_platform_id native)
446 : CLPlatformImpl(platform), mNative(native)
447 {}
448
449 } // namespace rx
450