Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / opencl / inc / opencl_device_selection.h
blob4fa8cf4a55de6d7e7b885257be628c0163bf5a12
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11 #define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
13 #ifdef _MSC_VER
14 //#define _CRT_SECURE_NO_WARNINGS
15 #endif
17 #include <memory>
19 #include <float.h>
21 #include <clew/clew.h>
22 #include <tools/stream.hxx>
23 #include <tools/XmlWriter.hxx>
24 #include <tools/XmlWalker.hxx>
25 #include <rtl/math.hxx>
27 #include <opencl/OpenCLZone.hxx>
29 #include <vector>
31 enum ds_status
33 DS_SUCCESS = 0
34 ,DS_INVALID_PROFILE = 1000
35 ,DS_MEMORY_ERROR
36 , DS_INVALID_PERF_EVALUATOR_TYPE
37 , DS_INVALID_PERF_EVALUATOR
38 , DS_PERF_EVALUATOR_ERROR
39 , DS_FILE_ERROR
40 , DS_UNKNOWN_DEVICE_TYPE
41 , DS_PROFILE_FILE_ERROR
42 , DS_SCORE_SERIALIZER_ERROR
43 , DS_SCORE_DESERIALIZER_ERROR
46 // device type
47 enum class DeviceType
49 None,
50 // NativeCPU means the traditional Calc interpreter code path. (That also includes the so-called
51 // "software interpreter", but note that it definitely does not mean *exclusively* that.)
52 NativeCPU,
53 // OpenCLDevice means an OpenCL device as supplied by an OpenCL platform, which might well be
54 // implemented using code that runs on the CPU (and not a GPU). On Windows, OpenCL platforms
55 // typically provide two devices, one for the GPU and one for the CPU.
56 OpenCLDevice
59 struct ds_device
61 DeviceType eType;
62 cl_device_id aDeviceID;
64 OString sPlatformName;
65 OString sPlatformVendor;
66 OString sPlatformVersion;
67 OString sPlatformProfile;
68 OString sPlatformExtensions;
70 OString sDeviceName;
71 OString sDeviceVendor;
72 OString sDeviceVersion;
73 OString sDriverVersion;
74 OString sDeviceType;
75 OString sDeviceExtensions;
76 OString sDeviceOpenCLVersion;
78 bool bDeviceAvailable;
79 bool bDeviceCompilerAvailable;
80 bool bDeviceLinkerAvailable;
82 double fTime; // small time means faster device
83 bool bErrors; // were there any opencl errors
86 struct ds_profile
88 std::vector<ds_device> devices;
89 OString const version;
91 ds_profile(OString const & inVersion)
92 : version(inVersion)
96 inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
98 std::vector<char> temporary(2048, 0);
99 clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
100 return temporary.data();
103 inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
105 std::vector<char> temporary(2048, 0);
106 clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
107 return temporary.data();
110 inline OString getDeviceType(cl_device_id aDeviceId)
112 OString sType = "";
113 cl_device_type aDeviceType;
114 clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
115 if (aDeviceType & CL_DEVICE_TYPE_CPU)
116 sType += "cpu ";
117 if (aDeviceType & CL_DEVICE_TYPE_GPU)
118 sType += "gpu ";
119 if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
120 sType += "accelerator ";
121 if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
122 sType += "custom ";
123 if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
124 sType += "default ";
125 return sType;
128 inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
130 cl_bool bCLBool = 0;
131 // init to false in case clGetDeviceInfo returns CL_INVALID_VALUE when
132 // requesting unsupported (in version 1.0) CL_DEVICE_LINKER_AVAILABLE
133 clGetDeviceInfo(aDeviceId, aDeviceInfo, sizeof(bCLBool), &bCLBool, nullptr);
134 return bCLBool == CL_TRUE;
137 inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString const & rVersion)
139 OpenCLZone zone;
141 int numDevices;
142 cl_uint numPlatforms;
143 std::vector<cl_platform_id> platforms;
144 std::vector<cl_device_id> devices;
146 unsigned int next;
147 unsigned int i;
149 rProfile.reset(new ds_profile(rVersion));
151 clGetPlatformIDs(0, nullptr, &numPlatforms);
152 if (numPlatforms != 0)
154 platforms.resize(numPlatforms);
155 clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
158 numDevices = 0;
159 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
161 cl_uint num = 0;
162 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &num);
163 if (err != CL_SUCCESS)
165 /* we want to catch at least the case when the call returns
166 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
167 * don't set num to 0 in this case; but in fact this is a good
168 * thing to do for _any_ error returned by the call
170 num = 0;
172 numDevices += num;
175 if (numDevices != 0)
177 devices.resize(numDevices);
180 rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
182 next = 0;
183 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
185 cl_uint num = 0;
186 unsigned j;
188 OString sPlatformProfile = getPlatformInfoString(platforms[i], CL_PLATFORM_PROFILE);
189 OString sPlatformVersion = getPlatformInfoString(platforms[i], CL_PLATFORM_VERSION);
190 OString sPlatformName = getPlatformInfoString(platforms[i], CL_PLATFORM_NAME);
191 OString sPlatformVendor = getPlatformInfoString(platforms[i], CL_PLATFORM_VENDOR);
192 OString sPlatformExts = getPlatformInfoString(platforms[i], CL_PLATFORM_EXTENSIONS);
194 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
195 if (err != CL_SUCCESS)
197 /* we want to catch at least the case when the call returns
198 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
199 * don't set num to 0 in this case; but in fact this is a good
200 * thing to do for _any_ error returned by the call
202 num = 0;
204 for (j = 0; j < num; j++, next++)
206 cl_device_id aDeviceID = devices[j];
208 ds_device& rDevice = rProfile->devices[next];
209 rDevice.eType = DeviceType::OpenCLDevice;
210 rDevice.aDeviceID = aDeviceID;
212 rDevice.sPlatformName = sPlatformName;
213 rDevice.sPlatformVendor = sPlatformVendor;
214 rDevice.sPlatformVersion = sPlatformVersion;
215 rDevice.sPlatformProfile = sPlatformProfile;
216 rDevice.sPlatformExtensions = sPlatformExts;
218 rDevice.sDeviceName = getDeviceInfoString(aDeviceID, CL_DEVICE_NAME);
219 rDevice.sDeviceVendor = getDeviceInfoString(aDeviceID, CL_DEVICE_VENDOR);
220 rDevice.sDeviceVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_VERSION);
221 rDevice.sDriverVersion = getDeviceInfoString(aDeviceID, CL_DRIVER_VERSION);
222 rDevice.sDeviceType = getDeviceType(aDeviceID);
223 rDevice.sDeviceExtensions = getDeviceInfoString(aDeviceID, CL_DEVICE_EXTENSIONS);
224 rDevice.sDeviceOpenCLVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_OPENCL_C_VERSION);
226 rDevice.bDeviceAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_AVAILABLE);
227 rDevice.bDeviceCompilerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_COMPILER_AVAILABLE);
228 rDevice.bDeviceLinkerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_LINKER_AVAILABLE);
231 rProfile->devices[next].eType = DeviceType::NativeCPU;
233 return DS_SUCCESS;
236 inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
238 if (pProfile == nullptr)
239 return DS_INVALID_PROFILE;
240 if (rStreamName.isEmpty())
241 return DS_INVALID_PROFILE;
243 std::unique_ptr<SvStream> pStream;
244 pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
246 tools::XmlWriter aXmlWriter(pStream.get());
248 if (!aXmlWriter.startDocument())
249 return DS_FILE_ERROR;
251 aXmlWriter.startElement("profile");
253 aXmlWriter.startElement("version");
254 aXmlWriter.content(pProfile->version);
255 aXmlWriter.endElement();
257 for (const ds_device& rDevice : pProfile->devices)
259 aXmlWriter.startElement("device");
261 switch(rDevice.eType)
263 case DeviceType::NativeCPU:
264 aXmlWriter.startElement("type");
265 aXmlWriter.content(OString("native"));
266 aXmlWriter.endElement();
267 break;
268 case DeviceType::OpenCLDevice:
269 aXmlWriter.startElement("type");
270 aXmlWriter.content(OString("opencl"));
271 aXmlWriter.endElement();
273 aXmlWriter.startElement("name");
274 aXmlWriter.content(rDevice.sDeviceName);
275 aXmlWriter.endElement();
277 aXmlWriter.startElement("driver");
278 aXmlWriter.content(rDevice.sDriverVersion);
279 aXmlWriter.endElement();
280 break;
281 default:
282 break;
285 aXmlWriter.startElement("time");
286 if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
287 aXmlWriter.content(OString("max"));
288 else
289 aXmlWriter.content(OString::number(rDevice.fTime));
290 aXmlWriter.endElement();
292 aXmlWriter.startElement("errors");
293 aXmlWriter.content(rDevice.bErrors ? OString("true") : OString("false"));
294 aXmlWriter.endElement();
296 aXmlWriter.endElement();
299 aXmlWriter.endElement();
300 aXmlWriter.endDocument();
302 return DS_SUCCESS;
305 inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
307 ds_status eStatus = DS_SUCCESS;
309 if (rStreamName.isEmpty())
310 return DS_INVALID_PROFILE;
312 std::unique_ptr<SvStream> pStream;
313 pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
314 tools::XmlWalker aWalker;
316 if (!aWalker.open(pStream.get()))
317 return DS_FILE_ERROR;
319 if (aWalker.name() == "profile")
321 aWalker.children();
322 while (aWalker.isValid())
324 if (aWalker.name() == "version")
326 if (aWalker.content() != pProfile->version)
327 return DS_PROFILE_FILE_ERROR;
329 else if (aWalker.name() == "device")
331 aWalker.children();
333 DeviceType eDeviceType = DeviceType::None;
334 OString sName;
335 OString sVersion;
336 double fTime = -1.0;
337 bool bErrors = true;
339 while (aWalker.isValid())
341 if (aWalker.name() == "type")
343 OString sContent = aWalker.content();
344 if (sContent == "native")
345 eDeviceType = DeviceType::NativeCPU;
346 else if (sContent == "opencl")
347 eDeviceType = DeviceType::OpenCLDevice;
348 else
349 return DS_PROFILE_FILE_ERROR;
351 else if (aWalker.name() == "name")
353 sName = aWalker.content();
355 else if (aWalker.name() == "driver")
357 sVersion = aWalker.content();
359 else if (aWalker.name() == "time")
361 if (aWalker.content() == "max")
362 fTime = DBL_MAX;
363 else
364 fTime = aWalker.content().toDouble();
366 else if (aWalker.name() == "errors")
368 bErrors = (aWalker.content() == "true");
371 aWalker.next();
374 if (fTime < 0.0)
375 return DS_PROFILE_FILE_ERROR;
377 for (ds_device& rDevice : pProfile->devices)
379 // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
380 if (rDevice.eType == eDeviceType)
382 // is DS_DEVICE_NATIVE_CPU or name + version matches?
383 if (eDeviceType == DeviceType::NativeCPU ||
384 (sName == rDevice.sDeviceName &&
385 sVersion == rDevice.sDriverVersion))
387 rDevice.fTime = fTime;
388 rDevice.bErrors = bErrors;
393 aWalker.parent();
395 aWalker.next();
397 aWalker.parent();
400 return eStatus;
403 #endif
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */