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