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>
34 ,DS_INVALID_PROFILE
= 1000
36 , DS_INVALID_PERF_EVALUATOR_TYPE
37 , DS_INVALID_PERF_EVALUATOR
38 , DS_PERF_EVALUATOR_ERROR
40 , DS_UNKNOWN_DEVICE_TYPE
41 , DS_PROFILE_FILE_ERROR
42 , DS_SCORE_SERIALIZER_ERROR
43 , DS_SCORE_DESERIALIZER_ERROR
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.)
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.
62 cl_device_id aDeviceID
;
64 OString sPlatformName
;
65 OString sPlatformVendor
;
66 OString sPlatformVersion
;
67 OString sPlatformProfile
;
68 OString sPlatformExtensions
;
71 OString sDeviceVendor
;
72 OString sDeviceVersion
;
73 OString sDriverVersion
;
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
88 std::vector
<ds_device
> devices
;
89 OString
const version
;
91 ds_profile(OString
const & 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
)
113 cl_device_type aDeviceType
;
114 clGetDeviceInfo(aDeviceId
, CL_DEVICE_TYPE
, sizeof(aDeviceType
), &aDeviceType
, nullptr);
115 if (aDeviceType
& CL_DEVICE_TYPE_CPU
)
117 if (aDeviceType
& CL_DEVICE_TYPE_GPU
)
119 if (aDeviceType
& CL_DEVICE_TYPE_ACCELERATOR
)
120 sType
+= "accelerator ";
121 if (aDeviceType
& CL_DEVICE_TYPE_CUSTOM
)
123 if (aDeviceType
& CL_DEVICE_TYPE_DEFAULT
)
128 inline bool getDeviceInfoBool(cl_device_id aDeviceId
, cl_device_info aDeviceInfo
)
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
)
142 cl_uint numPlatforms
;
143 std::vector
<cl_platform_id
> platforms
;
144 std::vector
<cl_device_id
> devices
;
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);
159 for (i
= 0; i
< static_cast<unsigned int>(numPlatforms
); i
++)
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
177 devices
.resize(numDevices
);
180 rProfile
->devices
.resize(numDevices
+ 1); // +1 to numDevices to include the native CPU
183 for (i
= 0; i
< static_cast<unsigned int>(numPlatforms
); i
++)
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
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
;
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();
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();
285 aXmlWriter
.startElement("time");
286 if (rtl::math::approxEqual(rDevice
.fTime
, DBL_MAX
))
287 aXmlWriter
.content(OString("max"));
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();
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")
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")
333 DeviceType eDeviceType
= DeviceType::None
;
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
;
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")
364 fTime
= aWalker
.content().toDouble();
366 else if (aWalker
.name() == "errors")
368 bErrors
= (aWalker
.content() == "true");
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
;
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */