• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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