[opencl] Class docs improvements

Also try hard to find a device before giving up
This commit is contained in:
Alessandro Pasotti 2018-07-21 19:20:00 +02:00
parent 6956436804
commit 86b60c6c63
2 changed files with 142 additions and 70 deletions

View File

@ -32,12 +32,10 @@ cl::Device QgsOpenClUtils::sActiveDevice = cl::Device();
QString QgsOpenClUtils::sSourcePath = QString();
std::vector<cl::Device> QgsOpenClUtils::devices()
const std::vector<cl::Device> QgsOpenClUtils::devices()
{
std::vector<cl::Platform> platforms;
cl::Platform::get( &platforms );
cl::Platform plat;
cl::Device dev;
std::vector<cl::Device> existingDevices;
for ( auto &p : platforms )
{
@ -91,7 +89,7 @@ void QgsOpenClUtils::init()
QString QgsOpenClUtils::sourcePath()
{
return sSourcePath.isEmpty() ? QStringLiteral( "./" ) : sSourcePath;
return sSourcePath;
}
void QgsOpenClUtils::setSourcePath( const QString &value )
@ -99,7 +97,7 @@ void QgsOpenClUtils::setSourcePath( const QString &value )
sSourcePath = value;
}
QString QgsOpenClUtils::defaultDeviceInfo( const QgsOpenClUtils::Info infoType )
QString QgsOpenClUtils::activeDeviceInfo( const QgsOpenClUtils::Info infoType )
{
return deviceInfo( infoType, activeDevice( ) );
}
@ -126,7 +124,7 @@ QString QgsOpenClUtils::deviceInfo( const Info infoType, cl::Device device )
return QString::number( device.getInfo<CL_DEVICE_IMAGE2D_MAX_WIDTH>() );
case Info::Type:
{
int type( device.getInfo<CL_DEVICE_TYPE>() );
unsigned long type( device.getInfo<CL_DEVICE_TYPE>() );
int mappedType;
switch ( type )
{
@ -143,7 +141,6 @@ QString QgsOpenClUtils::deviceInfo( const Info infoType, cl::Device device )
return metaEnum.valueToKey( mappedType );
}
case Info::Name:
default:
return QString::fromStdString( device.getInfo<CL_DEVICE_NAME>() );
}
}
@ -168,11 +165,6 @@ cl::Device QgsOpenClUtils::activeDevice()
return sActiveDevice;
}
void QgsOpenClUtils::setActiveDevice( const cl::Device device )
{
sActiveDevice = device;
}
void QgsOpenClUtils::storePreferredDevice( const QString deviceId )
{
QgsSettings().setValue( SETTINGS_DEFAULT_DEVICE_KEY, deviceId, QgsSettings::Section::Core );
@ -192,7 +184,7 @@ QString QgsOpenClUtils::deviceId( const cl::Device device )
.arg( deviceInfo( QgsOpenClUtils::Info::Type, device ) );
}
bool QgsOpenClUtils::activate( const QString preferredDeviceId )
bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
{
if ( deviceId( activeDevice() ) == preferredDeviceId )
{
@ -204,8 +196,11 @@ bool QgsOpenClUtils::activate( const QString preferredDeviceId )
cl::Platform::get( &platforms );
cl::Platform plat;
cl::Device dev;
bool deviceFound = false;
for ( auto &p : platforms )
{
if ( deviceFound )
break;
std::string platver = p.getInfo<CL_PLATFORM_VERSION>();
QgsDebugMsg( QStringLiteral( "Found OpenCL platform %1: %2" ).arg( QString::fromStdString( platver ), QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ) );
if ( platver.find( "OpenCL 1." ) != std::string::npos )
@ -215,30 +210,69 @@ bool QgsOpenClUtils::activate( const QString preferredDeviceId )
try
{
p.getDevices( CL_DEVICE_TYPE_ALL, &devices );
// First search for the preferred device
if ( ! preferredDeviceId.isEmpty() )
{
for ( const auto &_dev : devices )
{
if ( preferredDeviceId == deviceId( _dev ) )
{
// Got one!
plat = p;
dev = _dev;
deviceFound = true;
break;
}
}
}
// Not found or preferred device id not set: get the first GPU
if ( ! deviceFound )
{
for ( const auto &_dev : devices )
{
if ( preferredDeviceId.isEmpty() && _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
{
// Got one!
plat = p;
dev = _dev;
deviceFound = true;
break;
}
}
}
// Still nothing? Get the first device
if ( ! deviceFound )
{
for ( const auto &_dev : devices )
{
if ( preferredDeviceId.isEmpty() && _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
{
// Got one!
plat = p;
dev = _dev;
deviceFound = true;
break;
}
}
}
if ( ! deviceFound )
{
QgsMessageLog::logMessage( QObject::tr( "No OpenCL 1.x device could be found." ), LOGMESSAGE_TAG, Qgis::Warning );
}
}
catch ( cl::Error &e )
{
QgsDebugMsgLevel( QStringLiteral( "Error %1 on platform %3 searching for OpenCL device: %2" )
.arg( errorText( e.err() ),
QString::fromStdString( e.what() ),
QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ), 2 );
}
for ( const auto &_dev : devices )
{
if ( ( preferredDeviceId.isEmpty() && _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU ) ||
( preferredDeviceId == deviceId( _dev ) ) )
{
// Got one!
plat = p;
dev = _dev;
break;
}
QgsDebugMsg( QStringLiteral( "Error %1 on platform %3 searching for OpenCL device: %2" )
.arg( errorText( e.err() ),
QString::fromStdString( e.what() ),
QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ) );
}
}
}
if ( plat() == 0 )
if ( ! plat() )
{
QgsMessageLog::logMessage( QObject::tr( "No OpenCL 1.x platform with GPU found." ), LOGMESSAGE_TAG, Qgis::Warning );
QgsMessageLog::logMessage( QObject::tr( "No OpenCL 1.x platform found." ), LOGMESSAGE_TAG, Qgis::Warning );
sAvailable = false;
}
else
@ -253,7 +287,7 @@ bool QgsOpenClUtils::activate( const QString preferredDeviceId )
else
{
cl::Device::setDefault( dev );
QgsMessageLog::logMessage( QObject::tr( "Activated OpenCL device %1" )
QgsMessageLog::logMessage( QObject::tr( "Active OpenCL device: %1" )
.arg( QString::fromStdString( dev.getInfo<CL_DEVICE_NAME>() ) ),
LOGMESSAGE_TAG, Qgis::Success );
sAvailable = true;
@ -487,7 +521,7 @@ cl::Program QgsOpenClUtils::buildProgram( const cl::Context &context, const QStr
}
catch ( cl::Error &e )
{
QString err = QObject::tr( "Error %1 running OpenCL program in %2" )
QString err = QObject::tr( "Error %1 building OpenCL program in %2" )
.arg( errorText( e.err() ), QString::fromStdString( e.what() ) );
QgsMessageLog::logMessage( err, LOGMESSAGE_TAG, Qgis::Critical );
throw e;

View File

@ -34,12 +34,30 @@
* \brief The QgsOpenClUtils class is responsible for common OpenCL operations such as
* - enable/disable opencl
* - store and retrieve preferences for the default device
* - check opencl device availability and automatically choose the first GPU (TODO: let the user choose & override!)
* - creating contexts
* - check opencl device availability and automatically choose the first GPU device
* - creating the default context
* - loading program sources from standard locations
* - build programs and log errors
* \since QGIS 3.4
*
* Usage:
*
* \code{.cpp}
// This will check if OpenCL is enabled in user options and if there is a suitable
// device, if a device is found it is initialized.
if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() )
{
// Use the default context
cl::Context ctx = QgsOpenClUtils::context();
cl::CommandQueue queue( ctx );
// Load the program from a standard location and build it
cl::Program program = QgsOpenClUtils::buildProgram( ctx, QgsOpenClUtils::sourceFromBaseName( QStringLiteral ( "hillshade" ) ) );
// Continue with the usual OpenCL buffer, kernel and execution
...
}
* \endcode
*
* \note not available in Python bindings
* \since QGIS 3.4
*/
class CORE_EXPORT QgsOpenClUtils
{
@ -52,8 +70,8 @@ class CORE_EXPORT QgsOpenClUtils
*/
enum ExceptionBehavior
{
Catch, // Write errors in the message log and silently fail
Throw // Write errors in the message log and re-throw exceptions
Catch, //! Write errors in the message log and silently fail
Throw //! Write errors in the message log and re-throw exceptions
};
/**
@ -86,18 +104,32 @@ class CORE_EXPORT QgsOpenClUtils
Type = CL_DEVICE_TYPE // CPU/GPU etc.
};
//! Return true if OpenCL is enabled in the user settings
/**
* Checks whether a suitable OpenCL platform and device is available on this system
* and initialize the QGIS OpenCL system by activating the preferred device
* if specified in the user the settings, if no preferred device was set or
* the preferred device could not be found the first GPU device is activated,
* the first CPU device acts as a fallback if none of the previous could be found.
*
* This function must always be called before using QGIS OpenCL utils
*/
static bool available();
//! Returns true if OpenCL is enabled in the user settings
static bool enabled();
//! Return a list of OpenCL devices found on this sysytem
static std::vector<cl::Device> devices();
//! Returns a list of OpenCL devices found on this sysytem
static const std::vector<cl::Device> devices();
//! Return the active device
/**
* Returns the active device.
*
* The active device is set as the default device for all OpenCL operations,
* once it is set it cannot be changed until QGIS is restarted (this is
* due to the way the underlying OpenCL library is built).
*/
static cl::Device activeDevice( );
//! Set the active \a device
static void setActiveDevice( const cl::Device device );
//! Store in the settings the preferred \a deviceId device identifier
static void storePreferredDevice( const QString deviceId );
@ -107,15 +139,6 @@ class CORE_EXPORT QgsOpenClUtils
//! Create a string identifier from a \a device
static QString deviceId( const cl::Device device );
/**
* Activate a device identified by its \a preferredDeviceId by making it the default device
* if the device does not exists or deviceId is empty, the first GPU device will be
* activated
* \return true if the device could be found and activated. Return false if the device was already
* the active one or if it could not be found.
*/
static bool activate( const QString preferredDeviceId = QString() );
/**
* Returns a formatted description for the \a device
*/
@ -126,12 +149,6 @@ class CORE_EXPORT QgsOpenClUtils
*/
static QString deviceDescription( const QString deviceId );
/**
* Checks whether a suitable OpenCL platform and device is found on this system and initialize the QGIS OpenCL system
* This function must be called before using QGIS OpenCL utils
*/
static bool available();
//! Set the OpenCL user setting to \a enabled
static void setEnabled( bool enabled );
@ -141,17 +158,17 @@ class CORE_EXPORT QgsOpenClUtils
//! Read an OpenCL source file from \a path
static QString sourceFromPath( const QString &path );
//! Return the full path to a an OpenCL source file from the \a baseName ('.cl' extension is automatically appended)
//! Returns the full path to a an OpenCL source file from the \a baseName ('.cl' extension is automatically appended)
static QString sourceFromBaseName( const QString &baseName );
//! OpenCL string for message logs
static QLatin1String LOGMESSAGE_TAG;
//! Return a string representation from an OpenCL \a errorCode
//! Returns a string representation from an OpenCL \a errorCode
static QString errorText( const int errorCode );
/**
* Build the program from \a source in the given \a context and depending on \a exeptionBehavior
* Build the program from \a source in the given \a context and depending on \a exceptionBehavior
* can throw or catch the exceptions
* \return the built program
*/
@ -166,16 +183,16 @@ class CORE_EXPORT QgsOpenClUtils
*/
static cl::Context context();
//! Return the base path to OpenCL program directory
//! Returns the base path to OpenCL program directory
static QString sourcePath();
//! Set the base path to OpenCL program directory
static void setSourcePath( const QString &value );
//! Return \a infoType information about the default device
static QString defaultDeviceInfo( const Info infoType = Info::Name );
//! Returns \a infoType information about the active (default) device
static QString activeDeviceInfo( const Info infoType = Info::Name );
//! Return \a infoType information about the \a device
//! Returns \a infoType information about the \a device
static QString deviceInfo( const Info infoType, cl::Device device );
/**
@ -188,23 +205,23 @@ class CORE_EXPORT QgsOpenClUtils
public:
explicit CPLAllocator( unsigned long size ): mMem( ( T * )CPLMalloc( sizeof( T ) * size ) ) { }
explicit CPLAllocator( unsigned long size ): mMem( static_cast<T *>( CPLMalloc( sizeof( T ) * size ) ) ) { }
~CPLAllocator()
{
CPLFree( ( void * )mMem );
CPLFree( static_cast<void *>( mMem ) );
}
void reset( T *newData )
{
if ( mMem )
CPLFree( ( void * )mMem );
CPLFree( static_cast<void *>( mMem ) );
mMem = newData;
}
void reset( unsigned long size )
{
reset( ( T * )CPLMalloc( sizeof( T ) *size ) );
reset( static_cast<T *>( CPLMalloc( sizeof( T ) *size ) ) );
}
T &operator* ()
@ -236,8 +253,29 @@ class CORE_EXPORT QgsOpenClUtils
private:
QgsOpenClUtils();
/**
* Activate a device identified by its \a preferredDeviceId by making it the default device
* if the device does not exists or deviceId is empty, the first GPU device will be
* activated, if a GPU device is not found, the first CPU device will be chosen instead.
*
* Called once by init() when OpenCL is used for the first time in a QGIS working session.
*
* \return true if the device could be found and activated. Return false if the device was already
* the active one or if a device could not be activated.
*
* \see init()
* \see available()
*/
static bool activate( const QString &preferredDeviceId = QString() );
/**
* Initialize the OpenCL system by setting and activating the default device.
*/
static void init();
static bool sAvailable;
static cl::Device sActiveDevice;
static cl::Platform sDefaultPlatform;