office-gobmx/opencl/inc/opencl_device_selection.h
Stephan Bergmann 235ae98ae6 Extended loplugin:ostr: opencl
Change-Id: I126ed9e9fca6d1ab8d4e42ec0fe1ffb33a5f8ae8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159689
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2023-11-19 22:35:12 +01:00

406 lines
13 KiB
C++

/* -*- 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 <utility>
#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 inVersion)
: version(std::move(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 = ""_ostr;
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("native"_ostr);
aXmlWriter.endElement();
break;
case DeviceType::OpenCLDevice:
aXmlWriter.startElement("type");
aXmlWriter.content("opencl"_ostr);
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("max"_ostr);
else
aXmlWriter.content(OString::number(rDevice.fTime));
aXmlWriter.endElement();
aXmlWriter.startElement("errors");
aXmlWriter.content(rDevice.bErrors ? "true"_ostr : "false"_ostr);
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: */