LibreOffice Module opencl (master) 1
opencl_device_selection.h
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11#define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
12
13#ifdef _MSC_VER
14//#define _CRT_SECURE_NO_WARNINGS
15#endif
16
17#include <memory>
18
19#include <float.h>
20
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>
26
27#include <opencl/OpenCLZone.hxx>
28
29#include <utility>
30#include <vector>
31
33{
34 DS_SUCCESS = 0
45};
46
47// device type
48enum class DeviceType
49{
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.)
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.
58};
59
61{
63 cl_device_id aDeviceID;
64
70
71 OString sDeviceName;
75 OString sDeviceType;
78
82
83 double fTime; // small time means faster device
84 bool bErrors; // were there any opencl errors
85};
86
88{
89 std::vector<ds_device> devices;
90 OString version;
91
92 ds_profile(OString inVersion)
93 : version(std::move(inVersion))
94 {}
95};
96
97inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
98{
99 std::vector<char> temporary(2048, 0);
100 clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
101 return temporary.data();
102}
103
104inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
105{
106 std::vector<char> temporary(2048, 0);
107 clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
108 return temporary.data();
109}
110
111inline OString getDeviceType(cl_device_id aDeviceId)
112{
113 OString sType = "";
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;
127}
128
129inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
130{
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;
136}
137
138inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString const & rVersion)
139{
140 OpenCLZone zone;
141
142 int numDevices;
143 cl_uint numPlatforms;
144 std::vector<cl_platform_id> platforms;
145 std::vector<cl_device_id> devices;
146
147 unsigned int next;
148 unsigned int i;
149
150 rProfile.reset(new ds_profile(rVersion));
151
152 clGetPlatformIDs(0, nullptr, &numPlatforms);
153 if (numPlatforms != 0)
154 {
155 platforms.resize(numPlatforms);
156 clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
157 }
158
159 numDevices = 0;
160 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
161 {
162 cl_uint num = 0;
163 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &num);
164 if (err != CL_SUCCESS)
165 {
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
170 */
171 num = 0;
172 }
173 numDevices += num;
174 }
175
176 if (numDevices != 0)
177 {
178 devices.resize(numDevices);
179 }
180
181 rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
182
183 next = 0;
184 for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
185 {
186 cl_uint num = 0;
187 unsigned j;
188
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);
194
195 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
196 if (err != CL_SUCCESS)
197 {
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
202 */
203 num = 0;
204 }
205 for (j = 0; j < num; j++, next++)
206 {
207 cl_device_id aDeviceID = devices[j];
208
209 ds_device& rDevice = rProfile->devices[next];
211 rDevice.aDeviceID = aDeviceID;
212
213 rDevice.sPlatformName = sPlatformName;
214 rDevice.sPlatformVendor = sPlatformVendor;
215 rDevice.sPlatformVersion = sPlatformVersion;
216 rDevice.sPlatformProfile = sPlatformProfile;
217 rDevice.sPlatformExtensions = sPlatformExts;
218
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);
226
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);
230 }
231 }
232 rProfile->devices[next].eType = DeviceType::NativeCPU;
233
234 return DS_SUCCESS;
235}
236
237inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
238{
239 if (pProfile == nullptr)
240 return DS_INVALID_PROFILE;
241 if (rStreamName.isEmpty())
242 return DS_INVALID_PROFILE;
243
244 std::unique_ptr<SvStream> pStream;
245 pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
246
247 tools::XmlWriter aXmlWriter(pStream.get());
248
249 if (!aXmlWriter.startDocument())
250 return DS_FILE_ERROR;
251
252 aXmlWriter.startElement("profile");
253
254 aXmlWriter.startElement("version");
255 aXmlWriter.content(pProfile->version);
256 aXmlWriter.endElement();
257
258 for (const ds_device& rDevice : pProfile->devices)
259 {
260 aXmlWriter.startElement("device");
261
262 switch(rDevice.eType)
263 {
265 aXmlWriter.startElement("type");
266 aXmlWriter.content(OString("native"));
267 aXmlWriter.endElement();
268 break;
270 aXmlWriter.startElement("type");
271 aXmlWriter.content(OString("opencl"));
272 aXmlWriter.endElement();
273
274 aXmlWriter.startElement("name");
275 aXmlWriter.content(rDevice.sDeviceName);
276 aXmlWriter.endElement();
277
278 aXmlWriter.startElement("driver");
279 aXmlWriter.content(rDevice.sDriverVersion);
280 aXmlWriter.endElement();
281 break;
282 default:
283 break;
284 }
285
286 aXmlWriter.startElement("time");
287 if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
288 aXmlWriter.content(OString("max"));
289 else
290 aXmlWriter.content(OString::number(rDevice.fTime));
291 aXmlWriter.endElement();
292
293 aXmlWriter.startElement("errors");
294 aXmlWriter.content(rDevice.bErrors ? OString("true") : OString("false"));
295 aXmlWriter.endElement();
296
297 aXmlWriter.endElement();
298 }
299
300 aXmlWriter.endElement();
301 aXmlWriter.endDocument();
302
303 return DS_SUCCESS;
304}
305
306inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
307{
308 ds_status eStatus = DS_SUCCESS;
309
310 if (rStreamName.isEmpty())
311 return DS_INVALID_PROFILE;
312
313 std::unique_ptr<SvStream> pStream;
314 pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
315 tools::XmlWalker aWalker;
316
317 if (!aWalker.open(pStream.get()))
318 return DS_FILE_ERROR;
319
320 if (aWalker.name() == "profile")
321 {
322 aWalker.children();
323 while (aWalker.isValid())
324 {
325 if (aWalker.name() == "version")
326 {
327 if (aWalker.content() != pProfile->version)
329 }
330 else if (aWalker.name() == "device")
331 {
332 aWalker.children();
333
334 DeviceType eDeviceType = DeviceType::None;
335 OString sName;
336 OString sVersion;
337 double fTime = -1.0;
338 bool bErrors = true;
339
340 while (aWalker.isValid())
341 {
342 if (aWalker.name() == "type")
343 {
344 OString sContent = aWalker.content();
345 if (sContent == "native")
346 eDeviceType = DeviceType::NativeCPU;
347 else if (sContent == "opencl")
348 eDeviceType = DeviceType::OpenCLDevice;
349 else
351 }
352 else if (aWalker.name() == "name")
353 {
354 sName = aWalker.content();
355 }
356 else if (aWalker.name() == "driver")
357 {
358 sVersion = aWalker.content();
359 }
360 else if (aWalker.name() == "time")
361 {
362 if (aWalker.content() == "max")
363 fTime = DBL_MAX;
364 else
365 fTime = aWalker.content().toDouble();
366 }
367 else if (aWalker.name() == "errors")
368 {
369 bErrors = (aWalker.content() == "true");
370 }
371
372 aWalker.next();
373 }
374
375 if (fTime < 0.0)
377
378 for (ds_device& rDevice : pProfile->devices)
379 {
380 // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
381 if (rDevice.eType == eDeviceType)
382 {
383 // is DS_DEVICE_NATIVE_CPU or name + version matches?
384 if (eDeviceType == DeviceType::NativeCPU ||
385 (sName == rDevice.sDeviceName &&
386 sVersion == rDevice.sDriverVersion))
387 {
388 rDevice.fTime = fTime;
389 rDevice.bErrors = bErrors;
390 }
391 }
392 }
393
394 aWalker.parent();
395 }
396 aWalker.next();
397 }
398 aWalker.parent();
399 }
400
401 return eStatus;
402}
403
404#endif
405
406/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sType
OString content()
bool isValid() const
bool open(SvStream *pStream)
void content(const OString &sValue)
bool startDocument(sal_Int32 nIndent=2, bool bWriteXmlHeader=true)
void startElement(const char *sName)
OUString sName
err
int i
constexpr T & temporary(T &&x)
None
@ DS_SCORE_DESERIALIZER_ERROR
@ DS_SCORE_SERIALIZER_ERROR
@ DS_PROFILE_FILE_ERROR
@ DS_INVALID_PERF_EVALUATOR
@ DS_MEMORY_ERROR
@ DS_UNKNOWN_DEVICE_TYPE
@ DS_INVALID_PROFILE
@ DS_INVALID_PERF_EVALUATOR_TYPE
@ DS_PERF_EVALUATOR_ERROR
OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
OString getDeviceType(cl_device_id aDeviceId)
ds_status writeProfile(const OUString &rStreamName, std::unique_ptr< ds_profile > const &pProfile)
ds_status readProfile(const OUString &rStreamName, std::unique_ptr< ds_profile > const &pProfile)
ds_status initDSProfile(std::unique_ptr< ds_profile > &rProfile, OString const &rVersion)
OString sPlatformExtensions
cl_device_id aDeviceID
OString sDeviceOpenCLVersion
std::vector< ds_device > devices
ds_profile(OString inVersion)