Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / opencl / inc / opencl_device_selection.h
bloba796a55caf346cf350ae0572257443a1db839b1c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11 #define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
13 #ifdef _MSC_VER
14 #define _CRT_SECURE_NO_WARNINGS
15 #endif
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
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>
29 #include <vector>
31 enum ds_status
33 DS_SUCCESS = 0
34 ,DS_INVALID_PROFILE = 1000
35 ,DS_MEMORY_ERROR
36 , DS_INVALID_PERF_EVALUATOR_TYPE
37 , DS_INVALID_PERF_EVALUATOR
38 , DS_PERF_EVALUATOR_ERROR
39 , DS_FILE_ERROR
40 , DS_UNKNOWN_DEVICE_TYPE
41 , DS_PROFILE_FILE_ERROR
42 , DS_SCORE_SERIALIZER_ERROR
43 , DS_SCORE_DESERIALIZER_ERROR
46 // device type
47 enum class DeviceType
49 None,
50 NativeCPU,
51 OpenCLDevice
54 struct ds_device
56 DeviceType eType;
57 cl_device_id aDeviceID;
59 OString sPlatformName;
60 OString sPlatformVendor;
61 OString sPlatformVersion;
62 OString sPlatformProfile;
63 OString sPlatformExtensions;
65 OString sDeviceName;
66 OString sDeviceVendor;
67 OString sDeviceVersion;
68 OString sDriverVersion;
69 OString sDeviceType;
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
81 struct ds_profile
83 std::vector<ds_device> devices;
84 OString version;
86 ds_profile(OString& inVersion)
87 : version(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)
107 OString sType = "";
108 cl_device_type aDeviceType;
109 clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
110 if (aDeviceType & CL_DEVICE_TYPE_CPU)
111 sType += "cpu ";
112 if (aDeviceType & CL_DEVICE_TYPE_GPU)
113 sType += "gpu ";
114 if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
115 sType += "accelerator ";
116 if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
117 sType += "custom ";
118 if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
119 sType += "default ";
120 return sType;
123 inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
125 cl_bool bCLBool;
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)
132 OpenCLZone zone;
134 int numDevices;
135 cl_uint numPlatforms;
136 std::vector<cl_platform_id> platforms;
137 std::vector<cl_device_id> devices;
139 unsigned int next;
140 unsigned int i;
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);
151 numDevices = 0;
152 for (i = 0; i < (unsigned int)numPlatforms; i++)
154 cl_uint num = 0;
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
163 num = 0;
165 numDevices += num;
168 if (numDevices != 0)
170 devices.resize(numDevices);
173 rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
175 next = 0;
176 for (i = 0; i < (unsigned int)numPlatforms; i++)
178 cl_uint num = 0;
179 unsigned j;
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
195 num = 0;
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;
226 return DS_SUCCESS;
229 namespace
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
235 * development.
237 * The codepage used for XML is always "utf-8" and the output is indented so it
238 * is easier to read.
240 * TODO: move to common code
242 class XmlWriter
244 private:
245 SvStream* mpStream;
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);
257 pStream->Flush();
258 return 0; // 0 or -1 in case of error
261 public:
263 XmlWriter(SvStream* pStream)
264 : mpStream(pStream)
265 , mpWriter(nullptr)
269 ~XmlWriter()
271 if (mpWriter != nullptr)
272 endDocument();
275 bool startDocument()
277 xmlOutputBufferPtr xmlOutBuffer = xmlOutputBufferCreateIO(funcWriteCallback, funcCloseCallback, mpStream, nullptr);
278 mpWriter = xmlNewTextWriter(xmlOutBuffer);
279 if (mpWriter == nullptr)
280 return false;
281 xmlTextWriterSetIndent(mpWriter, 1);
282 xmlTextWriterStartDocument(mpWriter, nullptr, "UTF-8", nullptr);
283 return true;
286 void endDocument()
288 xmlTextWriterEndDocument(mpWriter);
289 xmlFreeTextWriter(mpWriter);
290 mpWriter = nullptr;
293 void startElement(const OString& sName)
295 xmlChar* xmlName = xmlCharStrdup(sName.getStr());
296 xmlTextWriterStartElement(mpWriter, xmlName);
297 xmlFree(xmlName);
300 void endElement()
302 xmlTextWriterEndElement(mpWriter);
305 void content(const OString& sValue)
307 xmlChar* xmlValue = xmlCharStrdup(sValue.getStr());
308 xmlTextWriterWriteString(mpWriter, xmlValue);
309 xmlFree(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
322 class XmlWalker
324 private:
325 xmlDocPtr mpDocPtr;
326 xmlNodePtr mpRoot;
327 xmlNodePtr mpCurrent;
329 std::vector<xmlNodePtr> mpStack;
331 public:
332 XmlWalker()
333 : mpDocPtr(nullptr)
334 , mpRoot(nullptr)
335 , mpCurrent(nullptr)
338 ~XmlWalker()
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);
348 aBuffer[nSize] = 0;
349 mpDocPtr = xmlParseDoc(reinterpret_cast<xmlChar*>(aBuffer.data()));
350 if (mpDocPtr == nullptr)
351 return false;
352 mpRoot = xmlDocGetRootElement(mpDocPtr);
353 mpCurrent = mpRoot;
354 mpStack.push_back(mpCurrent);
355 return true;
358 OString name()
360 return OString(reinterpret_cast<const char*>(mpCurrent->name));
363 OString content()
365 OString aContent;
366 if (mpCurrent->xmlChildrenNode != nullptr)
368 xmlChar* pContent = xmlNodeListGetString(mpDocPtr, mpCurrent->xmlChildrenNode, 1);
369 aContent = OString(reinterpret_cast<const char*>(pContent));
370 xmlFree(pContent);
372 return aContent;
375 void children()
377 mpStack.push_back(mpCurrent);
378 mpCurrent = mpCurrent->xmlChildrenNode;
381 void parent()
383 mpCurrent = mpStack.back();
384 mpStack.pop_back();
387 void next()
389 mpCurrent = mpCurrent->next;
392 bool isValid()
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();
431 break;
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();
444 break;
445 default:
446 break;
449 aXmlWriter.startElement("time");
450 if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
451 aXmlWriter.content("max");
452 else
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();
466 return DS_SUCCESS;
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));
478 XmlWalker aWalker;
480 if (!aWalker.open(pStream.get()))
481 return DS_FILE_ERROR;
483 if (aWalker.name() == "profile")
485 aWalker.children();
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")
495 aWalker.children();
497 DeviceType eDeviceType = DeviceType::None;
498 OString sName;
499 OString sVersion;
500 double fTime = -1.0;
501 bool bErrors = true;
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;
512 else
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")
526 fTime = DBL_MAX;
527 else
528 fTime = aWalker.content().toDouble();
530 else if (aWalker.name() == "errors")
532 bErrors = (aWalker.content() == "true");
535 aWalker.next();
538 if (fTime < 0.0)
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;
557 aWalker.parent();
559 aWalker.next();
561 aWalker.parent();
564 return eStatus;
567 #endif
569 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */