28#include "moc_qgsopenclutils.cpp"
30using namespace Qt::StringLiterals;
33#if defined(UNICODE) && !defined(_UNICODE)
45QLatin1String QgsOpenClUtils::SETTINGS_GLOBAL_ENABLED_KEY =
"OpenClEnabled"_L1;
46QLatin1String QgsOpenClUtils::SETTINGS_DEFAULT_DEVICE_KEY =
"OpenClDefaultDevice"_L1;
48bool QgsOpenClUtils::sAvailable =
false;
53const std::vector<cl::Device> QgsOpenClUtils::
devices()
55 std::vector<cl::Platform> platforms;
56 cl::Platform::get( &platforms );
57 std::vector<cl::Device> existingDevices;
58 for (
const auto &p : platforms )
60 const std::string platver = p.getInfo<CL_PLATFORM_VERSION>();
62 .arg( QString::fromStdString( platver ),
63 QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ),
65 if ( platver.find(
"OpenCL " ) != std::string::npos )
67 std::vector<cl::Device> _devices;
71 p.getDevices( CL_DEVICE_TYPE_ALL, &_devices );
73 catch ( cl::Error &e )
77 QString::fromStdString( e.what() ),
78 QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ),
81 if ( _devices.size() > 0 )
83 for (
unsigned long i = 0; i < _devices.size(); i++ )
88 existingDevices.push_back( _devices[i] );
93 return existingDevices;
96void QgsOpenClUtils::init()
98 static std::once_flag initialized;
99 std::call_once( initialized, []( )
102 QLibrary openCLLib { u
"/System/Library/Frameworks/OpenCL.framework/Versions/Current/OpenCL"_s };
104 QLibrary openCLLib { u
"OpenCL"_s };
106 openCLLib.setLoadHints( QLibrary::LoadHint::ResolveAllSymbolsHint );
107 if ( ! openCLLib.load() )
110 .arg( openCLLib.errorString() ),
121 HMODULE hModule = GetModuleHandle( _T(
"OpenCL.dll" ) );
124 TCHAR pszFileName[1024];
125 if ( GetModuleFileName( hModule, pszFileName, 1024 ) < 1024 )
132 DWORD dwLen = GetFileVersionInfoSize( pszFileName, &dwUseless );
135 LPTSTR lpVI = ( LPTSTR ) malloc( dwLen *
sizeof( TCHAR ) );
138 if ( GetFileVersionInfo( pszFileName, 0, dwLen, lpVI ) )
140 VS_FIXEDFILEINFO *lpFFI;
141 if ( VerQueryValue( lpVI, _T(
"\\" ), ( LPVOID * ) &lpFFI, ( UINT * ) &dwUseless ) )
144 .arg( lpFFI->dwProductVersionMS >> 16 )
145 .arg( lpFFI->dwProductVersionMS & 0xffff )
146 .arg( lpFFI->dwProductVersionLS >> 16 )
147 .arg( lpFFI->dwProductVersionLS & 0xffff ),
151 struct LANGANDCODEPAGE
159 if ( VerQueryValue( lpVI, _T(
"\\VarFileInfo\\Translation" ), ( LPVOID * ) &lpTranslate, ( UINT * ) &cbTranslate ) && cbTranslate >=
sizeof(
struct LANGANDCODEPAGE ) )
161 QStringList items = QStringList()
166 << u
"LegalCopyright"_s
167 << u
"ProductVersion"_s
168 << u
"FileDescription"_s
169 << u
"LegalTrademarks"_s
172 << u
"OriginalFilename"_s
173 << u
"SpecialBuild"_s;
174 for (
auto d : items )
177 QString subBlock = QString( u
"\\StringFileInfo\\%1%2\\%3"_s )
178 .arg( lpTranslate[0].wLanguage, 4, 16,
'0'_L1 )
179 .arg( lpTranslate[0].wCodePage, 4, 16,
'0'_L1 )
182 QgsDebugMsgLevel( QString(
"d:%1 subBlock:%2" ).arg( d ).arg( subBlock ), 2 );
184 BOOL r = VerQueryValue( lpVI,
186 subBlock.toStdWString().c_str(),
190 ( LPVOID * )&lpBuffer, ( UINT * )&dwUseless );
192 if ( r && lpBuffer && lpBuffer != INVALID_HANDLE_VALUE && dwUseless < 1023 )
197 .arg( QString::fromUtf16( (
const ushort * ) lpBuffer ) ),
199 .arg( QString::fromLocal8Bit( lpBuffer ) ),
226 catch ( cl::Error &e )
229 .arg(
errorText( e.err() ), QString::fromStdString( e.what() ) ),
238 return *sSourcePath();
243 *sSourcePath() = value;
258 return QString::fromStdString( device.getInfo<CL_DEVICE_VENDOR>() );
260 return QString::fromStdString( device.getInfo<CL_DEVICE_PROFILE>() );
262 return QString::fromStdString( device.getInfo<CL_DEVICE_VERSION>() );
264 return device.getInfo<CL_DEVICE_IMAGE_SUPPORT>() ? u
"True"_s : u
"False"_s;
266 return QString::number( device.getInfo<CL_DEVICE_IMAGE2D_MAX_HEIGHT>() );
268 return QString::number( device.getInfo<CL_DEVICE_MAX_MEM_ALLOC_SIZE>() );
270 return QString::number( device.getInfo<CL_DEVICE_IMAGE2D_MAX_WIDTH>() );
273 const unsigned long type( device.getInfo<CL_DEVICE_TYPE>() );
277 case CL_DEVICE_TYPE_CPU:
280 case CL_DEVICE_TYPE_GPU:
286 const QMetaEnum metaEnum = QMetaEnum::fromType<QgsOpenClUtils::HardwareType>();
287 return metaEnum.valueToKey( mappedType );
290 return QString::fromStdString( device.getInfo<CL_DEVICE_NAME>() );
293 catch ( cl::Error &e )
297 .arg(
errorText( e.err() ), QString::fromStdString( e.what() ) ),
312 return cl::Device::getDefault();
318 if ( cl::Platform::getDefault()() )
320 const std::string platver = cl::Platform::getDefault().getInfo<CL_PLATFORM_VERSION>();
321 if ( platver.find(
"OpenCL " ) != std::string::npos )
323 version = QString::fromStdString( platver.substr( 7 ) ).split(
' ' ).first();
341 return u
"%1|%2|%3|%4"_s
349static void emitLogMessageForSEHException(
int exceptionCode )
351 QgsMessageLog::logMessage( QObject::tr(
"Unexpected exception of code %1 occurred while searching for OpenCL device. Note that the application may become unreliable and may need to be restarted." ).arg( exceptionCode ),
356bool QgsOpenClUtils::activate(
const QString &preferredDeviceId )
363 return activateInternal( preferredDeviceId );
365 __except ( EXCEPTION_EXECUTE_HANDLER )
367 emitLogMessageForSEHException( GetExceptionCode() );
371 return activateInternal( preferredDeviceId );
375bool QgsOpenClUtils::activateInternal(
const QString &preferredDeviceId )
385 std::vector<cl::Platform> platforms;
386 cl::Platform::get( &platforms );
389 bool deviceFound =
false;
390 for (
const auto &p : platforms )
394 const std::string platver = p.getInfo<CL_PLATFORM_VERSION>();
395 QgsDebugMsgLevel( u
"Found OpenCL platform %1: %2"_s.arg( QString::fromStdString( platver ), QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ), 2 );
396 if ( platver.find(
"OpenCL " ) != std::string::npos )
398 std::vector<cl::Device>
devices;
402 p.getDevices( CL_DEVICE_TYPE_ALL, &
devices );
404 if ( ! preferredDeviceId.isEmpty() )
406 for (
const auto &_dev :
devices )
408 if ( preferredDeviceId ==
deviceId( _dev ) )
421 for (
const auto &_dev :
devices )
423 if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
436 for (
const auto &_dev :
devices )
438 if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_CPU )
453 catch ( cl::Error &e )
455 QgsDebugError( u
"Error %1 on platform %3 searching for OpenCL device: %2"_s
457 QString::fromStdString( e.what() ),
458 QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ) );
470 const cl::Platform newP = cl::Platform::setDefault( plat );
479 cl::Device::setDefault( dev );
481 .arg( QString::fromStdString( dev.getInfo<CL_DEVICE_NAME>() ) ),
488 catch ( cl::Error &e )
491 .arg(
errorText( e.err() ), QString::fromStdString( e.what() ) ),
501 return QStringLiteral(
502 "Type: <b>%9</b><br>"
503 "Name: <b>%1</b><br>"
504 "Vendor: <b>%2</b><br>"
505 "Profile: <b>%3</b><br>"
506 "Version: <b>%4</b><br>"
507 "Image support: <b>%5</b><br>"
508 "Max image2d width: <b>%6</b><br>"
509 "Max image2d height: <b>%7</b><br>"
510 "Max mem alloc size: <b>%8</b><br>"
524 for (
const auto &dev :
devices( ) )
551 if ( file.open( QFile::ReadOnly | QFile::Text ) )
553 QTextStream in( &file );
554 source_str = in.readAll();
566 const QString path = u
"%1/%2.cl"_s.arg(
sourcePath(), baseName );
572 cl::BuildLogType build_logs = error.getBuildLog();
574 if ( build_logs.size() > 0 )
575 build_log = QString::fromStdString( build_logs[0].second );
583 case 0:
return u
"CL_SUCCESS"_s;
584 case -1:
return u
"CL_DEVICE_NOT_FOUND"_s;
585 case -2:
return u
"CL_DEVICE_NOT_AVAILABLE"_s;
586 case -3:
return u
"CL_COMPILER_NOT_AVAILABLE"_s;
587 case -4:
return u
"CL_MEM_OBJECT_ALLOCATION_FAILURE"_s;
588 case -5:
return u
"CL_OUT_OF_RESOURCES"_s;
589 case -6:
return u
"CL_OUT_OF_HOST_MEMORY"_s;
590 case -7:
return u
"CL_PROFILING_INFO_NOT_AVAILABLE"_s;
591 case -8:
return u
"CL_MEM_COPY_OVERLAP"_s;
592 case -9:
return u
"CL_IMAGE_FORMAT_MISMATCH"_s;
593 case -10:
return u
"CL_IMAGE_FORMAT_NOT_SUPPORTED"_s;
594 case -12:
return u
"CL_MAP_FAILURE"_s;
595 case -13:
return u
"CL_MISALIGNED_SUB_BUFFER_OFFSET"_s;
596 case -14:
return u
"CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST"_s;
597 case -15:
return u
"CL_COMPILE_PROGRAM_FAILURE"_s;
598 case -16:
return u
"CL_LINKER_NOT_AVAILABLE"_s;
599 case -17:
return u
"CL_LINK_PROGRAM_FAILURE"_s;
600 case -18:
return u
"CL_DEVICE_PARTITION_FAILED"_s;
601 case -19:
return u
"CL_KERNEL_ARG_INFO_NOT_AVAILABLE"_s;
602 case -30:
return u
"CL_INVALID_VALUE"_s;
603 case -31:
return u
"CL_INVALID_DEVICE_TYPE"_s;
604 case -32:
return u
"CL_INVALID_PLATFORM"_s;
605 case -33:
return u
"CL_INVALID_DEVICE"_s;
606 case -34:
return u
"CL_INVALID_CONTEXT"_s;
607 case -35:
return u
"CL_INVALID_QUEUE_PROPERTIES"_s;
608 case -36:
return u
"CL_INVALID_COMMAND_QUEUE"_s;
609 case -37:
return u
"CL_INVALID_HOST_PTR"_s;
610 case -38:
return u
"CL_INVALID_MEM_OBJECT"_s;
611 case -39:
return u
"CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"_s;
612 case -40:
return u
"CL_INVALID_IMAGE_SIZE"_s;
613 case -41:
return u
"CL_INVALID_SAMPLER"_s;
614 case -42:
return u
"CL_INVALID_BINARY"_s;
615 case -43:
return u
"CL_INVALID_BUILD_OPTIONS"_s;
616 case -44:
return u
"CL_INVALID_PROGRAM"_s;
617 case -45:
return u
"CL_INVALID_PROGRAM_EXECUTABLE"_s;
618 case -46:
return u
"CL_INVALID_KERNEL_NAME"_s;
619 case -47:
return u
"CL_INVALID_KERNEL_DEFINITION"_s;
620 case -48:
return u
"CL_INVALID_KERNEL"_s;
621 case -49:
return u
"CL_INVALID_ARG_INDEX"_s;
622 case -50:
return u
"CL_INVALID_ARG_VALUE"_s;
623 case -51:
return u
"CL_INVALID_ARG_SIZE"_s;
624 case -52:
return u
"CL_INVALID_KERNEL_ARGS"_s;
625 case -53:
return u
"CL_INVALID_WORK_DIMENSION"_s;
626 case -54:
return u
"CL_INVALID_WORK_GROUP_SIZE"_s;
627 case -55:
return u
"CL_INVALID_WORK_ITEM_SIZE"_s;
628 case -56:
return u
"CL_INVALID_GLOBAL_OFFSET"_s;
629 case -57:
return u
"CL_INVALID_EVENT_WAIT_LIST"_s;
630 case -58:
return u
"CL_INVALID_EVENT"_s;
631 case -59:
return u
"CL_INVALID_OPERATION"_s;
632 case -60:
return u
"CL_INVALID_GL_OBJECT"_s;
633 case -61:
return u
"CL_INVALID_BUFFER_SIZE"_s;
634 case -62:
return u
"CL_INVALID_MIP_LEVEL"_s;
635 case -63:
return u
"CL_INVALID_GLOBAL_WORK_SIZE"_s;
636 case -64:
return u
"CL_INVALID_PROPERTY"_s;
637 case -65:
return u
"CL_INVALID_IMAGE_DESCRIPTOR"_s;
638 case -66:
return u
"CL_INVALID_COMPILER_OPTIONS"_s;
639 case -67:
return u
"CL_INVALID_LINKER_OPTIONS"_s;
640 case -68:
return u
"CL_INVALID_DEVICE_PARTITION_COUNT"_s;
641 case -69:
return u
"CL_INVALID_PIPE_SIZE"_s;
642 case -70:
return u
"CL_INVALID_DEVICE_QUEUE"_s;
643 case -71:
return u
"CL_INVALID_SPEC_ID"_s;
644 case -72:
return u
"CL_MAX_SIZE_RESTRICTION_EXCEEDED"_s;
645 case -1002:
return u
"CL_INVALID_D3D10_DEVICE_KHR"_s;
646 case -1003:
return u
"CL_INVALID_D3D10_RESOURCE_KHR"_s;
647 case -1004:
return u
"CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR"_s;
648 case -1005:
return u
"CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR"_s;
649 case -1006:
return u
"CL_INVALID_D3D11_DEVICE_KHR"_s;
650 case -1007:
return u
"CL_INVALID_D3D11_RESOURCE_KHR"_s;
651 case -1008:
return u
"CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR"_s;
652 case -1009:
return u
"CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR"_s;
653 case -1010:
return u
"CL_INVALID_DX9_MEDIA_ADAPTER_KHR"_s;
654 case -1011:
return u
"CL_INVALID_DX9_MEDIA_SURFACE_KHR"_s;
655 case -1012:
return u
"CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR"_s;
656 case -1013:
return u
"CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR"_s;
657 case -1093:
return u
"CL_INVALID_EGL_OBJECT_KHR"_s;
658 case -1092:
return u
"CL_EGL_RESOURCE_NOT_ACQUIRED_KHR"_s;
659 case -1001:
return u
"CL_PLATFORM_NOT_FOUND_KHR"_s;
660 case -1057:
return u
"CL_DEVICE_PARTITION_FAILED_EXT"_s;
661 case -1058:
return u
"CL_INVALID_PARTITION_COUNT_EXT"_s;
662 case -1059:
return u
"CL_INVALID_PARTITION_NAME_EXT"_s;
663 case -1094:
return u
"CL_INVALID_ACCELERATOR_INTEL"_s;
664 case -1095:
return u
"CL_INVALID_ACCELERATOR_TYPE_INTEL"_s;
665 case -1096:
return u
"CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL"_s;
666 case -1097:
return u
"CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL"_s;
667 case -1000:
return u
"CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR"_s;
668 case -1098:
return u
"CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL"_s;
669 case -1099:
return u
"CL_INVALID_VA_API_MEDIA_SURFACE_INTEL"_s;
670 case -1100:
return u
"CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL"_s;
671 case -1101:
return u
"CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL"_s;
672 default:
return u
"CL_UNKNOWN_ERROR"_s;
684 return cl::CommandQueue(
context );
689 const cl_command_queue_properties properties = 0;
691 cl_command_queue queue = clCreateCommandQueue(
context(), device(), properties,
nullptr );
693 return cl::CommandQueue( queue,
true );
700 static std::once_flag contextCreated;
701 std::call_once( contextCreated, []()
703 if (
available() && cl::Platform::getDefault()() && cl::Device::getDefault()() )
705 context = cl::Context( cl::Device::getDefault() );
727 if ( ok && version < 2.0f )
729 program.build( u
"-cl-std=CL%1 -I\"%2\""_s
734 program.build( u
"-I\"%1\""_s
738 catch ( cl::BuildError &e )
741 if ( build_log.isEmpty() )
742 build_log = QObject::tr(
"Build logs not available!" );
743 const QString err = QObject::tr(
"Error building OpenCL program: %1" )
746 if ( exceptionBehavior ==
Throw )
749 catch ( cl::Error &e )
751 const QString err = QObject::tr(
"Error %1 building OpenCL program in %2" )
752 .arg(
errorText( e.err() ), QString::fromStdString( e.what() ) );
@ Warning
Warning message.
@ Critical
Critical/error message.
@ Info
Information message.
@ Success
Used for reporting a successful operation.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static Q_DECL_DEPRECATED cl::Program buildProgram(const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior=Catch)
Build the program from source in the given context and depending on exceptionBehavior can throw or ca...
static void setSourcePath(const QString &value)
Set the base path to OpenCL program directory.
static QString buildLog(cl::BuildError &error)
Extract and return the build log error from error.
static void storePreferredDevice(const QString deviceId)
Store in the settings the preferred deviceId device identifier.
static QString sourcePath()
Returns the base path to OpenCL program directory.
static cl::Context context()
Context factory.
static cl::Device activeDevice()
Returns the active device.
static bool enabled()
Returns true if OpenCL is enabled in the user settings.
static bool available()
Checks whether a suitable OpenCL platform and device is available on this system and initialize the Q...
static QString deviceDescription(const cl::Device device)
Returns a formatted description for the device.
static QString activeDeviceInfo(const Info infoType=Info::Name)
Returns infoType information about the active (default) device.
static const std::vector< cl::Device > devices()
Returns a list of OpenCL devices found on this sysytem.
static void setEnabled(bool enabled)
Set the OpenCL user setting to enabled.
static QString preferredDevice()
Read from the settings the preferred device identifier.
static QString deviceInfo(const Info infoType, cl::Device device)
Returns infoType information about the device.
static QString errorText(const int errorCode)
Returns a string representation from an OpenCL errorCode.
static cl::CommandQueue commandQueue()
Create an OpenCL command queue from the default context.
static QString sourceFromPath(const QString &path)
Read an OpenCL source file from path.
static QString sourceFromBaseName(const QString &baseName)
Returns the full path to a an OpenCL source file from the baseName ('.cl' extension is automatically ...
ExceptionBehavior
The ExceptionBehavior enum define how exceptions generated by OpenCL should be treated.
@ Throw
Write errors in the message log and re-throw exceptions.
static QString deviceId(const cl::Device device)
Create a string identifier from a device.
Info
The Info enum maps to OpenCL info constants.
static QLatin1String LOGMESSAGE_TAG
OpenCL string for message logs.
static QString activePlatformVersion()
Returns the active platform OpenCL version string (e.g.
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)