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 <libxml/xmlwriter.h>
23 #include <libxml/xmlstring.h>
24 #include <tools/stream.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
57 cl_device_id aDeviceID
;
59 OString sPlatformName
;
60 OString sPlatformVendor
;
61 OString sPlatformVersion
;
62 OString sPlatformProfile
;
63 OString sPlatformExtensions
;
66 OString sDeviceVendor
;
67 OString sDeviceVersion
;
68 OString sDriverVersion
;
70 OString sDeviceExtensions
;
71 OString sDeviceOpenCLVersion
;
73 bool bDeviceAvailable
;
74 bool bDeviceCompilerAvailable
;
75 bool bDeviceLinkerAvailable
;
77 double fTime
; // small time means faster device
78 bool bErrors
; // were there any opencl errors
83 std::vector
<ds_device
> devices
;
86 ds_profile(OString
& inVersion
)
91 inline OString
getPlatformInfoString(cl_platform_id aPlatformId
, cl_platform_info aPlatformInfo
)
93 std::vector
<char> temporary(2048, 0);
94 clGetPlatformInfo(aPlatformId
, aPlatformInfo
, temporary
.size(), temporary
.data(), nullptr);
95 return OString(temporary
.data());
98 inline OString
getDeviceInfoString(cl_device_id aDeviceId
, cl_device_info aDeviceInfo
)
100 std::vector
<char> temporary(2048, 0);
101 clGetDeviceInfo(aDeviceId
, aDeviceInfo
, temporary
.size(), temporary
.data(), nullptr);
102 return OString(temporary
.data());
105 inline OString
getDeviceType(cl_device_id aDeviceId
)
108 cl_device_type aDeviceType
;
109 clGetDeviceInfo(aDeviceId
, CL_DEVICE_TYPE
, sizeof(aDeviceType
), &aDeviceType
, nullptr);
110 if (aDeviceType
& CL_DEVICE_TYPE_CPU
)
112 if (aDeviceType
& CL_DEVICE_TYPE_GPU
)
114 if (aDeviceType
& CL_DEVICE_TYPE_ACCELERATOR
)
115 sType
+= "accelerator ";
116 if (aDeviceType
& CL_DEVICE_TYPE_CUSTOM
)
118 if (aDeviceType
& CL_DEVICE_TYPE_DEFAULT
)
123 inline bool getDeviceInfoBool(cl_device_id aDeviceId
, cl_device_info aDeviceInfo
)
126 clGetDeviceInfo(aDeviceId
, aDeviceInfo
, sizeof(bCLBool
), &bCLBool
, nullptr);
127 return bCLBool
== CL_TRUE
;
130 inline ds_status
initDSProfile(std::unique_ptr
<ds_profile
>& rProfile
, OString rVersion
)
135 cl_uint numPlatforms
;
136 std::vector
<cl_platform_id
> platforms
;
137 std::vector
<cl_device_id
> devices
;
142 rProfile
= std::unique_ptr
<ds_profile
>(new ds_profile(rVersion
));
144 clGetPlatformIDs(0, NULL
, &numPlatforms
);
145 if (numPlatforms
!= 0)
147 platforms
.resize(numPlatforms
);
148 clGetPlatformIDs(numPlatforms
, platforms
.data(), NULL
);
152 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
155 cl_int err
= clGetDeviceIDs(platforms
[i
], CL_DEVICE_TYPE_ALL
, 0, NULL
, &num
);
156 if (err
!= CL_SUCCESS
)
158 /* we want to catch at least the case when the call returns
159 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
160 * don't set num to 0 in this case; but in fact this is a good
161 * thing to do for _any_ error returned by the call
170 devices
.resize(numDevices
);
173 rProfile
->devices
.resize(numDevices
+ 1); // +1 to numDevices to include the native CPU
176 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
181 OString sPlatformProfile
= getPlatformInfoString(platforms
[i
], CL_PLATFORM_PROFILE
);
182 OString sPlatformVersion
= getPlatformInfoString(platforms
[i
], CL_PLATFORM_VERSION
);
183 OString sPlatformName
= getPlatformInfoString(platforms
[i
], CL_PLATFORM_NAME
);
184 OString sPlatformVendor
= getPlatformInfoString(platforms
[i
], CL_PLATFORM_VENDOR
);
185 OString sPlatformExts
= getPlatformInfoString(platforms
[i
], CL_PLATFORM_EXTENSIONS
);
187 cl_int err
= clGetDeviceIDs(platforms
[i
], CL_DEVICE_TYPE_ALL
, numDevices
, devices
.data(), &num
);
188 if (err
!= CL_SUCCESS
)
190 /* we want to catch at least the case when the call returns
191 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
192 * don't set num to 0 in this case; but in fact this is a good
193 * thing to do for _any_ error returned by the call
197 for (j
= 0; j
< num
; j
++, next
++)
199 cl_device_id aDeviceID
= devices
[j
];
201 ds_device
& rDevice
= rProfile
->devices
[next
];
202 rDevice
.eType
= DeviceType::OpenCLDevice
;
203 rDevice
.aDeviceID
= aDeviceID
;
205 rDevice
.sPlatformName
= sPlatformName
;
206 rDevice
.sPlatformVendor
= sPlatformVendor
;
207 rDevice
.sPlatformVersion
= sPlatformVersion
;
208 rDevice
.sPlatformProfile
= sPlatformProfile
;
209 rDevice
.sPlatformExtensions
= sPlatformExts
;
211 rDevice
.sDeviceName
= getDeviceInfoString(aDeviceID
, CL_DEVICE_NAME
);
212 rDevice
.sDeviceVendor
= getDeviceInfoString(aDeviceID
, CL_DEVICE_VENDOR
);
213 rDevice
.sDeviceVersion
= getDeviceInfoString(aDeviceID
, CL_DEVICE_VERSION
);
214 rDevice
.sDriverVersion
= getDeviceInfoString(aDeviceID
, CL_DRIVER_VERSION
);
215 rDevice
.sDeviceType
= getDeviceType(aDeviceID
);
216 rDevice
.sDeviceExtensions
= getDeviceInfoString(aDeviceID
, CL_DEVICE_EXTENSIONS
);
217 rDevice
.sDeviceOpenCLVersion
= getDeviceInfoString(aDeviceID
, CL_DEVICE_OPENCL_C_VERSION
);
219 rDevice
.bDeviceAvailable
= getDeviceInfoBool(aDeviceID
, CL_DEVICE_AVAILABLE
);
220 rDevice
.bDeviceCompilerAvailable
= getDeviceInfoBool(aDeviceID
, CL_DEVICE_COMPILER_AVAILABLE
);
221 rDevice
.bDeviceLinkerAvailable
= getDeviceInfoBool(aDeviceID
, CL_DEVICE_LINKER_AVAILABLE
);
224 rProfile
->devices
[next
].eType
= DeviceType::NativeCPU
;
233 * XmlWriter writes a XML to a SvStream. It uses libxml2 for writing but hides
234 * all the internal libxml2 workings and uses types that are native for LO
237 * The codepage used for XML is always "utf-8" and the output is indented so it
240 * TODO: move to common code
246 xmlTextWriterPtr mpWriter
;
248 static int funcWriteCallback(void* pContext
, const char* sBuffer
, int nLen
)
250 SvStream
* pStream
= static_cast<SvStream
*>(pContext
);
251 return static_cast<int>(pStream
->Write(sBuffer
, nLen
));
254 static int funcCloseCallback(void* pContext
)
256 SvStream
* pStream
= static_cast<SvStream
*>(pContext
);
258 return 0; // 0 or -1 in case of error
263 XmlWriter(SvStream
* pStream
)
271 if (mpWriter
!= nullptr)
277 xmlOutputBufferPtr xmlOutBuffer
= xmlOutputBufferCreateIO(funcWriteCallback
, funcCloseCallback
, mpStream
, nullptr);
278 mpWriter
= xmlNewTextWriter(xmlOutBuffer
);
279 if (mpWriter
== nullptr)
281 xmlTextWriterSetIndent(mpWriter
, 1);
282 xmlTextWriterStartDocument(mpWriter
, nullptr, "UTF-8", nullptr);
288 xmlTextWriterEndDocument(mpWriter
);
289 xmlFreeTextWriter(mpWriter
);
293 void startElement(const OString
& sName
)
295 xmlChar
* xmlName
= xmlCharStrdup(sName
.getStr());
296 xmlTextWriterStartElement(mpWriter
, xmlName
);
302 xmlTextWriterEndElement(mpWriter
);
305 void content(const OString
& sValue
)
307 xmlChar
* xmlValue
= xmlCharStrdup(sValue
.getStr());
308 xmlTextWriterWriteString(mpWriter
, xmlValue
);
314 * XmlWalker main purpose is to make it easier for walking the
315 * parsed XML DOM tree.
317 * It hides all the libxml2 and C -isms and makes the useage more
318 * comfortable from LO developer point of view.
320 * TODO: move to common code
327 xmlNodePtr mpCurrent
;
329 std::vector
<xmlNodePtr
> mpStack
;
340 xmlFreeDoc(mpDocPtr
);
343 bool open(SvStream
* pStream
)
345 sal_Size nSize
= pStream
->remainingSize();
346 std::vector
<sal_uInt8
> aBuffer(nSize
+ 1);
347 pStream
->Read(aBuffer
.data(), nSize
);
349 mpDocPtr
= xmlParseDoc(reinterpret_cast<xmlChar
*>(aBuffer
.data()));
350 if (mpDocPtr
== nullptr)
352 mpRoot
= xmlDocGetRootElement(mpDocPtr
);
354 mpStack
.push_back(mpCurrent
);
360 return OString(reinterpret_cast<const char*>(mpCurrent
->name
));
366 if (mpCurrent
->xmlChildrenNode
!= nullptr)
368 xmlChar
* pContent
= xmlNodeListGetString(mpDocPtr
, mpCurrent
->xmlChildrenNode
, 1);
369 aContent
= OString(reinterpret_cast<const char*>(pContent
));
377 mpStack
.push_back(mpCurrent
);
378 mpCurrent
= mpCurrent
->xmlChildrenNode
;
383 mpCurrent
= mpStack
.back();
389 mpCurrent
= mpCurrent
->next
;
394 return mpCurrent
!= nullptr;
398 } // end anonymous namespace
400 inline ds_status
writeProfile(const OUString
& rStreamName
, std::unique_ptr
<ds_profile
>& pProfile
)
402 if (pProfile
== nullptr)
403 return DS_INVALID_PROFILE
;
404 if (rStreamName
.isEmpty())
405 return DS_INVALID_PROFILE
;
407 std::unique_ptr
<SvStream
> pStream
;
408 pStream
.reset(new SvFileStream(rStreamName
, STREAM_STD_READWRITE
| StreamMode::TRUNC
));
410 XmlWriter
aXmlWriter(pStream
.get());
412 if (!aXmlWriter
.startDocument())
413 return DS_FILE_ERROR
;
415 aXmlWriter
.startElement("profile");
417 aXmlWriter
.startElement("version");
418 aXmlWriter
.content(OString(pProfile
->version
));
419 aXmlWriter
.endElement();
421 for (ds_device
& rDevice
: pProfile
->devices
)
423 aXmlWriter
.startElement("device");
425 switch(rDevice
.eType
)
427 case DeviceType::NativeCPU
:
428 aXmlWriter
.startElement("type");
429 aXmlWriter
.content("native");
430 aXmlWriter
.endElement();
432 case DeviceType::OpenCLDevice
:
433 aXmlWriter
.startElement("type");
434 aXmlWriter
.content("opencl");
435 aXmlWriter
.endElement();
437 aXmlWriter
.startElement("name");
438 aXmlWriter
.content(OString(rDevice
.sDeviceName
));
439 aXmlWriter
.endElement();
441 aXmlWriter
.startElement("driver");
442 aXmlWriter
.content(OString(rDevice
.sDriverVersion
));
443 aXmlWriter
.endElement();
449 aXmlWriter
.startElement("time");
450 if (rtl::math::approxEqual(rDevice
.fTime
, DBL_MAX
))
451 aXmlWriter
.content("max");
453 aXmlWriter
.content(OString::number(rDevice
.fTime
));
454 aXmlWriter
.endElement();
456 aXmlWriter
.startElement("errors");
457 aXmlWriter
.content(rDevice
.bErrors
? "true" : "false");
458 aXmlWriter
.endElement();
460 aXmlWriter
.endElement();
463 aXmlWriter
.endElement();
464 aXmlWriter
.endDocument();
469 inline ds_status
readProfile(const OUString
& rStreamName
, std::unique_ptr
<ds_profile
>& pProfile
)
471 ds_status eStatus
= DS_SUCCESS
;
473 if (rStreamName
.isEmpty())
474 return DS_INVALID_PROFILE
;
476 std::unique_ptr
<SvStream
> pStream
;
477 pStream
.reset(new SvFileStream(rStreamName
, StreamMode::READ
));
480 if (!aWalker
.open(pStream
.get()))
481 return DS_FILE_ERROR
;
483 if (aWalker
.name() == "profile")
486 while (aWalker
.isValid())
488 if (aWalker
.name() == "version")
490 if (aWalker
.content() != pProfile
->version
)
491 return DS_PROFILE_FILE_ERROR
;
493 else if (aWalker
.name() == "device")
497 DeviceType eDeviceType
= DeviceType::None
;
503 while (aWalker
.isValid())
505 if (aWalker
.name() == "type")
507 OString sContent
= aWalker
.content();
508 if (sContent
== "native")
509 eDeviceType
= DeviceType::NativeCPU
;
510 else if (sContent
== "opencl")
511 eDeviceType
= DeviceType::OpenCLDevice
;
513 return DS_PROFILE_FILE_ERROR
;
515 else if (aWalker
.name() == "name")
517 sName
= aWalker
.content();
519 else if (aWalker
.name() == "driver")
521 sVersion
= aWalker
.content();
523 else if (aWalker
.name() == "time")
525 if (aWalker
.content() == "max")
528 fTime
= aWalker
.content().toDouble();
530 else if (aWalker
.name() == "errors")
532 bErrors
= (aWalker
.content() == "true");
539 return DS_PROFILE_FILE_ERROR
;
541 for (ds_device
& rDevice
: pProfile
->devices
)
543 // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
544 if (rDevice
.eType
== eDeviceType
)
546 // is DS_DEVICE_NATIVE_CPU or name + version matches?
547 if (eDeviceType
== DeviceType::NativeCPU
||
548 (sName
== OString(rDevice
.sDeviceName
) &&
549 sVersion
== OString(rDevice
.sDriverVersion
)))
551 rDevice
.fTime
= fTime
;
552 rDevice
.bErrors
= bErrors
;
569 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */