Skip to content

Commit

Permalink
Merge pull request #8579 from elpaso/opencl-2
Browse files Browse the repository at this point in the history
[opencl] Support for platforms > 1.1
  • Loading branch information
elpaso committed Nov 30, 2018
2 parents 0af1ce4 + 13eca56 commit fbcfe0e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 31 deletions.
4 changes: 2 additions & 2 deletions src/analysis/raster/qgsninecellfilter.cpp
Expand Up @@ -215,7 +215,7 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee

// Prepare context and queue
cl::Context ctx = QgsOpenClUtils::context();
cl::CommandQueue queue( ctx );
cl::CommandQueue queue = QgsOpenClUtils::commandQueue();

//keep only three scanlines in memory at a time, make room for initial and final nodata
QgsOpenClUtils::CPLAllocator<float> scanLine( xSize + 2 );
Expand Down Expand Up @@ -245,7 +245,7 @@ int QgsNineCellFilter::processRasterGPU( const QString &source, QgsFeedback *fee
cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, inputSize, nullptr, nullptr );

// Create a program from the kernel source
cl::Program program( QgsOpenClUtils::buildProgram( ctx, source, QgsOpenClUtils::ExceptionBehavior::Throw ) );
cl::Program program( QgsOpenClUtils::buildProgram( source, QgsOpenClUtils::ExceptionBehavior::Throw ) );

// Create the OpenCL kernel
auto kernel = cl::KernelFunctor <
Expand Down
75 changes: 64 additions & 11 deletions src/core/qgsopenclutils.cpp
Expand Up @@ -41,7 +41,7 @@ const std::vector<cl::Device> QgsOpenClUtils::devices()
{
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 )
if ( platver.find( "OpenCL " ) != std::string::npos )
{
std::vector<cl::Device> _devices;
// Check for a device
Expand Down Expand Up @@ -166,6 +166,20 @@ cl::Device QgsOpenClUtils::activeDevice()
return sActiveDevice;
}

QString QgsOpenClUtils::activePlatformVersion()
{
QString version;
if ( QgsOpenClUtils::sDefaultPlatform() )
{
std::string platver = QgsOpenClUtils::sDefaultPlatform.getInfo<CL_PLATFORM_VERSION>();
if ( platver.find( "OpenCL " ) != std::string::npos )
{
version = QString::fromStdString( platver.substr( 7 ) ).split( ' ' ).first();
}
}
return version;
}

void QgsOpenClUtils::storePreferredDevice( const QString deviceId )
{
QgsSettings().setValue( SETTINGS_DEFAULT_DEVICE_KEY, deviceId, QgsSettings::Section::Core );
Expand Down Expand Up @@ -204,7 +218,7 @@ bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
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 )
if ( platver.find( "OpenCL " ) != std::string::npos )
{
std::vector<cl::Device> devices;
// Search for a device
Expand All @@ -231,7 +245,7 @@ bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
{
for ( const auto &_dev : devices )
{
if ( preferredDeviceId.isEmpty() && _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
{
// Got one!
plat = p;
Expand All @@ -246,7 +260,7 @@ bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
{
for ( const auto &_dev : devices )
{
if ( preferredDeviceId.isEmpty() && _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_CPU )
{
// Got one!
plat = p;
Expand All @@ -258,7 +272,7 @@ bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
}
if ( ! deviceFound )
{
QgsMessageLog::logMessage( QObject::tr( "No OpenCL 1.x device could be found." ), LOGMESSAGE_TAG, Qgis::Warning );
QgsMessageLog::logMessage( QObject::tr( "No OpenCL device could be found." ), LOGMESSAGE_TAG, Qgis::Warning );
}
}
catch ( cl::Error &e )
Expand All @@ -273,7 +287,7 @@ bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
}
if ( ! plat() )
{
QgsMessageLog::logMessage( QObject::tr( "No OpenCL 1.x platform found." ), LOGMESSAGE_TAG, Qgis::Warning );
QgsMessageLog::logMessage( QObject::tr( "No OpenCL platform found." ), LOGMESSAGE_TAG, Qgis::Warning );
sAvailable = false;
}
else
Expand Down Expand Up @@ -485,6 +499,27 @@ QString QgsOpenClUtils::errorText( const int errorCode )
}
}

cl::CommandQueue QgsOpenClUtils::commandQueue()
{
// Depending on the platform version, to avoid a crash
// we need to use the legacy calls to C API instead of the 2.0
// compatible C++ API.
cl::Context context( QgsOpenClUtils::context() );
if ( QgsOpenClUtils::activePlatformVersion().toFloat() >= 200 )
{
return cl::CommandQueue( context );
}
else // legacy
{
cl::Device device( QgsOpenClUtils::activeDevice() );
cl_command_queue_properties properties = 0;
Q_NOWARN_DEPRECATED_PUSH
cl_command_queue queue = clCreateCommandQueue( context(), device(), properties, nullptr );
Q_NOWARN_DEPRECATED_POP
return cl::CommandQueue( queue, true );
}
}

cl::Context QgsOpenClUtils::context()
{
static cl::Context context;
Expand All @@ -499,15 +534,33 @@ cl::Context QgsOpenClUtils::context()
return context;
}

cl::Program QgsOpenClUtils::buildProgram( const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior )
cl::Program QgsOpenClUtils::buildProgram( const cl::Context &, const QString &source, ExceptionBehavior exceptionBehavior )
{
// Deprecated: ignore context and use default
return buildProgram( source, exceptionBehavior );
}

cl::Program QgsOpenClUtils::buildProgram( const QString &source, QgsOpenClUtils::ExceptionBehavior exceptionBehavior )
{
cl::Program program;
try
{
program = cl::Program( context, source.toStdString( ) );
// OpenCL 1.1 for compatibility with older hardware
// TODO: make this configurable
program.build( QStringLiteral( "-cl-std=CL1.1 -I%1" ).arg( sourcePath() ).toStdString().c_str() );
program = cl::Program( QgsOpenClUtils::context(), source.toStdString( ) );
// OpenCL version for compatibility with older hardware, but it's up to
// llvm to support latest CL versions
bool ok;
float version( QgsOpenClUtils::activePlatformVersion().toFloat( &ok ) );
if ( ok && version < 2.0f )
{
program.build( QStringLiteral( "-cl-std=CL%1 -I%2" )
.arg( QgsOpenClUtils::activePlatformVersion( ) )
.arg( sourcePath() ).toStdString().c_str() );
}
else
{
program.build( QStringLiteral( "-I%1" )
.arg( sourcePath() ).toStdString().c_str() );
}
}
catch ( cl::BuildError &e )
{
Expand Down
34 changes: 27 additions & 7 deletions src/core/qgsopenclutils.h
Expand Up @@ -19,12 +19,9 @@
#define SIP_NO_FILE

#define CL_HPP_ENABLE_EXCEPTIONS
// Set target version to 1.10 because it's the best compromise
// between features and compatibility with "older" graphics
// cards
#define CL_HPP_MINIMUM_OPENCL_VERSION 110
#define CL_HPP_TARGET_OPENCL_VERSION 110
#define CL_TARGET_OPENCL_VERSION 110
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#define CL_HPP_TARGET_OPENCL_VERSION 220
#define CL_TARGET_OPENCL_VERSION 200
#include <CL/cl2.hpp>

#include "qgis_core.h"
Expand Down Expand Up @@ -134,6 +131,13 @@ class CORE_EXPORT QgsOpenClUtils
*/
static cl::Device activeDevice( );

/**
* Returns the active platform OpenCL version string (e.g. 1.1, 2.0 etc.)
* or a blank string if there is no active platform.
* \since QGIS 3.6
*/
static QString activePlatformVersion( );

//! Store in the settings the preferred \a deviceId device identifier
static void storePreferredDevice( const QString deviceId );

Expand Down Expand Up @@ -171,12 +175,28 @@ class CORE_EXPORT QgsOpenClUtils
//! Returns a string representation from an OpenCL \a errorCode
static QString errorText( const int errorCode );

/**
* Create an OpenCL command queue from the default context.
*
* This wrapper is required in order to prevent a crash when
* running on OpenCL platforms < 2
*/
static cl::CommandQueue commandQueue();

/**
* 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
* \deprecated since QGIS 3.6
*/
Q_DECL_DEPRECATED static cl::Program buildProgram( const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior = Catch );

/**
* Build the program from \a source, depending on \a exceptionBehavior can throw or catch the exceptions
* \return the built program
*/
static cl::Program buildProgram( const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior = Catch );
static cl::Program buildProgram( const QString &source, ExceptionBehavior exceptionBehavior = Catch );


/**
* Context factory
Expand Down
4 changes: 2 additions & 2 deletions src/core/raster/qgshillshaderenderer.cpp
Expand Up @@ -205,7 +205,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext
// Note: output block is not 2px wider and it is an image
// Prepare context and queue
cl::Context ctx = QgsOpenClUtils::context();
cl::CommandQueue queue( ctx );
cl::CommandQueue queue = QgsOpenClUtils::commandQueue();

// Cast to float (because double just crashes on some GPUs)
std::vector<float> rasterParams;
Expand Down Expand Up @@ -250,7 +250,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext
std::call_once( programBuilt, [ = ]()
{
// Create a program from the kernel source
program = QgsOpenClUtils::buildProgram( ctx, source, QgsOpenClUtils::ExceptionBehavior::Throw );
program = QgsOpenClUtils::buildProgram( source, QgsOpenClUtils::ExceptionBehavior::Throw );
} );

// Disable program cache when developing and testing cl program
Expand Down
19 changes: 10 additions & 9 deletions tests/src/core/testqgsopenclutils.cpp
Expand Up @@ -46,6 +46,7 @@ class TestQgsOpenClUtils: public QObject
void testProgramSource();
void testContext();
void testDevices();
void testActiveDeviceVersion();

// For benchmarking performance testing
void testHillshadeCPU();
Expand All @@ -56,13 +57,6 @@ class TestQgsOpenClUtils: public QObject
void _testMakeRunProgram();
void _testMakeHillshade( const int loops );

cl::Program buildProgram( const cl::Context &context, const QString &source )
{
cl::Program program( context, source.toStdString( ) );
program.build( "-cl-std=CL1.1" );
return program;
}

std::string source()
{
std::string pgm = R"CL(
Expand Down Expand Up @@ -149,7 +143,7 @@ void TestQgsOpenClUtils::_testMakeRunProgram()
QVERIFY( err == 0 );

cl::Context ctx = QgsOpenClUtils::context();
cl::CommandQueue queue( ctx );
cl::CommandQueue queue = QgsOpenClUtils::commandQueue();

std::vector<float> a_vec = {1, 10, 100};
std::vector<float> b_vec = {1, 10, 100};
Expand All @@ -158,7 +152,7 @@ void TestQgsOpenClUtils::_testMakeRunProgram()
cl::Buffer b_buf( queue, b_vec.begin(), b_vec.end(), true );
cl::Buffer c_buf( queue, c_vec.begin(), c_vec.end(), false );

cl::Program program = QgsOpenClUtils::buildProgram( ctx, QString::fromStdString( source() ) );
cl::Program program = QgsOpenClUtils::buildProgram( QString::fromStdString( source() ) );

auto kernel =
cl::KernelFunctor <
Expand Down Expand Up @@ -207,6 +201,13 @@ void TestQgsOpenClUtils::testDevices()
qDebug() << QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Type, _devices.at( 0 ) );
}

void TestQgsOpenClUtils::testActiveDeviceVersion()
{
QString version = QgsOpenClUtils::activePlatformVersion();
qDebug() << "OPENCL VERSION" << version;
QVERIFY( version.length() == 3 );
}

void TestQgsOpenClUtils::_testMakeHillshade( const int loops )
{
for ( int i = 0 ; i < loops; i++ )
Expand Down

0 comments on commit fbcfe0e

Please sign in to comment.