summaryrefslogtreecommitdiffstats
path: root/opencl/inc/opencl_device_selection.h
diff options
context:
space:
mode:
Diffstat (limited to 'opencl/inc/opencl_device_selection.h')
-rw-r--r--opencl/inc/opencl_device_selection.h405
1 files changed, 405 insertions, 0 deletions
diff --git a/opencl/inc/opencl_device_selection.h b/opencl/inc/opencl_device_selection.h
new file mode 100644
index 000000000..af0ecd37b
--- /dev/null
+++ b/opencl/inc/opencl_device_selection.h
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
+#define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
+
+#ifdef _MSC_VER
+//#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <memory>
+
+#include <float.h>
+
+#include <clew/clew.h>
+#include <tools/stream.hxx>
+#include <tools/XmlWriter.hxx>
+#include <tools/XmlWalker.hxx>
+#include <rtl/math.hxx>
+
+#include <opencl/OpenCLZone.hxx>
+
+#include <vector>
+
+enum ds_status
+{
+ DS_SUCCESS = 0
+ ,DS_INVALID_PROFILE = 1000
+ ,DS_MEMORY_ERROR
+ , DS_INVALID_PERF_EVALUATOR_TYPE
+ , DS_INVALID_PERF_EVALUATOR
+ , DS_PERF_EVALUATOR_ERROR
+ , DS_FILE_ERROR
+ , DS_UNKNOWN_DEVICE_TYPE
+ , DS_PROFILE_FILE_ERROR
+ , DS_SCORE_SERIALIZER_ERROR
+ , DS_SCORE_DESERIALIZER_ERROR
+};
+
+// device type
+enum class DeviceType
+{
+ None,
+ // NativeCPU means the traditional Calc interpreter code path. (That also includes the so-called
+ // "software interpreter", but note that it definitely does not mean *exclusively* that.)
+ NativeCPU,
+ // OpenCLDevice means an OpenCL device as supplied by an OpenCL platform, which might well be
+ // implemented using code that runs on the CPU (and not a GPU). On Windows, OpenCL platforms
+ // typically provide two devices, one for the GPU and one for the CPU.
+ OpenCLDevice
+};
+
+struct ds_device
+{
+ DeviceType eType;
+ cl_device_id aDeviceID;
+
+ OString sPlatformName;
+ OString sPlatformVendor;
+ OString sPlatformVersion;
+ OString sPlatformProfile;
+ OString sPlatformExtensions;
+
+ OString sDeviceName;
+ OString sDeviceVendor;
+ OString sDeviceVersion;
+ OString sDriverVersion;
+ OString sDeviceType;
+ OString sDeviceExtensions;
+ OString sDeviceOpenCLVersion;
+
+ bool bDeviceAvailable;
+ bool bDeviceCompilerAvailable;
+ bool bDeviceLinkerAvailable;
+
+ double fTime; // small time means faster device
+ bool bErrors; // were there any opencl errors
+};
+
+struct ds_profile
+{
+ std::vector<ds_device> devices;
+ OString version;
+
+ ds_profile(OString const & inVersion)
+ : version(inVersion)
+ {}
+};
+
+inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
+{
+ std::vector<char> temporary(2048, 0);
+ clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
+ return temporary.data();
+}
+
+inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
+{
+ std::vector<char> temporary(2048, 0);
+ clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
+ return temporary.data();
+}
+
+inline OString getDeviceType(cl_device_id aDeviceId)
+{
+ OString sType = "";
+ cl_device_type aDeviceType;
+ clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
+ if (aDeviceType & CL_DEVICE_TYPE_CPU)
+ sType += "cpu ";
+ if (aDeviceType & CL_DEVICE_TYPE_GPU)
+ sType += "gpu ";
+ if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
+ sType += "accelerator ";
+ if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
+ sType += "custom ";
+ if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
+ sType += "default ";
+ return sType;
+}
+
+inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
+{
+ cl_bool bCLBool = 0;
+ // init to false in case clGetDeviceInfo returns CL_INVALID_VALUE when
+ // requesting unsupported (in version 1.0) CL_DEVICE_LINKER_AVAILABLE
+ clGetDeviceInfo(aDeviceId, aDeviceInfo, sizeof(bCLBool), &bCLBool, nullptr);
+ return bCLBool == CL_TRUE;
+}
+
+inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString const & rVersion)
+{
+ OpenCLZone zone;
+
+ int numDevices;
+ cl_uint numPlatforms;
+ std::vector<cl_platform_id> platforms;
+ std::vector<cl_device_id> devices;
+
+ unsigned int next;
+ unsigned int i;
+
+ rProfile.reset(new ds_profile(rVersion));
+
+ clGetPlatformIDs(0, nullptr, &numPlatforms);
+ if (numPlatforms != 0)
+ {
+ platforms.resize(numPlatforms);
+ clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
+ }
+
+ numDevices = 0;
+ for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
+ {
+ cl_uint num = 0;
+ cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &num);
+ if (err != CL_SUCCESS)
+ {
+ /* we want to catch at least the case when the call returns
+ * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
+ * don't set num to 0 in this case; but in fact this is a good
+ * thing to do for _any_ error returned by the call
+ */
+ num = 0;
+ }
+ numDevices += num;
+ }
+
+ if (numDevices != 0)
+ {
+ devices.resize(numDevices);
+ }
+
+ rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
+
+ next = 0;
+ for (i = 0; i < static_cast<unsigned int>(numPlatforms); i++)
+ {
+ cl_uint num = 0;
+ unsigned j;
+
+ OString sPlatformProfile = getPlatformInfoString(platforms[i], CL_PLATFORM_PROFILE);
+ OString sPlatformVersion = getPlatformInfoString(platforms[i], CL_PLATFORM_VERSION);
+ OString sPlatformName = getPlatformInfoString(platforms[i], CL_PLATFORM_NAME);
+ OString sPlatformVendor = getPlatformInfoString(platforms[i], CL_PLATFORM_VENDOR);
+ OString sPlatformExts = getPlatformInfoString(platforms[i], CL_PLATFORM_EXTENSIONS);
+
+ cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
+ if (err != CL_SUCCESS)
+ {
+ /* we want to catch at least the case when the call returns
+ * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
+ * don't set num to 0 in this case; but in fact this is a good
+ * thing to do for _any_ error returned by the call
+ */
+ num = 0;
+ }
+ for (j = 0; j < num; j++, next++)
+ {
+ cl_device_id aDeviceID = devices[j];
+
+ ds_device& rDevice = rProfile->devices[next];
+ rDevice.eType = DeviceType::OpenCLDevice;
+ rDevice.aDeviceID = aDeviceID;
+
+ rDevice.sPlatformName = sPlatformName;
+ rDevice.sPlatformVendor = sPlatformVendor;
+ rDevice.sPlatformVersion = sPlatformVersion;
+ rDevice.sPlatformProfile = sPlatformProfile;
+ rDevice.sPlatformExtensions = sPlatformExts;
+
+ rDevice.sDeviceName = getDeviceInfoString(aDeviceID, CL_DEVICE_NAME);
+ rDevice.sDeviceVendor = getDeviceInfoString(aDeviceID, CL_DEVICE_VENDOR);
+ rDevice.sDeviceVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_VERSION);
+ rDevice.sDriverVersion = getDeviceInfoString(aDeviceID, CL_DRIVER_VERSION);
+ rDevice.sDeviceType = getDeviceType(aDeviceID);
+ rDevice.sDeviceExtensions = getDeviceInfoString(aDeviceID, CL_DEVICE_EXTENSIONS);
+ rDevice.sDeviceOpenCLVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_OPENCL_C_VERSION);
+
+ rDevice.bDeviceAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_AVAILABLE);
+ rDevice.bDeviceCompilerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_COMPILER_AVAILABLE);
+ rDevice.bDeviceLinkerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_LINKER_AVAILABLE);
+ }
+ }
+ rProfile->devices[next].eType = DeviceType::NativeCPU;
+
+ return DS_SUCCESS;
+}
+
+inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
+{
+ if (pProfile == nullptr)
+ return DS_INVALID_PROFILE;
+ if (rStreamName.isEmpty())
+ return DS_INVALID_PROFILE;
+
+ std::unique_ptr<SvStream> pStream;
+ pStream.reset(new SvFileStream(rStreamName, StreamMode::STD_READWRITE | StreamMode::TRUNC));
+
+ tools::XmlWriter aXmlWriter(pStream.get());
+
+ if (!aXmlWriter.startDocument())
+ return DS_FILE_ERROR;
+
+ aXmlWriter.startElement("profile");
+
+ aXmlWriter.startElement("version");
+ aXmlWriter.content(pProfile->version);
+ aXmlWriter.endElement();
+
+ for (const ds_device& rDevice : pProfile->devices)
+ {
+ aXmlWriter.startElement("device");
+
+ switch(rDevice.eType)
+ {
+ case DeviceType::NativeCPU:
+ aXmlWriter.startElement("type");
+ aXmlWriter.content(OString("native"));
+ aXmlWriter.endElement();
+ break;
+ case DeviceType::OpenCLDevice:
+ aXmlWriter.startElement("type");
+ aXmlWriter.content(OString("opencl"));
+ aXmlWriter.endElement();
+
+ aXmlWriter.startElement("name");
+ aXmlWriter.content(rDevice.sDeviceName);
+ aXmlWriter.endElement();
+
+ aXmlWriter.startElement("driver");
+ aXmlWriter.content(rDevice.sDriverVersion);
+ aXmlWriter.endElement();
+ break;
+ default:
+ break;
+ }
+
+ aXmlWriter.startElement("time");
+ if (rtl::math::approxEqual(rDevice.fTime, DBL_MAX))
+ aXmlWriter.content(OString("max"));
+ else
+ aXmlWriter.content(OString::number(rDevice.fTime));
+ aXmlWriter.endElement();
+
+ aXmlWriter.startElement("errors");
+ aXmlWriter.content(rDevice.bErrors ? OString("true") : OString("false"));
+ aXmlWriter.endElement();
+
+ aXmlWriter.endElement();
+ }
+
+ aXmlWriter.endElement();
+ aXmlWriter.endDocument();
+
+ return DS_SUCCESS;
+}
+
+inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile> const & pProfile)
+{
+ ds_status eStatus = DS_SUCCESS;
+
+ if (rStreamName.isEmpty())
+ return DS_INVALID_PROFILE;
+
+ std::unique_ptr<SvStream> pStream;
+ pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
+ tools::XmlWalker aWalker;
+
+ if (!aWalker.open(pStream.get()))
+ return DS_FILE_ERROR;
+
+ if (aWalker.name() == "profile")
+ {
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "version")
+ {
+ if (aWalker.content() != pProfile->version)
+ return DS_PROFILE_FILE_ERROR;
+ }
+ else if (aWalker.name() == "device")
+ {
+ aWalker.children();
+
+ DeviceType eDeviceType = DeviceType::None;
+ OString sName;
+ OString sVersion;
+ double fTime = -1.0;
+ bool bErrors = true;
+
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "type")
+ {
+ OString sContent = aWalker.content();
+ if (sContent == "native")
+ eDeviceType = DeviceType::NativeCPU;
+ else if (sContent == "opencl")
+ eDeviceType = DeviceType::OpenCLDevice;
+ else
+ return DS_PROFILE_FILE_ERROR;
+ }
+ else if (aWalker.name() == "name")
+ {
+ sName = aWalker.content();
+ }
+ else if (aWalker.name() == "driver")
+ {
+ sVersion = aWalker.content();
+ }
+ else if (aWalker.name() == "time")
+ {
+ if (aWalker.content() == "max")
+ fTime = DBL_MAX;
+ else
+ fTime = aWalker.content().toDouble();
+ }
+ else if (aWalker.name() == "errors")
+ {
+ bErrors = (aWalker.content() == "true");
+ }
+
+ aWalker.next();
+ }
+
+ if (fTime < 0.0)
+ return DS_PROFILE_FILE_ERROR;
+
+ for (ds_device& rDevice : pProfile->devices)
+ {
+ // type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
+ if (rDevice.eType == eDeviceType)
+ {
+ // is DS_DEVICE_NATIVE_CPU or name + version matches?
+ if (eDeviceType == DeviceType::NativeCPU ||
+ (sName == rDevice.sDeviceName &&
+ sVersion == rDevice.sDriverVersion))
+ {
+ rDevice.fTime = fTime;
+ rDevice.bErrors = bErrors;
+ }
+ }
+ }
+
+ aWalker.parent();
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+ }
+
+ return eStatus;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */