• 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/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