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 // CLPlatform.cpp: Implements the cl::Platform class.
7
8 #include "libANGLE/Context.h"
9 #include "libANGLE/capture/FrameCapture.h"
10
11 #include "libANGLE/CLPlatform.h"
12
13 #include "entry_points_utils.h"
14 #include "libANGLE/CLContext.h"
15 #include "libANGLE/CLDevice.h"
16 #include "libANGLE/cl_utils.h"
17
18 #include <cstring>
19
20 namespace cl
21 {
22
23 namespace
24 {
25
IsDeviceTypeMatch(DeviceType select,DeviceType type)26 bool IsDeviceTypeMatch(DeviceType select, DeviceType type)
27 {
28 // The type 'DeviceType' is a bitfield, so it matches if any selected bit is set.
29 // A custom device is an exception, which only matches if it was explicitely selected, see:
30 // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clGetDeviceIDs
31 return type == CL_DEVICE_TYPE_CUSTOM ? select == CL_DEVICE_TYPE_CUSTOM
32 : type.intersects(select);
33 }
34
ParseContextProperties(const cl_context_properties * properties,Platform * & platform,bool & userSync)35 Context::PropArray ParseContextProperties(const cl_context_properties *properties,
36 Platform *&platform,
37 bool &userSync)
38 {
39 Context::PropArray propArray;
40 if (properties != nullptr)
41 {
42 const cl_context_properties *propIt = properties;
43 while (*propIt != 0)
44 {
45 switch (*propIt++)
46 {
47 case CL_CONTEXT_PLATFORM:
48 platform = &reinterpret_cast<cl_platform_id>(*propIt++)->cast<Platform>();
49 break;
50 case CL_CONTEXT_INTEROP_USER_SYNC:
51 userSync = *propIt++ != CL_FALSE;
52 break;
53 }
54 }
55 // Include the trailing zero
56 ++propIt;
57 propArray.reserve(propIt - properties);
58 propArray.insert(propArray.cend(), properties, propIt);
59 }
60 return propArray;
61 }
62
63 } // namespace
64
Initialize(const cl_icd_dispatch & dispatch,rx::CLPlatformImpl::CreateFuncs && createFuncs)65 void Platform::Initialize(const cl_icd_dispatch &dispatch,
66 rx::CLPlatformImpl::CreateFuncs &&createFuncs)
67 {
68 PlatformPtrs &platforms = GetPointers();
69 ASSERT(_cl_platform_id::sDispatch == nullptr && platforms.empty());
70 if (_cl_platform_id::sDispatch != nullptr || !platforms.empty())
71 {
72 ERR() << "Already initialized";
73 return;
74 }
75 Dispatch::sDispatch = &dispatch;
76
77 platforms.reserve(createFuncs.size());
78 while (!createFuncs.empty())
79 {
80 platforms.emplace_back(new Platform(createFuncs.front()));
81
82 // Release initialization reference, lifetime controlled by RefPointer.
83 platforms.back()->release();
84
85 // Remove platform on any errors
86 if (!platforms.back()->mInfo.isValid() || platforms.back()->mDevices.empty())
87 {
88 platforms.pop_back();
89 }
90
91 createFuncs.pop_front();
92 }
93 }
94
GetPlatformIDs(cl_uint numEntries,cl_platform_id * platforms,cl_uint * numPlatforms)95 angle::Result Platform::GetPlatformIDs(cl_uint numEntries,
96 cl_platform_id *platforms,
97 cl_uint *numPlatforms)
98 {
99 const PlatformPtrs &availPlatforms = GetPlatforms();
100 if (numPlatforms != nullptr)
101 {
102 *numPlatforms = static_cast<cl_uint>(availPlatforms.size());
103 }
104 if (platforms != nullptr)
105 {
106 cl_uint entry = 0u;
107 auto platformIt = availPlatforms.cbegin();
108 while (entry < numEntries && platformIt != availPlatforms.cend())
109 {
110 platforms[entry++] = (*platformIt++).get();
111 }
112 }
113 return angle::Result::Continue;
114 }
115
getInfo(PlatformInfo name,size_t valueSize,void * value,size_t * valueSizeRet) const116 angle::Result Platform::getInfo(PlatformInfo name,
117 size_t valueSize,
118 void *value,
119 size_t *valueSizeRet) const
120 {
121 const void *copyValue = nullptr;
122 size_t copySize = 0u;
123
124 switch (name)
125 {
126 case PlatformInfo::Profile:
127 copyValue = mInfo.profile.c_str();
128 copySize = mInfo.profile.length() + 1u;
129 break;
130 case PlatformInfo::Version:
131 copyValue = mInfo.versionStr.c_str();
132 copySize = mInfo.versionStr.length() + 1u;
133 break;
134 case PlatformInfo::NumericVersion:
135 copyValue = &mInfo.version;
136 copySize = sizeof(mInfo.version);
137 break;
138 case PlatformInfo::Name:
139 copyValue = mInfo.name.c_str();
140 copySize = mInfo.name.length() + 1u;
141 break;
142 case PlatformInfo::Vendor:
143 copyValue = kVendor;
144 copySize = sizeof(kVendor);
145 break;
146 case PlatformInfo::Extensions:
147 copyValue = mInfo.extensions.c_str();
148 copySize = mInfo.extensions.length() + 1u;
149 break;
150 case PlatformInfo::ExtensionsWithVersion:
151 copyValue = mInfo.extensionsWithVersion.data();
152 copySize = mInfo.extensionsWithVersion.size() *
153 sizeof(decltype(mInfo.extensionsWithVersion)::value_type);
154 break;
155 case PlatformInfo::HostTimerResolution:
156 copyValue = &mInfo.hostTimerRes;
157 copySize = sizeof(mInfo.hostTimerRes);
158 break;
159 case PlatformInfo::IcdSuffix:
160 copyValue = kIcdSuffix;
161 copySize = sizeof(kIcdSuffix);
162 break;
163 default:
164 ASSERT(false);
165 ANGLE_CL_RETURN_ERROR(CL_INVALID_VALUE);
166 }
167
168 if (value != nullptr)
169 {
170 // CL_INVALID_VALUE if size in bytes specified by param_value_size is < size of return type
171 // as specified in the OpenCL Platform Queries table, and param_value is not a NULL value.
172 if (valueSize < copySize)
173 {
174 ANGLE_CL_RETURN_ERROR(CL_INVALID_VALUE);
175 }
176 if (copyValue != nullptr)
177 {
178 std::memcpy(value, copyValue, copySize);
179 }
180 }
181 if (valueSizeRet != nullptr)
182 {
183 *valueSizeRet = copySize;
184 }
185 return angle::Result::Continue;
186 }
187
getDeviceIDs(DeviceType deviceType,cl_uint numEntries,cl_device_id * devices,cl_uint * numDevices) const188 angle::Result Platform::getDeviceIDs(DeviceType deviceType,
189 cl_uint numEntries,
190 cl_device_id *devices,
191 cl_uint *numDevices) const
192 {
193 cl_uint found = 0u;
194 for (const DevicePtr &device : mDevices)
195 {
196 if (IsDeviceTypeMatch(deviceType, device->getInfo().type))
197 {
198 if (devices != nullptr && found < numEntries)
199 {
200 devices[found] = device.get();
201 }
202 ++found;
203 }
204 }
205 if (numDevices != nullptr)
206 {
207 *numDevices = found;
208 }
209
210 // CL_DEVICE_NOT_FOUND if no OpenCL devices that matched device_type were found.
211 if (found == 0u)
212 {
213 ANGLE_CL_RETURN_ERROR(CL_DEVICE_NOT_FOUND);
214 }
215
216 return angle::Result::Continue;
217 }
218
hasDeviceType(DeviceType deviceType) const219 bool Platform::hasDeviceType(DeviceType deviceType) const
220 {
221 for (const DevicePtr &device : mDevices)
222 {
223 if (IsDeviceTypeMatch(deviceType, device->getInfo().type))
224 {
225 return true;
226 }
227 }
228 return false;
229 }
230
CreateContext(const cl_context_properties * properties,cl_uint numDevices,const cl_device_id * devices,ContextErrorCB notify,void * userData)231 cl_context Platform::CreateContext(const cl_context_properties *properties,
232 cl_uint numDevices,
233 const cl_device_id *devices,
234 ContextErrorCB notify,
235 void *userData)
236 {
237 DevicePtrs devs;
238 devs.reserve(numDevices);
239 while (numDevices-- != 0u)
240 {
241 devs.emplace_back(&(*devices++)->cast<Device>());
242 }
243
244 Platform *platform = nullptr;
245 bool userSync = false;
246 Context::PropArray propArray = ParseContextProperties(properties, platform, userSync);
247 if (platform == nullptr)
248 {
249 // All devices in the list have already been validated at this point to contain the same
250 // platform - just use/select the first device's platform.
251 platform = &devs.front()->getPlatform();
252 }
253
254 return Object::Create<Context>(*platform, std::move(propArray), std::move(devs), notify,
255 userData, userSync);
256 }
257
CreateContextFromType(const cl_context_properties * properties,DeviceType deviceType,ContextErrorCB notify,void * userData)258 cl_context Platform::CreateContextFromType(const cl_context_properties *properties,
259 DeviceType deviceType,
260 ContextErrorCB notify,
261 void *userData)
262 {
263 Platform *platform = nullptr;
264 bool userSync = false;
265 Context::PropArray propArray = ParseContextProperties(properties, platform, userSync);
266
267 // Choose default platform if user does not specify in the context properties field
268 if (platform == nullptr)
269 {
270 platform = Platform::GetDefault();
271 }
272
273 return Object::Create<Context>(*platform, std::move(propArray), deviceType, notify, userData,
274 userSync);
275 }
276
unloadCompiler()277 angle::Result Platform::unloadCompiler()
278 {
279 return mImpl->unloadCompiler();
280 }
281
282 Platform::~Platform() = default;
283
Platform(const rx::CLPlatformImpl::CreateFunc & createFunc)284 Platform::Platform(const rx::CLPlatformImpl::CreateFunc &createFunc)
285 : mImpl(createFunc(*this)),
286 mInfo(mImpl ? mImpl->createInfo() : rx::CLPlatformImpl::Info{}),
287 mDevices(mImpl ? createDevices(mImpl->createDevices()) : DevicePtrs{}),
288 mMultiThreadPool(mImpl ? angle::WorkerThreadPool::Create(0, ANGLEPlatformCurrent()) : nullptr)
289 {}
290
createDevices(rx::CLDeviceImpl::CreateDatas && createDatas)291 DevicePtrs Platform::createDevices(rx::CLDeviceImpl::CreateDatas &&createDatas)
292 {
293 DevicePtrs devices;
294 devices.reserve(createDatas.size());
295 while (!createDatas.empty())
296 {
297 devices.emplace_back(
298 new Device(*this, nullptr, createDatas.front().first, createDatas.front().second));
299 // Release initialization reference, lifetime controlled by RefPointer.
300 devices.back()->release();
301 if (!devices.back()->mInfo.isValid())
302 {
303 devices.pop_back();
304 }
305 createDatas.pop_front();
306 }
307 return devices;
308 }
309
getFrameCaptureShared()310 angle::FrameCaptureShared *Platform::getFrameCaptureShared()
311 {
312 if (mFrameCaptureShared == nullptr)
313 {
314 mFrameCaptureShared = new angle::FrameCaptureShared;
315 }
316 return mFrameCaptureShared;
317 }
318
319 angle::FrameCaptureShared *Platform::mFrameCaptureShared = nullptr;
320 constexpr char Platform::kVendor[];
321 constexpr char Platform::kIcdSuffix[];
322
323 } // namespace cl
324