QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsopenclutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsopenclutils.cpp - QgsOpenClUtils
3 
4  ---------------------
5  begin : 11.4.2018
6  copyright : (C) 2018 by elpaso
7  email : elpaso at itopen dot it
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 #include "qgsopenclutils.h"
17 #include "qgssettings.h"
18 #include "qgsmessagelog.h"
19 #include "qgslogger.h"
20 
21 #include <QLibrary>
22 
23 #include <QTextStream>
24 #include <QFile>
25 #include <QDebug>
26 
27 #ifdef Q_OS_WIN
28 #include <windows.h>
29 #include <tchar.h>
30 #endif
31 
32 QLatin1String QgsOpenClUtils::SETTINGS_GLOBAL_ENABLED_KEY = QLatin1String( "OpenClEnabled" );
33 QLatin1String QgsOpenClUtils::SETTINGS_DEFAULT_DEVICE_KEY = QLatin1String( "OpenClDefaultDevice" );
34 QLatin1String QgsOpenClUtils::LOGMESSAGE_TAG = QLatin1String( "OpenCL" );
35 bool QgsOpenClUtils::sAvailable = false;
36 
37 Q_GLOBAL_STATIC( QString, sSourcePath )
38 
39 
40 const std::vector<cl::Device> QgsOpenClUtils::devices()
41 {
42  std::vector<cl::Platform> platforms;
43  cl::Platform::get( &platforms );
44  std::vector<cl::Device> existingDevices;
45  for ( const auto &p : platforms )
46  {
47  const std::string platver = p.getInfo<CL_PLATFORM_VERSION>();
48  QgsMessageLog::logMessage( QObject::tr( "Found OpenCL platform %1: %2" )
49  .arg( QString::fromStdString( platver ),
50  QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ),
51  LOGMESSAGE_TAG, Qgis::MessageLevel::Info );
52  if ( platver.find( "OpenCL " ) != std::string::npos )
53  {
54  std::vector<cl::Device> _devices;
55  // Check for a device
56  try
57  {
58  p.getDevices( CL_DEVICE_TYPE_ALL, &_devices );
59  }
60  catch ( cl::Error &e )
61  {
62  QgsMessageLog::logMessage( QObject::tr( "Error %1 on platform %3 searching for OpenCL device: %2" )
63  .arg( errorText( e.err() ),
64  QString::fromStdString( e.what() ),
65  QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ),
66  LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
67  }
68  if ( _devices.size() > 0 )
69  {
70  for ( unsigned long i = 0; i < _devices.size(); i++ )
71  {
72  QgsMessageLog::logMessage( QObject::tr( "Found OpenCL device: %1" )
73  .arg( deviceId( _devices[i] ) ),
74  LOGMESSAGE_TAG, Qgis::MessageLevel::Info );
75  existingDevices.push_back( _devices[i] );
76  }
77  }
78  }
79  }
80  return existingDevices;
81 }
82 
83 void QgsOpenClUtils::init()
84 {
85  static std::once_flag initialized;
86  std::call_once( initialized, [ = ]( )
87  {
88 #ifdef Q_OS_MAC
89  QLibrary openCLLib { QStringLiteral( "/System/Library/Frameworks/OpenCL.framework/Versions/Current/OpenCL" ) };
90 #else
91  QLibrary openCLLib { QStringLiteral( "OpenCL" ) };
92 #endif
93  openCLLib.setLoadHints( QLibrary::LoadHint::ResolveAllSymbolsHint );
94  if ( ! openCLLib.load() )
95  {
96  QgsMessageLog::logMessage( QObject::tr( "Error loading OpenCL library: %1" )
97  .arg( openCLLib.errorString() ),
98  LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
99  return;
100  }
101 
102 #ifdef Q_OS_WIN
103  HMODULE hModule = GetModuleHandle( "OpenCL.dll" );
104  if ( hModule )
105  {
106  TCHAR pszFileName[1024];
107  if ( GetModuleFileName( hModule, pszFileName, 1024 ) < 1024 )
108  {
109  QgsMessageLog::logMessage( QObject::tr( "Found OpenCL library filename %1" )
110  .arg( pszFileName ),
111  LOGMESSAGE_TAG, Qgis::MessageLevel::Info );
112 
113  DWORD dwUseless;
114  DWORD dwLen = GetFileVersionInfoSize( pszFileName, &dwUseless );
115  if ( dwLen )
116  {
117  LPTSTR lpVI = ( LPSTR ) malloc( dwLen );
118  if ( lpVI )
119  {
120  if ( GetFileVersionInfo( pszFileName, NULL, dwLen, lpVI ) )
121  {
122  VS_FIXEDFILEINFO *lpFFI;
123  if ( VerQueryValue( lpVI, "\\", ( LPVOID * ) &lpFFI, ( UINT * ) &dwUseless ) )
124  {
125  QgsMessageLog::logMessage( QObject::tr( "OpenCL Product version: %1.%2.%3.%4" )
126  .arg( lpFFI->dwProductVersionMS >> 16 )
127  .arg( lpFFI->dwProductVersionMS & 0xffff )
128  .arg( lpFFI->dwProductVersionLS >> 16 )
129  .arg( lpFFI->dwProductVersionLS & 0xffff ),
130  LOGMESSAGE_TAG, Qgis::MessageLevel::Info );
131  }
132 
133  struct LANGANDCODEPAGE
134  {
135  WORD wLanguage;
136  WORD wCodePage;
137  } *lpTranslate;
138 
139  DWORD cbTranslate;
140 
141  if ( VerQueryValue( lpVI, _T( "\\VarFileInfo\\Translation" ), ( LPVOID * ) &lpTranslate, ( UINT * ) &cbTranslate ) && cbTranslate >= sizeof( struct LANGANDCODEPAGE ) )
142  {
143  QStringList items = QStringList()
144  << QStringLiteral( "Comments" )
145  << QStringLiteral( "InternalName" )
146  << QStringLiteral( "ProductName" )
147  << QStringLiteral( "CompanyName" )
148  << QStringLiteral( "LegalCopyright" )
149  << QStringLiteral( "ProductVersion" )
150  << QStringLiteral( "FileDescription" )
151  << QStringLiteral( "LegalTrademarks" )
152  << QStringLiteral( "PrivateBuild" )
153  << QStringLiteral( "FileVersion" )
154  << QStringLiteral( "OriginalFilename" )
155  << QStringLiteral( "SpecialBuild" );
156  for ( auto d : items )
157  {
158  LPTSTR lpBuffer;
159  QString subBlock = QString( QStringLiteral( "\\StringFileInfo\\%1%2\\%3" ) )
160  .arg( lpTranslate[0].wLanguage, 4, 16, QLatin1Char( '0' ) )
161  .arg( lpTranslate[0].wCodePage, 4, 16, QLatin1Char( '0' ) )
162  .arg( d );
163 
164  QgsDebugMsg( QString( "d:%1 subBlock:%2" ).arg( d ).arg( subBlock ) );
165 
166  BOOL r = VerQueryValue( lpVI, subBlock.toUtf8(), ( LPVOID * )&lpBuffer, ( UINT * )&dwUseless );
167 
168  if ( r && lpBuffer && lpBuffer != INVALID_HANDLE_VALUE && dwUseless < 1023 )
169  {
170  QgsMessageLog::logMessage( QObject::tr( "Found OpenCL version info %1: %2" )
171  .arg( d )
172  .arg( QString::fromLocal8Bit( lpBuffer ) ),
173  LOGMESSAGE_TAG, Qgis::MessageLevel::Info );
174  }
175  }
176  }
177  }
178 
179  free( lpVI );
180  }
181  }
182  }
183  else
184  {
185  QgsMessageLog::logMessage( QObject::tr( "No module handle to OpenCL library" ), LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
186  }
187  }
188  else
189  {
190  QgsMessageLog::logMessage( QObject::tr( "No module handle to OpenCL library" ), LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
191  }
192 #endif
193 
194  try
195  {
196  activate( preferredDevice() );
197  }
198  catch ( cl::Error &e )
199  {
200  QgsMessageLog::logMessage( QObject::tr( "Error %1 initializing OpenCL device: %2" )
201  .arg( errorText( e.err() ), QString::fromStdString( e.what() ) ),
202  LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
203  }
204 
205  } );
206 }
207 
209 {
210  return *sSourcePath();
211 }
212 
213 void QgsOpenClUtils::setSourcePath( const QString &value )
214 {
215  *sSourcePath() = value;
216 }
217 
219 {
220  return deviceInfo( infoType, activeDevice( ) );
221 }
222 
223 QString QgsOpenClUtils::deviceInfo( const Info infoType, cl::Device device )
224 {
225  try
226  {
227  switch ( infoType )
228  {
229  case Info::Vendor:
230  return QString::fromStdString( device.getInfo<CL_DEVICE_VENDOR>() );
231  case Info::Profile:
232  return QString::fromStdString( device.getInfo<CL_DEVICE_PROFILE>() );
233  case Info::Version:
234  return QString::fromStdString( device.getInfo<CL_DEVICE_VERSION>() );
235  case Info::ImageSupport:
236  return device.getInfo<CL_DEVICE_IMAGE_SUPPORT>() ? QStringLiteral( "True" ) : QStringLiteral( "False" );
237  case Info::Image2dMaxHeight:
238  return QString::number( device.getInfo<CL_DEVICE_IMAGE2D_MAX_HEIGHT>() );
239  case Info::MaxMemAllocSize:
240  return QString::number( device.getInfo<CL_DEVICE_MAX_MEM_ALLOC_SIZE>() );
241  case Info::Image2dMaxWidth:
242  return QString::number( device.getInfo<CL_DEVICE_IMAGE2D_MAX_WIDTH>() );
243  case Info::Type:
244  {
245  const unsigned long type( device.getInfo<CL_DEVICE_TYPE>() );
246  int mappedType;
247  switch ( type )
248  {
249  case CL_DEVICE_TYPE_CPU:
250  mappedType = QgsOpenClUtils::HardwareType::CPU;
251  break;
252  case CL_DEVICE_TYPE_GPU:
253  mappedType = QgsOpenClUtils::HardwareType::GPU;
254  break;
255  default:
256  mappedType = QgsOpenClUtils::HardwareType::Other;
257  }
258  const QMetaEnum metaEnum = QMetaEnum::fromType<QgsOpenClUtils::HardwareType>();
259  return metaEnum.valueToKey( mappedType );
260  }
261  case Info::Name:
262  return QString::fromStdString( device.getInfo<CL_DEVICE_NAME>() );
263  }
264  }
265  catch ( cl::Error &e )
266  {
267  // This can be a legitimate error when initializing, let's log it quietly
268  QgsDebugMsgLevel( QStringLiteral( "Error %1 getting info for OpenCL device: %2" )
269  .arg( errorText( e.err() ), QString::fromStdString( e.what() ) ),
270  4 );
271  return QString();
272  }
273  return QString();
274 }
275 
276 
278 {
279  return QgsSettings().value( SETTINGS_GLOBAL_ENABLED_KEY, false, QgsSettings::Section::Core ).toBool();
280 }
281 
283 {
284  return cl::Device::getDefault();
285 }
286 
288 {
289  QString version;
290  if ( cl::Platform::getDefault()() )
291  {
292  const std::string platver = cl::Platform::getDefault().getInfo<CL_PLATFORM_VERSION>();
293  if ( platver.find( "OpenCL " ) != std::string::npos )
294  {
295  version = QString::fromStdString( platver.substr( 7 ) ).split( ' ' ).first();
296  }
297  }
298  return version;
299 }
300 
301 void QgsOpenClUtils::storePreferredDevice( const QString deviceId )
302 {
303  QgsSettings().setValue( SETTINGS_DEFAULT_DEVICE_KEY, deviceId, QgsSettings::Section::Core );
304 }
305 
307 {
308  return QgsSettings().value( SETTINGS_DEFAULT_DEVICE_KEY, QString( ), QgsSettings::Section::Core ).toString();
309 }
310 
311 QString QgsOpenClUtils::deviceId( const cl::Device device )
312 {
313  return QStringLiteral( "%1|%2|%3|%4" )
314  .arg( deviceInfo( QgsOpenClUtils::Info::Name, device ) )
315  .arg( deviceInfo( QgsOpenClUtils::Info::Vendor, device ) )
316  .arg( deviceInfo( QgsOpenClUtils::Info::Version, device ) )
317  .arg( deviceInfo( QgsOpenClUtils::Info::Type, device ) );
318 }
319 
320 bool QgsOpenClUtils::activate( const QString &preferredDeviceId )
321 {
322  if ( deviceId( activeDevice() ) == preferredDeviceId )
323  {
324  sAvailable = true;
325  return false;
326  }
327  try
328  {
329  std::vector<cl::Platform> platforms;
330  cl::Platform::get( &platforms );
331  cl::Platform plat;
332  cl::Device dev;
333  bool deviceFound = false;
334  for ( const auto &p : platforms )
335  {
336  if ( deviceFound )
337  break;
338  const std::string platver = p.getInfo<CL_PLATFORM_VERSION>();
339  QgsDebugMsg( QStringLiteral( "Found OpenCL platform %1: %2" ).arg( QString::fromStdString( platver ), QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ) );
340  if ( platver.find( "OpenCL " ) != std::string::npos )
341  {
342  std::vector<cl::Device> devices;
343  // Search for a device
344  try
345  {
346  p.getDevices( CL_DEVICE_TYPE_ALL, &devices );
347  // First search for the preferred device
348  if ( ! preferredDeviceId.isEmpty() )
349  {
350  for ( const auto &_dev : devices )
351  {
352  if ( preferredDeviceId == deviceId( _dev ) )
353  {
354  // Got one!
355  plat = p;
356  dev = _dev;
357  deviceFound = true;
358  break;
359  }
360  }
361  }
362  // Not found or preferred device id not set: get the first GPU
363  if ( ! deviceFound )
364  {
365  for ( const auto &_dev : devices )
366  {
367  if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_GPU )
368  {
369  // Got one!
370  plat = p;
371  dev = _dev;
372  deviceFound = true;
373  break;
374  }
375  }
376  }
377  // Still nothing? Get the first device
378  if ( ! deviceFound )
379  {
380  for ( const auto &_dev : devices )
381  {
382  if ( _dev.getInfo<CL_DEVICE_TYPE>() == CL_DEVICE_TYPE_CPU )
383  {
384  // Got one!
385  plat = p;
386  dev = _dev;
387  deviceFound = true;
388  break;
389  }
390  }
391  }
392  if ( ! deviceFound )
393  {
394  QgsMessageLog::logMessage( QObject::tr( "No OpenCL device could be found." ), LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
395  }
396  }
397  catch ( cl::Error &e )
398  {
399  QgsDebugMsg( QStringLiteral( "Error %1 on platform %3 searching for OpenCL device: %2" )
400  .arg( errorText( e.err() ),
401  QString::fromStdString( e.what() ),
402  QString::fromStdString( p.getInfo<CL_PLATFORM_NAME>() ) ) );
403  }
404 
405  }
406  }
407  if ( ! plat() )
408  {
409  QgsMessageLog::logMessage( QObject::tr( "No OpenCL platform found." ), LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
410  sAvailable = false;
411  }
412  else
413  {
414  const cl::Platform newP = cl::Platform::setDefault( plat );
415  if ( newP != plat )
416  {
417  QgsMessageLog::logMessage( QObject::tr( "Error setting default platform." ),
418  LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
419  sAvailable = false;
420  }
421  else
422  {
423  cl::Device::setDefault( dev );
424  QgsMessageLog::logMessage( QObject::tr( "Active OpenCL device: %1" )
425  .arg( QString::fromStdString( dev.getInfo<CL_DEVICE_NAME>() ) ),
426  LOGMESSAGE_TAG, Qgis::MessageLevel::Success );
427  sAvailable = true;
428  }
429  }
430  }
431 
432  catch ( cl::Error &e )
433  {
434  QgsMessageLog::logMessage( QObject::tr( "Error %1 searching for OpenCL device: %2" )
435  .arg( errorText( e.err() ), QString::fromStdString( e.what() ) ),
436  LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
437  sAvailable = false;
438  }
439  return sAvailable;
440 }
441 
442 QString QgsOpenClUtils::deviceDescription( const cl::Device device )
443 {
444  return QStringLiteral(
445  "Type: <b>%9</b><br>"
446  "Name: <b>%1</b><br>"
447  "Vendor: <b>%2</b><br>"
448  "Profile: <b>%3</b><br>"
449  "Version: <b>%4</b><br>"
450  "Image support: <b>%5</b><br>"
451  "Max image2d width: <b>%6</b><br>"
452  "Max image2d height: <b>%7</b><br>"
453  "Max mem alloc size: <b>%8</b><br>"
454  ).arg( QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Name, device ),
455  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Vendor, device ),
456  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Profile, device ),
457  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Version, device ),
458  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::ImageSupport, device ),
459  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Image2dMaxWidth, device ),
460  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Image2dMaxHeight, device ),
461  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::MaxMemAllocSize, device ),
462  QgsOpenClUtils::deviceInfo( QgsOpenClUtils::Info::Type, device ) );
463 }
464 
465 QString QgsOpenClUtils::deviceDescription( const QString deviceId )
466 {
467  for ( const auto &dev : devices( ) )
468  {
469  if ( QgsOpenClUtils::deviceId( dev ) == deviceId )
470  return deviceDescription( dev );
471  }
472  return QString();
473 }
474 
476 {
477  init();
478  return sAvailable;
479 }
480 
481 void QgsOpenClUtils::setEnabled( bool enabled )
482 {
483  QgsSettings().setValue( SETTINGS_GLOBAL_ENABLED_KEY, enabled, QgsSettings::Section::Core );
484 }
485 
486 
487 
488 QString QgsOpenClUtils::sourceFromPath( const QString &path )
489 {
490  // TODO: check for compatibility with current platform ( cl_khr_fp64 )
491  // Try to load the program sources
492  QString source_str;
493  QFile file( path );
494  if ( file.open( QFile::ReadOnly | QFile::Text ) )
495  {
496  QTextStream in( &file );
497  source_str = in.readAll();
498  file.close();
499  }
500  else
501  {
502  QgsMessageLog::logMessage( QObject::tr( "Could not load OpenCL program from path %1." ).arg( path ), LOGMESSAGE_TAG, Qgis::MessageLevel::Warning );
503  }
504  return source_str;
505 }
506 
507 QString QgsOpenClUtils::sourceFromBaseName( const QString &baseName )
508 {
509  const QString path = QStringLiteral( "%1/%2.cl" ).arg( sourcePath(), baseName );
510  return sourceFromPath( path );
511 }
512 
513 QString QgsOpenClUtils::buildLog( cl::BuildError &error )
514 {
515  cl::BuildLogType build_logs = error.getBuildLog();
516  QString build_log;
517  if ( build_logs.size() > 0 )
518  build_log = QString::fromStdString( build_logs[0].second );
519  return build_log;
520 }
521 
522 QString QgsOpenClUtils::errorText( const int errorCode )
523 {
524  switch ( errorCode )
525  {
526  case 0: return QStringLiteral( "CL_SUCCESS" );
527  case -1: return QStringLiteral( "CL_DEVICE_NOT_FOUND" );
528  case -2: return QStringLiteral( "CL_DEVICE_NOT_AVAILABLE" );
529  case -3: return QStringLiteral( "CL_COMPILER_NOT_AVAILABLE" );
530  case -4: return QStringLiteral( "CL_MEM_OBJECT_ALLOCATION_FAILURE" );
531  case -5: return QStringLiteral( "CL_OUT_OF_RESOURCES" );
532  case -6: return QStringLiteral( "CL_OUT_OF_HOST_MEMORY" );
533  case -7: return QStringLiteral( "CL_PROFILING_INFO_NOT_AVAILABLE" );
534  case -8: return QStringLiteral( "CL_MEM_COPY_OVERLAP" );
535  case -9: return QStringLiteral( "CL_IMAGE_FORMAT_MISMATCH" );
536  case -10: return QStringLiteral( "CL_IMAGE_FORMAT_NOT_SUPPORTED" );
537  case -12: return QStringLiteral( "CL_MAP_FAILURE" );
538  case -13: return QStringLiteral( "CL_MISALIGNED_SUB_BUFFER_OFFSET" );
539  case -14: return QStringLiteral( "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST" );
540  case -15: return QStringLiteral( "CL_COMPILE_PROGRAM_FAILURE" );
541  case -16: return QStringLiteral( "CL_LINKER_NOT_AVAILABLE" );
542  case -17: return QStringLiteral( "CL_LINK_PROGRAM_FAILURE" );
543  case -18: return QStringLiteral( "CL_DEVICE_PARTITION_FAILED" );
544  case -19: return QStringLiteral( "CL_KERNEL_ARG_INFO_NOT_AVAILABLE" );
545  case -30: return QStringLiteral( "CL_INVALID_VALUE" );
546  case -31: return QStringLiteral( "CL_INVALID_DEVICE_TYPE" );
547  case -32: return QStringLiteral( "CL_INVALID_PLATFORM" );
548  case -33: return QStringLiteral( "CL_INVALID_DEVICE" );
549  case -34: return QStringLiteral( "CL_INVALID_CONTEXT" );
550  case -35: return QStringLiteral( "CL_INVALID_QUEUE_PROPERTIES" );
551  case -36: return QStringLiteral( "CL_INVALID_COMMAND_QUEUE" );
552  case -37: return QStringLiteral( "CL_INVALID_HOST_PTR" );
553  case -38: return QStringLiteral( "CL_INVALID_MEM_OBJECT" );
554  case -39: return QStringLiteral( "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR" );
555  case -40: return QStringLiteral( "CL_INVALID_IMAGE_SIZE" );
556  case -41: return QStringLiteral( "CL_INVALID_SAMPLER" );
557  case -42: return QStringLiteral( "CL_INVALID_BINARY" );
558  case -43: return QStringLiteral( "CL_INVALID_BUILD_OPTIONS" );
559  case -44: return QStringLiteral( "CL_INVALID_PROGRAM" );
560  case -45: return QStringLiteral( "CL_INVALID_PROGRAM_EXECUTABLE" );
561  case -46: return QStringLiteral( "CL_INVALID_KERNEL_NAME" );
562  case -47: return QStringLiteral( "CL_INVALID_KERNEL_DEFINITION" );
563  case -48: return QStringLiteral( "CL_INVALID_KERNEL" );
564  case -49: return QStringLiteral( "CL_INVALID_ARG_INDEX" );
565  case -50: return QStringLiteral( "CL_INVALID_ARG_VALUE" );
566  case -51: return QStringLiteral( "CL_INVALID_ARG_SIZE" );
567  case -52: return QStringLiteral( "CL_INVALID_KERNEL_ARGS" );
568  case -53: return QStringLiteral( "CL_INVALID_WORK_DIMENSION" );
569  case -54: return QStringLiteral( "CL_INVALID_WORK_GROUP_SIZE" );
570  case -55: return QStringLiteral( "CL_INVALID_WORK_ITEM_SIZE" );
571  case -56: return QStringLiteral( "CL_INVALID_GLOBAL_OFFSET" );
572  case -57: return QStringLiteral( "CL_INVALID_EVENT_WAIT_LIST" );
573  case -58: return QStringLiteral( "CL_INVALID_EVENT" );
574  case -59: return QStringLiteral( "CL_INVALID_OPERATION" );
575  case -60: return QStringLiteral( "CL_INVALID_GL_OBJECT" );
576  case -61: return QStringLiteral( "CL_INVALID_BUFFER_SIZE" );
577  case -62: return QStringLiteral( "CL_INVALID_MIP_LEVEL" );
578  case -63: return QStringLiteral( "CL_INVALID_GLOBAL_WORK_SIZE" );
579  case -64: return QStringLiteral( "CL_INVALID_PROPERTY" );
580  case -65: return QStringLiteral( "CL_INVALID_IMAGE_DESCRIPTOR" );
581  case -66: return QStringLiteral( "CL_INVALID_COMPILER_OPTIONS" );
582  case -67: return QStringLiteral( "CL_INVALID_LINKER_OPTIONS" );
583  case -68: return QStringLiteral( "CL_INVALID_DEVICE_PARTITION_COUNT" );
584  case -69: return QStringLiteral( "CL_INVALID_PIPE_SIZE" );
585  case -70: return QStringLiteral( "CL_INVALID_DEVICE_QUEUE" );
586  case -71: return QStringLiteral( "CL_INVALID_SPEC_ID" );
587  case -72: return QStringLiteral( "CL_MAX_SIZE_RESTRICTION_EXCEEDED" );
588  case -1002: return QStringLiteral( "CL_INVALID_D3D10_DEVICE_KHR" );
589  case -1003: return QStringLiteral( "CL_INVALID_D3D10_RESOURCE_KHR" );
590  case -1004: return QStringLiteral( "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR" );
591  case -1005: return QStringLiteral( "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR" );
592  case -1006: return QStringLiteral( "CL_INVALID_D3D11_DEVICE_KHR" );
593  case -1007: return QStringLiteral( "CL_INVALID_D3D11_RESOURCE_KHR" );
594  case -1008: return QStringLiteral( "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR" );
595  case -1009: return QStringLiteral( "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR" );
596  case -1010: return QStringLiteral( "CL_INVALID_DX9_MEDIA_ADAPTER_KHR" );
597  case -1011: return QStringLiteral( "CL_INVALID_DX9_MEDIA_SURFACE_KHR" );
598  case -1012: return QStringLiteral( "CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR" );
599  case -1013: return QStringLiteral( "CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR" );
600  case -1093: return QStringLiteral( "CL_INVALID_EGL_OBJECT_KHR" );
601  case -1092: return QStringLiteral( "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR" );
602  case -1001: return QStringLiteral( "CL_PLATFORM_NOT_FOUND_KHR" );
603  case -1057: return QStringLiteral( "CL_DEVICE_PARTITION_FAILED_EXT" );
604  case -1058: return QStringLiteral( "CL_INVALID_PARTITION_COUNT_EXT" );
605  case -1059: return QStringLiteral( "CL_INVALID_PARTITION_NAME_EXT" );
606  case -1094: return QStringLiteral( "CL_INVALID_ACCELERATOR_INTEL" );
607  case -1095: return QStringLiteral( "CL_INVALID_ACCELERATOR_TYPE_INTEL" );
608  case -1096: return QStringLiteral( "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL" );
609  case -1097: return QStringLiteral( "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL" );
610  case -1000: return QStringLiteral( "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR" );
611  case -1098: return QStringLiteral( "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL" );
612  case -1099: return QStringLiteral( "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL" );
613  case -1100: return QStringLiteral( "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL" );
614  case -1101: return QStringLiteral( "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL" );
615  default: return QStringLiteral( "CL_UNKNOWN_ERROR" );
616  }
617 }
618 
620 {
621  // Depending on the platform version, to avoid a crash
622  // we need to use the legacy calls to C API instead of the 2.0
623  // compatible C++ API.
624  cl::Context context( QgsOpenClUtils::context() );
625  if ( QgsOpenClUtils::activePlatformVersion().toFloat() >= 200 )
626  {
627  return cl::CommandQueue( context );
628  }
629  else // legacy
630  {
631  cl::Device device( QgsOpenClUtils::activeDevice() );
632  const cl_command_queue_properties properties = 0;
634  cl_command_queue queue = clCreateCommandQueue( context(), device(), properties, nullptr );
636  return cl::CommandQueue( queue, true );
637  }
638 }
639 
641 {
642  static cl::Context context;
643  static std::once_flag contextCreated;
644  std::call_once( contextCreated, [ = ]()
645  {
646  if ( available() && cl::Platform::getDefault()() && cl::Device::getDefault()() )
647  {
648  context = cl::Context( cl::Device::getDefault() );
649  }
650  } );
651  return context;
652 }
653 
654 cl::Program QgsOpenClUtils::buildProgram( const cl::Context &, const QString &source, ExceptionBehavior exceptionBehavior )
655 {
656  // Deprecated: ignore context and use default
657  return buildProgram( source, exceptionBehavior );
658 }
659 
660 cl::Program QgsOpenClUtils::buildProgram( const QString &source, QgsOpenClUtils::ExceptionBehavior exceptionBehavior )
661 {
662  cl::Program program;
663  try
664  {
665  program = cl::Program( QgsOpenClUtils::context(), source.toStdString( ) );
666  // OpenCL version for compatibility with older hardware, but it's up to
667  // llvm to support latest CL versions
668  bool ok;
669  const float version( QgsOpenClUtils::activePlatformVersion().toFloat( &ok ) );
670  if ( ok && version < 2.0f )
671  {
672  program.build( QStringLiteral( "-cl-std=CL%1 -I\"%2\"" )
674  .arg( sourcePath() ).toStdString().c_str() );
675  }
676  else
677  {
678  program.build( QStringLiteral( "-I\"%1\"" )
679  .arg( sourcePath() ).toStdString().c_str() );
680  }
681  }
682  catch ( cl::BuildError &e )
683  {
684  QString build_log( buildLog( e ) );
685  if ( build_log.isEmpty() )
686  build_log = QObject::tr( "Build logs not available!" );
687  const QString err = QObject::tr( "Error building OpenCL program: %1" )
688  .arg( build_log );
689  QgsMessageLog::logMessage( err, LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
690  if ( exceptionBehavior == Throw )
691  throw e;
692  }
693  catch ( cl::Error &e )
694  {
695  const QString err = QObject::tr( "Error %1 building OpenCL program in %2" )
696  .arg( errorText( e.err() ), QString::fromStdString( e.what() ) );
697  QgsMessageLog::logMessage( err, LOGMESSAGE_TAG, Qgis::MessageLevel::Critical );
698  throw e;
699  }
700  return program;
701 }
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
The QgsOpenClUtils class is responsible for common OpenCL operations such as.
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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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
Definition: qgis.h:2065
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2064
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38