Get the style color and number just once
[LibreOffice.git] / opencl / inc / opencl_device_selection.h
blob4baffbf2737c1d360aa6e4c86853cbe383ab37b6
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 <utility>
30 #include <vector>
32 enum ds_status
34 DS_SUCCESS = 0
35 ,DS_INVALID_PROFILE = 1000
36 ,DS_MEMORY_ERROR
37 , DS_INVALID_PERF_EVALUATOR_TYPE
38 , DS_INVALID_PERF_EVALUATOR
39 , DS_PERF_EVALUATOR_ERROR
40 , DS_FILE_ERROR
41 , DS_UNKNOWN_DEVICE_TYPE
42 , DS_PROFILE_FILE_ERROR
43 , DS_SCORE_SERIALIZER_ERROR
44 , DS_SCORE_DESERIALIZER_ERROR
47 // device type
48 enum class DeviceType
50 None,
51 // NativeCPU means the traditional Calc interpreter code path. (That also includes the so-called
52 // "software interpreter", but note that it definitely does not mean *exclusively* that.)
53 NativeCPU,
54 // OpenCLDevice means an OpenCL device as supplied by an OpenCL platform, which might well be
55 // implemented using code that runs on the CPU (and not a GPU). On Windows, OpenCL platforms
56 // typically provide two devices, one for the GPU and one for the CPU.
57 OpenCLDevice
60 struct ds_device
62 DeviceType eType;
63 cl_device_id aDeviceID;
65 OString sPlatformName;
66 OString sPlatformVendor;
67 OString sPlatformVersion;
68 OString sPlatformProfile;
69 OString sPlatformExtensions;
71 OString sDeviceName;
72 OString sDeviceVendor;
73 OString sDeviceVersion;
74 OString sDriverVersion;
75 OString sDeviceType;
76 OString sDeviceExtensions;
77 OString sDeviceOpenCLVersion;
79 bool bDeviceAvailable;
80 bool bDeviceCompilerAvailable;
81 bool bDeviceLinkerAvailable;
83 double fTime; // small time means faster device
84 bool bErrors; // were there any opencl errors
87 struct ds_profile
89 std::vector<ds_device> devices;
90 OString version;
92 ds_profile(OString inVersion)
93 : version(std::move(inVersion))
97 inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
99 std::vector<char> temporary(2048, 0);
100 clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
101 return temporary.data();
104 inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
106 std::vector<char> temporary(2048, 0);
107 clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
108 return temporary.data();
111 inline OString getDeviceType(cl_device_id aDeviceId)
113 OString sType = ""_ostr;
114 cl_device_type aDeviceType;
115 clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
116 if (aDeviceType & CL_DEVICE_TYPE_CPU)
117 sType += "cpu ";
118 if (aDeviceType & CL_DEVICE_TYPE_GPU)
119 sType += "gpu ";
120 if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
121 sType += "accelerator ";
122 if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
123 sType += "custom ";
124 if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
125 sType += "default ";
126 return sType;
129 inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
131 cl_bool bCLBool = 0;
132 // init to false in case clGetDeviceInfo returns CL_INVALID_VALUE when
133 // requesting unsupported (in version 1.0) CL_DEVICE_LINKER_AVAILABLE
134 clGetDeviceInfo(aDeviceId, aDeviceInfo, sizeof(bCLBool), &bCLBool, nullptr);
135 return bCLBool == CL_TRUE;
138 inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString const & rVersion)
140 OpenCLZone zone;
142 int numDevices;
143 cl_uint numPlatforms;
144 std::vector<cl_platform_id> platforms;
145 std::vector<cl_device_id> devices;
147 unsigned int next;
148 unsigned int i;
150 rProfile.reset(new ds_profile(rVersion));
152 clGetPlatformIDs(0, nullptr, &numPlatforms);
153 if (numPlatforms != 0)
155 platforms.resize(numPlatforms);
156 clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
159 numDevices = 0;
160 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
162 cl_uint num = 0;
163 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &num);
164 if (err != CL_SUCCESS)
166 /* we want to catch at least the case when the call returns
167 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
168 * don't set num to 0 in this case; but in fact this is a good
169 * thing to do for _any_ error returned by the call
171 num = 0;
173 numDevices += num;
176 if (numDevices != 0)
178 devices.resize(numDevices);
181 rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
183 next = 0;
184 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
186 cl_uint num = 0;
187 unsigned j;
189 OString sPlatformProfile = getPlatformInfoString(platforms[i], CL_PLATFORM_PROFILE);
190 OString sPlatformVersion = getPlatformInfoString(platforms[i], CL_PLATFORM_VERSION);
191 OString sPlatformName = getPlatformInfoString(platforms[i], CL_PLATFORM_NAME);
192 OString sPlatformVendor = getPlatformInfoString(platforms[i], CL_PLATFORM_VENDOR);
193 OString sPlatformExts = getPlatformInfoString(platforms[i], CL_PLATFORM_EXTENSIONS);
195 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
196 if (err != CL_SUCCESS)
198 /* we want to catch at least the case when the call returns
199 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
200 * don't set num to 0 in this case; but in fact this is a good
201 * thing to do for _any_ error returned by the call
203 num = 0;
205 for (j = 0; j < num; j++, next++)
207 cl_device_id aDeviceID = devices[j];
209 ds_device& rDevice = rProfile->devices[next];
210 rDevice.eType = DeviceType::OpenCLDevice;
211 rDevice.aDeviceID = aDeviceID;
213 rDevice.sPlatformName = sPlatformName;
214 rDevice.sPlatformVendor = sPlatformVendor;
215 rDevice.sPlatformVersion = sPlatformVersion;
216 rDevice.sPlatformProfile = sPlatformProfile;
217 rDevice.sPlatformExtensions = sPlatformExts;
219 rDevice.sDeviceName = getDeviceInfoString(aDeviceID, CL_DEVICE_NAME);
220 rDevice.sDeviceVendor = getDeviceInfoString(aDeviceID, CL_DEVICE_VENDOR);
221 rDevice.sDeviceVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_VERSION);
222 rDevice.sDriverVersion = getDeviceInfoString(aDeviceID, CL_DRIVER_VERSION);
223 rDevice.sDeviceType = getDeviceType(aDeviceID);
224 rDevice.sDeviceExtensions = getDeviceInfoString(aDeviceID, CL_DEVICE_EXTENSIONS);
225 rDevice.sDeviceOpenCLVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_OPENCL_C_VERSION);
227 rDevice.bDeviceAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_AVAILABLE);
228 rDevice.bDeviceCompilerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_COMPILER_AVAILABLE);
229 rDevice.bDeviceLinkerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_LINKER_AVAILABLE);
232 rProfile->devices[next].eType = DeviceType::NativeCPU;
234 return DS_SUCCESS;
237 inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
239 if (pProfile == nullptr)
240 return DS_INVALID_PROFILE;
241 if (rStreamName.isEmpty())
242 return DS_INVALID_PROFILE;
244 std::unique_ptr<SvStream> pStream;
245 pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
247 tools::XmlWriter aXmlWriter(pStream.get());
249 if (!aXmlWriter.startDocument())
250 return DS_FILE_ERROR;
252 aXmlWriter.startElement("profile");
254 aXmlWriter.startElement("version");
255 aXmlWriter.content(pProfile->version);
256 aXmlWriter.endElement();
258 for (const ds_device& rDevice : pProfile->devices)
260 aXmlWriter.startElement("device");
262 switch(rDevice.eType)
264 case DeviceType::NativeCPU:
265 aXmlWriter.startElement("type");
266 aXmlWriter.content("native"_ostr);
267 aXmlWriter.endElement();
268 break;
269 case DeviceType::OpenCLDevice:
270 aXmlWriter.startElement("type");
271 aXmlWriter.content("opencl"_ostr);
272 aXmlWriter.endElement();
274 aXmlWriter.startElement("name");
275 aXmlWriter.content(rDevice.sDeviceName);
276 aXmlWriter.endElement();
278 aXmlWriter.startElement("driver");
279 aXmlWriter.content(rDevice.sDriverVersion);
280 aXmlWriter.endElement();
281 break;
282 default:
283 break;
286 aXmlWriter.startElement("time");
287 if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
288 aXmlWriter.content("max"_ostr);
289 else
290 aXmlWriter.content(OString::number(rDevice.fTime));
291 aXmlWriter.endElement();
293 aXmlWriter.startElement("errors");
294 aXmlWriter.content(rDevice.bErrors ? "true"_ostr : "false"_ostr);
295 aXmlWriter.endElement();
297 aXmlWriter.endElement();
300 aXmlWriter.endElement();
301 aXmlWriter.endDocument();
303 return DS_SUCCESS;
306 inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
308 ds_status eStatus = DS_SUCCESS;
310 if (rStreamName.isEmpty())
311 return DS_INVALID_PROFILE;
313 std::unique_ptr<SvStream> pStream;
314 pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
315 tools::XmlWalker aWalker;
317 if (!aWalker.open(pStream.get()))
318 return DS_FILE_ERROR;
320 if (aWalker.name() == "profile")
322 aWalker.children();
323 while (aWalker.isValid())
325 if (aWalker.name() == "version")
327 if (aWalker.content() != pProfile->version)
328 return DS_PROFILE_FILE_ERROR;
330 else if (aWalker.name() == "device")
332 aWalker.children();
334 DeviceType eDeviceType = DeviceType::None;
335 OString sName;
336 OString sVersion;
337 double fTime = -1.0;
338 bool bErrors = true;
340 while (aWalker.isValid())
342 if (aWalker.name() == "type")
344 OString sContent = aWalker.content();
345 if (sContent == "native")
346 eDeviceType = DeviceType::NativeCPU;
347 else if (sContent == "opencl")
348 eDeviceType = DeviceType::OpenCLDevice;
349 else
350 return DS_PROFILE_FILE_ERROR;
352 else if (aWalker.name() == "name")
354 sName = aWalker.content();
356 else if (aWalker.name() == "driver")
358 sVersion = aWalker.content();
360 else if (aWalker.name() == "time")
362 if (aWalker.content() == "max")
363 fTime = DBL_MAX;
364 else
365 fTime = aWalker.content().toDouble();
367 else if (aWalker.name() == "errors")
369 bErrors = (aWalker.content() == "true");
372 aWalker.next();
375 if (fTime < 0.0)
376 return DS_PROFILE_FILE_ERROR;
378 for (ds_device& rDevice : pProfile->devices)
380 // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
381 if (rDevice.eType == eDeviceType)
383 // is DS_DEVICE_NATIVE_CPU or name + version matches?
384 if (eDeviceType == DeviceType::NativeCPU ||
385 (sName == rDevice.sDeviceName &&
386 sVersion == rDevice.sDriverVersion))
388 rDevice.fTime = fTime;
389 rDevice.bErrors = bErrors;
394 aWalker.parent();
396 aWalker.next();
398 aWalker.parent();
401 return eStatus;
404 #endif
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */