Bump version to 6.0-36
[LibreOffice.git] / opencl / inc / opencl_device_selection.h
blob5ebc87a9410c0c3553c09d40b9415fe5ba21b380
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 <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
23 #include <clew/clew.h>
24 #include <tools/stream.hxx>
25 #include <tools/XmlWriter.hxx>
26 #include <tools/XmlWalker.hxx>
27 #include <rtl/math.hxx>
29 #include <opencl/OpenCLZone.hxx>
31 #include <vector>
33 enum ds_status
35 DS_SUCCESS = 0
36 ,DS_INVALID_PROFILE = 1000
37 ,DS_MEMORY_ERROR
38 , DS_INVALID_PERF_EVALUATOR_TYPE
39 , DS_INVALID_PERF_EVALUATOR
40 , DS_PERF_EVALUATOR_ERROR
41 , DS_FILE_ERROR
42 , DS_UNKNOWN_DEVICE_TYPE
43 , DS_PROFILE_FILE_ERROR
44 , DS_SCORE_SERIALIZER_ERROR
45 , DS_SCORE_DESERIALIZER_ERROR
48 // device type
49 enum class DeviceType
51 None,
52 // NativeCPU means the traditional Calc interpreter code path. (That also includes the so-called
53 // "software interpreter", but note that it definitely does not mean *exclusively* that.)
54 NativeCPU,
55 // OpenCLDevice means an OpenCL device as supplied by an OpenCL platform, which might well be
56 // implemented using code that runs on the CPU (and not a GPU). On Windows, OpenCL platforms
57 // typically provide two devices, one for the GPU and one for the CPU.
58 OpenCLDevice
61 struct ds_device
63 DeviceType eType;
64 cl_device_id aDeviceID;
66 OString sPlatformName;
67 OString sPlatformVendor;
68 OString sPlatformVersion;
69 OString sPlatformProfile;
70 OString sPlatformExtensions;
72 OString sDeviceName;
73 OString sDeviceVendor;
74 OString sDeviceVersion;
75 OString sDriverVersion;
76 OString sDeviceType;
77 OString sDeviceExtensions;
78 OString sDeviceOpenCLVersion;
80 bool bDeviceAvailable;
81 bool bDeviceCompilerAvailable;
82 bool bDeviceLinkerAvailable;
84 double fTime; // small time means faster device
85 bool bErrors; // were there any opencl errors
88 struct ds_profile
90 std::vector<ds_device> devices;
91 OString version;
93 ds_profile(OString const & inVersion)
94 : version(inVersion)
98 inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
100 std::vector<char> temporary(2048, 0);
101 clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
102 return OString(temporary.data());
105 inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
107 std::vector<char> temporary(2048, 0);
108 clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
109 return OString(temporary.data());
112 inline OString getDeviceType(cl_device_id aDeviceId)
114 OString sType = "";
115 cl_device_type aDeviceType;
116 clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
117 if (aDeviceType & CL_DEVICE_TYPE_CPU)
118 sType += "cpu ";
119 if (aDeviceType & CL_DEVICE_TYPE_GPU)
120 sType += "gpu ";
121 if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
122 sType += "accelerator ";
123 if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
124 sType += "custom ";
125 if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
126 sType += "default ";
127 return sType;
130 inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
132 cl_bool bCLBool = 0;
133 // init to false in case clGetDeviceInfo returns CL_INVALID_VALUE when
134 // requesting unsupported (in version 1.0) CL_DEVICE_LINKER_AVAILABLE
135 clGetDeviceInfo(aDeviceId, aDeviceInfo, sizeof(bCLBool), &bCLBool, nullptr);
136 return bCLBool == CL_TRUE;
139 inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString const & rVersion)
141 OpenCLZone zone;
143 int numDevices;
144 cl_uint numPlatforms;
145 std::vector<cl_platform_id> platforms;
146 std::vector<cl_device_id> devices;
148 unsigned int next;
149 unsigned int i;
151 rProfile.reset(new ds_profile(rVersion));
153 clGetPlatformIDs(0, nullptr, &numPlatforms);
154 if (numPlatforms != 0)
156 platforms.resize(numPlatforms);
157 clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
160 numDevices = 0;
161 for (i = 0; i < (unsigned int)numPlatforms; i++)
163 cl_uint num = 0;
164 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &num);
165 if (err != CL_SUCCESS)
167 /* we want to catch at least the case when the call returns
168 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
169 * don't set num to 0 in this case; but in fact this is a good
170 * thing to do for _any_ error returned by the call
172 num = 0;
174 numDevices += num;
177 if (numDevices != 0)
179 devices.resize(numDevices);
182 rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
184 next = 0;
185 for (i = 0; i < (unsigned int)numPlatforms; i++)
187 cl_uint num = 0;
188 unsigned j;
190 OString sPlatformProfile = getPlatformInfoString(platforms[i], CL_PLATFORM_PROFILE);
191 OString sPlatformVersion = getPlatformInfoString(platforms[i], CL_PLATFORM_VERSION);
192 OString sPlatformName = getPlatformInfoString(platforms[i], CL_PLATFORM_NAME);
193 OString sPlatformVendor = getPlatformInfoString(platforms[i], CL_PLATFORM_VENDOR);
194 OString sPlatformExts = getPlatformInfoString(platforms[i], CL_PLATFORM_EXTENSIONS);
196 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
197 if (err != CL_SUCCESS)
199 /* we want to catch at least the case when the call returns
200 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
201 * don't set num to 0 in this case; but in fact this is a good
202 * thing to do for _any_ error returned by the call
204 num = 0;
206 for (j = 0; j < num; j++, next++)
208 cl_device_id aDeviceID = devices[j];
210 ds_device& rDevice = rProfile->devices[next];
211 rDevice.eType = DeviceType::OpenCLDevice;
212 rDevice.aDeviceID = aDeviceID;
214 rDevice.sPlatformName = sPlatformName;
215 rDevice.sPlatformVendor = sPlatformVendor;
216 rDevice.sPlatformVersion = sPlatformVersion;
217 rDevice.sPlatformProfile = sPlatformProfile;
218 rDevice.sPlatformExtensions = sPlatformExts;
220 rDevice.sDeviceName = getDeviceInfoString(aDeviceID, CL_DEVICE_NAME);
221 rDevice.sDeviceVendor = getDeviceInfoString(aDeviceID, CL_DEVICE_VENDOR);
222 rDevice.sDeviceVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_VERSION);
223 rDevice.sDriverVersion = getDeviceInfoString(aDeviceID, CL_DRIVER_VERSION);
224 rDevice.sDeviceType = getDeviceType(aDeviceID);
225 rDevice.sDeviceExtensions = getDeviceInfoString(aDeviceID, CL_DEVICE_EXTENSIONS);
226 rDevice.sDeviceOpenCLVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_OPENCL_C_VERSION);
228 rDevice.bDeviceAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_AVAILABLE);
229 rDevice.bDeviceCompilerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_COMPILER_AVAILABLE);
230 rDevice.bDeviceLinkerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_LINKER_AVAILABLE);
233 rProfile->devices[next].eType = DeviceType::NativeCPU;
235 return DS_SUCCESS;
238 inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
240 if (pProfile == nullptr)
241 return DS_INVALID_PROFILE;
242 if (rStreamName.isEmpty())
243 return DS_INVALID_PROFILE;
245 std::unique_ptr<SvStream> pStream;
246 pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
248 tools::XmlWriter aXmlWriter(pStream.get());
250 if (!aXmlWriter.startDocument())
251 return DS_FILE_ERROR;
253 aXmlWriter.startElement("profile");
255 aXmlWriter.startElement("version");
256 aXmlWriter.content(pProfile->version);
257 aXmlWriter.endElement();
259 for (ds_device& rDevice : pProfile->devices)
261 aXmlWriter.startElement("device");
263 switch(rDevice.eType)
265 case DeviceType::NativeCPU:
266 aXmlWriter.startElement("type");
267 aXmlWriter.content(OString("native"));
268 aXmlWriter.endElement();
269 break;
270 case DeviceType::OpenCLDevice:
271 aXmlWriter.startElement("type");
272 aXmlWriter.content(OString("opencl"));
273 aXmlWriter.endElement();
275 aXmlWriter.startElement("name");
276 aXmlWriter.content(rDevice.sDeviceName);
277 aXmlWriter.endElement();
279 aXmlWriter.startElement("driver");
280 aXmlWriter.content(rDevice.sDriverVersion);
281 aXmlWriter.endElement();
282 break;
283 default:
284 break;
287 aXmlWriter.startElement("time");
288 if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
289 aXmlWriter.content(OString("max"));
290 else
291 aXmlWriter.content(OString::number(rDevice.fTime));
292 aXmlWriter.endElement();
294 aXmlWriter.startElement("errors");
295 aXmlWriter.content(rDevice.bErrors ? OString("true") : OString("false"));
296 aXmlWriter.endElement();
298 aXmlWriter.endElement();
301 aXmlWriter.endElement();
302 aXmlWriter.endDocument();
304 return DS_SUCCESS;
307 inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
309 ds_status eStatus = DS_SUCCESS;
311 if (rStreamName.isEmpty())
312 return DS_INVALID_PROFILE;
314 std::unique_ptr<SvStream> pStream;
315 pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
316 tools::XmlWalker aWalker;
318 if (!aWalker.open(pStream.get()))
319 return DS_FILE_ERROR;
321 if (aWalker.name() == "profile")
323 aWalker.children();
324 while (aWalker.isValid())
326 if (aWalker.name() == "version")
328 if (aWalker.content() != pProfile->version)
329 return DS_PROFILE_FILE_ERROR;
331 else if (aWalker.name() == "device")
333 aWalker.children();
335 DeviceType eDeviceType = DeviceType::None;
336 OString sName;
337 OString sVersion;
338 double fTime = -1.0;
339 bool bErrors = true;
341 while (aWalker.isValid())
343 if (aWalker.name() == "type")
345 OString sContent = aWalker.content();
346 if (sContent == "native")
347 eDeviceType = DeviceType::NativeCPU;
348 else if (sContent == "opencl")
349 eDeviceType = DeviceType::OpenCLDevice;
350 else
351 return DS_PROFILE_FILE_ERROR;
353 else if (aWalker.name() == "name")
355 sName = aWalker.content();
357 else if (aWalker.name() == "driver")
359 sVersion = aWalker.content();
361 else if (aWalker.name() == "time")
363 if (aWalker.content() == "max")
364 fTime = DBL_MAX;
365 else
366 fTime = aWalker.content().toDouble();
368 else if (aWalker.name() == "errors")
370 bErrors = (aWalker.content() == "true");
373 aWalker.next();
376 if (fTime < 0.0)
377 return DS_PROFILE_FILE_ERROR;
379 for (ds_device& rDevice : pProfile->devices)
381 // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
382 if (rDevice.eType == eDeviceType)
384 // is DS_DEVICE_NATIVE_CPU or name + version matches?
385 if (eDeviceType == DeviceType::NativeCPU ||
386 (sName == rDevice.sDeviceName &&
387 sVersion == rDevice.sDriverVersion))
389 rDevice.fTime = fTime;
390 rDevice.bErrors = bErrors;
395 aWalker.parent();
397 aWalker.next();
399 aWalker.parent();
402 return eStatus;
405 #endif
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */