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