1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11 #define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
14 //#define _CRT_SECURE_NO_WARNINGS
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>
35 ,DS_INVALID_PROFILE
= 1000
37 , DS_INVALID_PERF_EVALUATOR_TYPE
38 , DS_INVALID_PERF_EVALUATOR
39 , DS_PERF_EVALUATOR_ERROR
41 , DS_UNKNOWN_DEVICE_TYPE
42 , DS_PROFILE_FILE_ERROR
43 , DS_SCORE_SERIALIZER_ERROR
44 , DS_SCORE_DESERIALIZER_ERROR
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.
63 cl_device_id aDeviceID
;
65 OString sPlatformName
;
66 OString sPlatformVendor
;
67 OString sPlatformVersion
;
68 OString sPlatformProfile
;
69 OString sPlatformExtensions
;
72 OString sDeviceVendor
;
73 OString sDeviceVersion
;
74 OString sDriverVersion
;
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
89 std::vector
<ds_device
> devices
;
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
)
118 if (aDeviceType
& CL_DEVICE_TYPE_GPU
)
120 if (aDeviceType
& CL_DEVICE_TYPE_ACCELERATOR
)
121 sType
+= "accelerator ";
122 if (aDeviceType
& CL_DEVICE_TYPE_CUSTOM
)
124 if (aDeviceType
& CL_DEVICE_TYPE_DEFAULT
)
129 inline bool getDeviceInfoBool(cl_device_id aDeviceId
, cl_device_info aDeviceInfo
)
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
)
143 cl_uint numPlatforms
;
144 std::vector
<cl_platform_id
> platforms
;
145 std::vector
<cl_device_id
> devices
;
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);
160 for (i
= 0; i
< static_cast<unsigned int>(numPlatforms
); i
++)
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
178 devices
.resize(numDevices
);
181 rProfile
->devices
.resize(numDevices
+ 1); // +1 to numDevices to include the native CPU
184 for (i
= 0; i
< static_cast<unsigned int>(numPlatforms
); i
++)
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
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
;
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();
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();
286 aXmlWriter
.startElement("time");
287 if (rtl::math::approxEqual(rDevice
.fTime
, DBL_MAX
))
288 aXmlWriter
.content("max"_ostr
);
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();
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")
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")
334 DeviceType eDeviceType
= DeviceType::None
;
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
;
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")
365 fTime
= aWalker
.content().toDouble();
367 else if (aWalker
.name() == "errors")
369 bErrors
= (aWalker
.content() == "true");
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
;
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */