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
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>
36 ,DS_INVALID_PROFILE
= 1000
38 , DS_INVALID_PERF_EVALUATOR_TYPE
39 , DS_INVALID_PERF_EVALUATOR
40 , DS_PERF_EVALUATOR_ERROR
42 , DS_UNKNOWN_DEVICE_TYPE
43 , DS_PROFILE_FILE_ERROR
44 , DS_SCORE_SERIALIZER_ERROR
45 , DS_SCORE_DESERIALIZER_ERROR
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.)
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.
64 cl_device_id aDeviceID
;
66 OString sPlatformName
;
67 OString sPlatformVendor
;
68 OString sPlatformVersion
;
69 OString sPlatformProfile
;
70 OString sPlatformExtensions
;
73 OString sDeviceVendor
;
74 OString sDeviceVersion
;
75 OString sDriverVersion
;
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
90 std::vector
<ds_device
> devices
;
93 ds_profile(OString
const & 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
)
115 cl_device_type aDeviceType
;
116 clGetDeviceInfo(aDeviceId
, CL_DEVICE_TYPE
, sizeof(aDeviceType
), &aDeviceType
, nullptr);
117 if (aDeviceType
& CL_DEVICE_TYPE_CPU
)
119 if (aDeviceType
& CL_DEVICE_TYPE_GPU
)
121 if (aDeviceType
& CL_DEVICE_TYPE_ACCELERATOR
)
122 sType
+= "accelerator ";
123 if (aDeviceType
& CL_DEVICE_TYPE_CUSTOM
)
125 if (aDeviceType
& CL_DEVICE_TYPE_DEFAULT
)
130 inline bool getDeviceInfoBool(cl_device_id aDeviceId
, cl_device_info aDeviceInfo
)
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
)
144 cl_uint numPlatforms
;
145 std::vector
<cl_platform_id
> platforms
;
146 std::vector
<cl_device_id
> devices
;
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);
161 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
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
179 devices
.resize(numDevices
);
182 rProfile
->devices
.resize(numDevices
+ 1); // +1 to numDevices to include the native CPU
185 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
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
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
;
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();
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();
287 aXmlWriter
.startElement("time");
288 if (rtl::math::approxEqual(rDevice
.fTime
, DBL_MAX
))
289 aXmlWriter
.content(OString("max"));
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();
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")
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")
335 DeviceType eDeviceType
= DeviceType::None
;
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
;
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")
366 fTime
= aWalker
.content().toDouble();
368 else if (aWalker
.name() == "errors")
370 bErrors
= (aWalker
.content() == "true");
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
;
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */