LibreOffice Module opencl (master) 1
openclwrapper.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <config_folders.h>
11
12#include <opencl_device.hxx>
14
18#include <osl/file.hxx>
19#include <rtl/bootstrap.hxx>
20#include <rtl/digest.h>
21#include <rtl/strbuf.hxx>
22#include <rtl/ustring.hxx>
23#include <sal/config.h>
24#include <sal/log.hxx>
25#include <opencl/OpenCLZone.hxx>
26
27#include <memory>
28#include <string_view>
29
30#include <stdlib.h>
31
32#include <officecfg/Office/Common.hxx>
33
34#ifdef _WIN32
35#include <prewin.h>
36#include <postwin.h>
37#define OPENCL_DLL_NAME "OpenCL.dll"
38#elif defined(MACOSX)
39#define OPENCL_DLL_NAME nullptr
40#else
41#define OPENCL_DLL_NAME "libOpenCL.so.1"
42#endif
43
44#ifdef _WIN32_WINNT_WINBLUE
45#include <VersionHelpers.h>
46#endif
47
48#define DEVICE_NAME_LENGTH 1024
49#define DRIVER_VERSION_LENGTH 1024
50#define PLATFORM_VERSION_LENGTH 1024
51
52#define CHECK_OPENCL(status,name) \
53if( status != CL_SUCCESS ) \
54{ \
55 SAL_WARN( "opencl", "OpenCL error code " << status << " at " SAL_DETAIL_WHERE "from " name ); \
56 return false; \
57}
58
59namespace {
60
61bool bIsInited = false;
62
63}
64
65namespace openclwrapper {
66
68sal_uInt64 kernelFailures = 0;
69
70namespace
71{
72
73OString generateMD5(const void* pData, size_t length)
74{
75 sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_MD5];
76 rtlDigestError aError = rtl_digest_MD5(pData, length,
77 pBuffer, RTL_DIGEST_LENGTH_MD5);
78 SAL_WARN_IF(aError != rtl_Digest_E_None, "opencl", "md5 generation failed");
79
80 OStringBuffer aBuffer(length * 2);
81 const char* const pString = "0123456789ABCDEF";
82 for(sal_uInt8 val : pBuffer)
83 {
84 aBuffer.append(OStringChar(pString[val/16]) + OStringChar(pString[val%16]));
85 }
86 return aBuffer.makeStringAndClear();
87}
88
89OString const & getCacheFolder()
90{
91 static OString const aCacheFolder = []()
92 {
93 OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
94 rtl::Bootstrap::expandMacros(url);
95
96 osl::Directory::create(url);
97
98 return OUStringToOString(url, RTL_TEXTENCODING_UTF8);
99 }();
100 return aCacheFolder;
101}
102
103}
104
105static bool initializeCommandQueue(GPUEnv& aGpuEnv)
106{
107 OpenCLZone zone;
108
109 cl_int nState;
110 cl_command_queue command_queue[OPENCL_CMDQUEUE_SIZE];
111
112 for (int i = 0; i < OPENCL_CMDQUEUE_SIZE; ++i)
113 {
114 command_queue[i] = clCreateCommandQueue(aGpuEnv.mpContext, aGpuEnv.mpDevID, 0, &nState);
115 if (nState != CL_SUCCESS)
116 SAL_WARN("opencl", "clCreateCommandQueue failed: " << errorString(nState));
117
118 if (command_queue[i] == nullptr || nState != CL_SUCCESS)
119 {
120 // Release all command queues created so far.
121 for (int j = 0; j <= i; ++j)
122 {
123 if (command_queue[j])
124 {
125 clReleaseCommandQueue(command_queue[j]);
126 command_queue[j] = nullptr;
127 }
128 }
129
130 clReleaseContext(aGpuEnv.mpContext);
131 SAL_WARN("opencl", "failed to set/switch opencl device");
132 return false;
133 }
134
135 SAL_INFO("opencl", "Created command queue " << command_queue[i] << " for context " << aGpuEnv.mpContext);
136 }
137
138 for (int i = 0; i < OPENCL_CMDQUEUE_SIZE; ++i)
139 {
140 aGpuEnv.mpCmdQueue[i] = command_queue[i];
141 }
142 aGpuEnv.mbCommandQueueInitialized = true;
143 return true;
144}
145
146void setKernelEnv( KernelEnv *envInfo )
147{
149 {
151 }
152
153 envInfo->mpkContext = gpuEnv.mpContext;
154 envInfo->mpkProgram = gpuEnv.mpArryPrograms[0];
155
158}
159
160namespace {
161
162OString createFileName(cl_device_id deviceId, const char* clFileName)
163{
164 OString fileName(clFileName);
165 sal_Int32 nIndex = fileName.lastIndexOf(".cl");
166 if(nIndex > 0)
167 fileName = fileName.copy(0, nIndex);
168
169 char deviceName[DEVICE_NAME_LENGTH] = {0};
170 clGetDeviceInfo(deviceId, CL_DEVICE_NAME,
171 sizeof(deviceName), deviceName, nullptr);
172
173 char driverVersion[DRIVER_VERSION_LENGTH] = {0};
174 clGetDeviceInfo(deviceId, CL_DRIVER_VERSION,
175 sizeof(driverVersion), driverVersion, nullptr);
176
177 cl_platform_id platformId;
178 clGetDeviceInfo(deviceId, CL_DEVICE_PLATFORM,
179 sizeof(platformId), &platformId, nullptr);
180
181 char platformVersion[PLATFORM_VERSION_LENGTH] = {0};
182 clGetPlatformInfo(platformId, CL_PLATFORM_VERSION, sizeof(platformVersion),
183 platformVersion, nullptr);
184
185 // create hash for deviceName + driver version + platform version
186 OString aString = OString::Concat(deviceName) + driverVersion + platformVersion;
187 OString aHash = generateMD5(aString.getStr(), aString.getLength());
188
189 return getCacheFolder() + fileName + "-" + aHash + ".bin";
190}
191
192std::vector<std::shared_ptr<osl::File> > binaryGenerated( const char * clFileName, cl_context context )
193{
194 size_t numDevices=0;
195
196 std::vector<std::shared_ptr<osl::File> > aGeneratedFiles;
197 cl_int clStatus = clGetContextInfo( context, CL_CONTEXT_DEVICES,
198 0, nullptr, &numDevices );
199 numDevices /= sizeof(numDevices);
200
201 if(clStatus != CL_SUCCESS)
202 return aGeneratedFiles;
203
204 assert(numDevices == 1);
205
206 // grab the handle to the device in the context.
207 cl_device_id pDevID;
208 clStatus = clGetContextInfo( context, CL_CONTEXT_DEVICES,
209 sizeof( cl_device_id ), &pDevID, nullptr );
210
211 if(clStatus != CL_SUCCESS)
212 return aGeneratedFiles;
213
214 assert(pDevID == gpuEnv.mpDevID);
215
216 OString fileName = createFileName(gpuEnv.mpDevID, clFileName);
217 auto pNewFile = std::make_shared<osl::File>(OStringToOUString(fileName, RTL_TEXTENCODING_UTF8));
218 if(pNewFile->open(osl_File_OpenFlag_Read) == osl::FileBase::E_None)
219 {
220 aGeneratedFiles.push_back(pNewFile);
221 SAL_INFO("opencl.file", "Opening binary file '" << fileName << "' for reading: success");
222 }
223 else
224 {
225 SAL_INFO("opencl.file", "Opening binary file '" << fileName << "' for reading: FAIL");
226 }
227
228 return aGeneratedFiles;
229}
230
231bool writeBinaryToFile( std::string_view rFileName, const char* binary, size_t numBytes )
232{
233 osl::File file(OStringToOUString(rFileName, RTL_TEXTENCODING_UTF8));
234 osl::FileBase::RC status = file.open(
235 osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
236
237 if(status != osl::FileBase::E_None)
238 return false;
239
240 sal_uInt64 nBytesWritten = 0;
241 file.write( binary, numBytes, nBytesWritten );
242
243 assert(numBytes == nBytesWritten);
244
245 return true;
246}
247
248}
249
250bool generatBinFromKernelSource( cl_program program, const char * clFileName )
251{
252 cl_uint numDevices;
253
254 cl_int clStatus = clGetProgramInfo( program, CL_PROGRAM_NUM_DEVICES,
255 sizeof(numDevices), &numDevices, nullptr );
256 CHECK_OPENCL( clStatus, "clGetProgramInfo" );
257
258 assert(numDevices == 1);
259
260 cl_device_id pDevID;
261 /* grab the handle to the device in the program. */
262 clStatus = clGetProgramInfo( program, CL_PROGRAM_DEVICES,
263 sizeof(cl_device_id), &pDevID, nullptr );
264 CHECK_OPENCL( clStatus, "clGetProgramInfo" );
265
266 /* figure out the size of the binary. */
267 size_t binarySize;
268
269 clStatus = clGetProgramInfo( program, CL_PROGRAM_BINARY_SIZES,
270 sizeof(size_t), &binarySize, nullptr );
271 CHECK_OPENCL( clStatus, "clGetProgramInfo" );
272
273 /* copy over the generated binary. */
274 if ( binarySize != 0 )
275 {
276 std::unique_ptr<char[]> binary(new char[binarySize]);
277 clStatus = clGetProgramInfo( program, CL_PROGRAM_BINARIES,
278 sizeof(char *), &binary, nullptr );
279 CHECK_OPENCL(clStatus,"clGetProgramInfo");
280
281 OString fileName = createFileName(pDevID, clFileName);
282 if ( !writeBinaryToFile( fileName,
283 binary.get(), binarySize ) )
284 SAL_INFO("opencl.file", "Writing binary file '" << fileName << "': FAIL");
285 else
286 SAL_INFO("opencl.file", "Writing binary file '" << fileName << "': success");
287 }
288 return true;
289}
290
291namespace {
292
293struct OpenCLEnv
294{
295 cl_platform_id mpOclPlatformID;
296 cl_context mpOclContext;
297 cl_device_id mpOclDevsID;
298};
299
300bool initOpenCLAttr( OpenCLEnv * env )
301{
303 return true;
304
305 gpuEnv.mpContext = env->mpOclContext;
306 gpuEnv.mpPlatformID = env->mpOclPlatformID;
307 gpuEnv.mpDevID = env->mpOclDevsID;
308
310
312
313 gpuEnv.mnCmdQueuePos = 0; // default to 0.
314
315 return false;
316}
317
318bool buildProgram(const char* buildOption, GPUEnv* gpuInfo, int idx)
319{
320 cl_int clStatus;
321 //char options[512];
322 // create a cl program executable for all the devices specified
323 clStatus = clBuildProgram(gpuInfo->mpArryPrograms[idx], 1, &gpuInfo->mpDevID,
324 buildOption, nullptr, nullptr);
325
326 if ( clStatus != CL_SUCCESS )
327 {
328 size_t length;
329 clStatus = clGetProgramBuildInfo( gpuInfo->mpArryPrograms[idx], gpuInfo->mpDevID,
330 CL_PROGRAM_BUILD_LOG, 0, nullptr, &length);
331 if ( clStatus != CL_SUCCESS )
332 {
333 return false;
334 }
335
336 std::unique_ptr<char[]> buildLog(new char[length]);
337 clStatus = clGetProgramBuildInfo( gpuInfo->mpArryPrograms[idx], gpuInfo->mpDevID,
338 CL_PROGRAM_BUILD_LOG, length, buildLog.get(), &length );
339 if ( clStatus != CL_SUCCESS )
340 {
341 return false;
342 }
343
344 OString aBuildLogFileURL = getCacheFolder() + "kernel-build.log";
345 osl::File aBuildLogFile(OStringToOUString(aBuildLogFileURL, RTL_TEXTENCODING_UTF8));
346 osl::FileBase::RC status = aBuildLogFile.open(
347 osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
348
349 if(status != osl::FileBase::E_None)
350 return false;
351
352 sal_uInt64 nBytesWritten = 0;
353 aBuildLogFile.write( buildLog.get(), length, nBytesWritten );
354
355 return false;
356 }
357
358 return true;
359}
360
361}
362
363bool buildProgramFromBinary(const char* buildOption, GPUEnv* gpuInfo, const char* filename, int idx)
364{
365 size_t numDevices;
366 cl_int clStatus = clGetContextInfo( gpuInfo->mpContext, CL_CONTEXT_DEVICES,
367 0, nullptr, &numDevices );
368 numDevices /= sizeof(numDevices);
369 CHECK_OPENCL( clStatus, "clGetContextInfo" );
370
371 std::vector<std::shared_ptr<osl::File> > aGeneratedFiles = binaryGenerated(
372 filename, gpuInfo->mpContext );
373
374 if (aGeneratedFiles.size() == numDevices)
375 {
376 std::unique_ptr<size_t[]> length(new size_t[numDevices]);
377 std::unique_ptr<unsigned char*[]> pBinary(new unsigned char*[numDevices]);
378 for(size_t i = 0; i < numDevices; ++i)
379 {
380 sal_uInt64 nSize;
381 aGeneratedFiles[i]->getSize(nSize);
382 unsigned char* binary = new unsigned char[nSize];
383 sal_uInt64 nBytesRead;
384 aGeneratedFiles[i]->read(binary, nSize, nBytesRead);
385 if(nSize != nBytesRead)
386 assert(false);
387
388 length[i] = nBytesRead;
389
390 pBinary[i] = binary;
391 }
392
393 // grab the handles to all of the devices in the context.
394 std::unique_ptr<cl_device_id[]> pArryDevsID(new cl_device_id[numDevices]);
395 clStatus = clGetContextInfo( gpuInfo->mpContext, CL_CONTEXT_DEVICES,
396 sizeof( cl_device_id ) * numDevices, pArryDevsID.get(), nullptr );
397
398 if(clStatus != CL_SUCCESS)
399 {
400 for(size_t i = 0; i < numDevices; ++i)
401 {
402 delete[] pBinary[i];
403 }
404 return false;
405 }
406
407 cl_int binary_status;
408
409 gpuInfo->mpArryPrograms[idx] = clCreateProgramWithBinary( gpuInfo->mpContext,numDevices,
410 pArryDevsID.get(), length.get(), const_cast<const unsigned char**>(pBinary.get()),
411 &binary_status, &clStatus );
412 if(clStatus != CL_SUCCESS)
413 {
414 // something went wrong, fall back to compiling from source
415 return false;
416 }
417 SAL_INFO("opencl", "Created program " << gpuInfo->mpArryPrograms[idx] << " from binary");
418 for(size_t i = 0; i < numDevices; ++i)
419 {
420 delete[] pBinary[i];
421 }
422 }
423
424 if ( !gpuInfo->mpArryPrograms[idx] )
425 {
426 return false;
427 }
428 return buildProgram(buildOption, gpuInfo, idx);
429}
430
431namespace {
432
433void checkDeviceForDoubleSupport(cl_device_id deviceId, bool& bKhrFp64, bool& bAmdFp64)
434{
435 OpenCLZone zone;
436
437 bKhrFp64 = false;
438 bAmdFp64 = false;
439
440 // Check device extensions for double type
441 size_t aDevExtInfoSize = 0;
442
443 cl_uint clStatus = clGetDeviceInfo( deviceId, CL_DEVICE_EXTENSIONS, 0, nullptr, &aDevExtInfoSize );
444 if( clStatus != CL_SUCCESS )
445 return;
446
447 std::unique_ptr<char[]> pExtInfo(new char[aDevExtInfoSize]);
448
449 clStatus = clGetDeviceInfo( deviceId, CL_DEVICE_EXTENSIONS,
450 sizeof(char) * aDevExtInfoSize, pExtInfo.get(), nullptr);
451
452 if( clStatus != CL_SUCCESS )
453 return;
454
455 if ( strstr( pExtInfo.get(), "cl_khr_fp64" ) )
456 {
457 bKhrFp64 = true;
458 }
459 else
460 {
461 // Check if cl_amd_fp64 extension is supported
462 if ( strstr( pExtInfo.get(), "cl_amd_fp64" ) )
463 bAmdFp64 = true;
464 }
465}
466
467bool initOpenCLRunEnv( GPUEnv *gpuInfo )
468{
469 OpenCLZone zone;
470 cl_uint nPreferredVectorWidthFloat;
471 char pName[64];
472
473 bool bKhrFp64 = false;
474 bool bAmdFp64 = false;
475
476 checkDeviceForDoubleSupport(gpuInfo->mpDevID, bKhrFp64, bAmdFp64);
477
478 gpuInfo->mnKhrFp64Flag = bKhrFp64;
479 gpuInfo->mnAmdFp64Flag = bAmdFp64;
480
481 gpuInfo->mbNeedsTDRAvoidance = false;
482
483 clGetDeviceInfo(gpuInfo->mpDevID, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, sizeof(cl_uint),
484 &nPreferredVectorWidthFloat, nullptr);
485 SAL_INFO("opencl", "CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT=" << nPreferredVectorWidthFloat);
486
487 clGetPlatformInfo(gpuInfo->mpPlatformID, CL_PLATFORM_NAME, 64,
488 pName, nullptr);
489
490#if defined (_WIN32)
491// the Win32 SDK 8.1 deprecates GetVersionEx()
492# ifdef _WIN32_WINNT_WINBLUE
493 const bool bIsNotWinOrIsWin8OrGreater = IsWindows8OrGreater();
494# else
495 bool bIsNotWinOrIsWin8OrGreater = true;
496 OSVERSIONINFOW aVersionInfo = {};
497 aVersionInfo.dwOSVersionInfoSize = sizeof( aVersionInfo );
498 if (GetVersionExW( &aVersionInfo ))
499 {
500 // Windows 7 or lower?
501 if (aVersionInfo.dwMajorVersion < 6 ||
502 (aVersionInfo.dwMajorVersion == 6 && aVersionInfo.dwMinorVersion < 2))
503 bIsNotWinOrIsWin8OrGreater = false;
504 }
505# endif
506#else
507 const bool bIsNotWinOrIsWin8OrGreater = true;
508#endif
509
510 // Heuristic: Certain old low-end OpenCL implementations don't
511 // work for us with too large group lengths. Looking at the preferred
512 // float vector width seems to be a way to detect these devices, except
513 // the non-working NVIDIA cards on Windows older than version 8.
514 gpuInfo->mbNeedsTDRAvoidance = ( nPreferredVectorWidthFloat == 4 ) ||
515 ( !bIsNotWinOrIsWin8OrGreater &&
516 OUString::createFromAscii(pName).indexOf("NVIDIA") > -1 );
517
518 size_t nMaxParameterSize;
519 clGetDeviceInfo(gpuInfo->mpDevID, CL_DEVICE_MAX_PARAMETER_SIZE, sizeof(size_t),
520 &nMaxParameterSize, nullptr);
521 SAL_INFO("opencl", "CL_DEVICE_MAX_PARAMETER_SIZE=" << nMaxParameterSize);
522
523 return false;
524}
525
526bool initOpenCLRunEnv( int argc )
527{
528 if ( ( argc > MAX_CLFILE_NUM ) || ( argc < 0 ) )
529 return true;
530
531 if ( !bIsInited )
532 {
533 if ( !gpuEnv.mnIsUserCreated )
534 memset( &gpuEnv, 0, sizeof(gpuEnv) );
535
536 //initialize devices, context, command_queue
537 bool status = initOpenCLRunEnv( &gpuEnv );
538 if ( status )
539 {
540 return true;
541 }
542 //initialize program, kernelName, kernelCount
543 if( getenv( "SC_FLOAT" ) )
544 {
545 gpuEnv.mnKhrFp64Flag = false;
546 gpuEnv.mnAmdFp64Flag = false;
547 }
549 {
550 SAL_INFO("opencl", "Use Khr double");
551 }
552 else if( gpuEnv.mnAmdFp64Flag )
553 {
554 SAL_INFO("opencl", "Use AMD double type");
555 }
556 else
557 {
558 SAL_INFO("opencl", "USE float type");
559 }
560 bIsInited = true;
561 }
562 return false;
563}
564
565// based on crashes and hanging during kernel compilation
566void createDeviceInfo(cl_device_id aDeviceId, OpenCLPlatformInfo& rPlatformInfo)
567{
568 OpenCLDeviceInfo aDeviceInfo;
569 aDeviceInfo.device = aDeviceId;
570
572 cl_int nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_NAME, DEVICE_NAME_LENGTH, pName, nullptr);
573 if(nState != CL_SUCCESS)
574 return;
575
576 aDeviceInfo.maName = OUString::createFromAscii(pName);
577
578 char pVendor[DEVICE_NAME_LENGTH];
579 nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_VENDOR, DEVICE_NAME_LENGTH, pVendor, nullptr);
580 if(nState != CL_SUCCESS)
581 return;
582
583 aDeviceInfo.maVendor = OUString::createFromAscii(pVendor);
584
585 cl_ulong nMemSize;
586 nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(nMemSize), &nMemSize, nullptr);
587 if(nState != CL_SUCCESS)
588 return;
589
590 aDeviceInfo.mnMemory = nMemSize;
591
592 cl_uint nClockFrequency;
593 nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(nClockFrequency), &nClockFrequency, nullptr);
594 if(nState != CL_SUCCESS)
595 return;
596
597 aDeviceInfo.mnFrequency = nClockFrequency;
598
599 cl_uint nComputeUnits;
600 nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(nComputeUnits), &nComputeUnits, nullptr);
601 if(nState != CL_SUCCESS)
602 return;
603
604 char pDriver[DEVICE_NAME_LENGTH];
605 nState = clGetDeviceInfo(aDeviceId, CL_DRIVER_VERSION, DEVICE_NAME_LENGTH, pDriver, nullptr);
606
607 if(nState != CL_SUCCESS)
608 return;
609
610 aDeviceInfo.maDriver = OUString::createFromAscii(pDriver);
611
612 bool bKhrFp64 = false;
613 bool bAmdFp64 = false;
614 checkDeviceForDoubleSupport(aDeviceId, bKhrFp64, bAmdFp64);
615
616 // only list devices that support double
617 if(!bKhrFp64 && !bAmdFp64)
618 return;
619
620 aDeviceInfo.mnComputeUnits = nComputeUnits;
621
622 if(!OpenCLConfig::get().checkImplementation(rPlatformInfo, aDeviceInfo))
623 rPlatformInfo.maDevices.push_back(aDeviceInfo);
624}
625
626bool createPlatformInfo(cl_platform_id nPlatformId, OpenCLPlatformInfo& rPlatformInfo)
627{
628 rPlatformInfo.platform = nPlatformId;
629 char pName[64];
630 cl_int nState = clGetPlatformInfo(nPlatformId, CL_PLATFORM_NAME, 64,
631 pName, nullptr);
632 if(nState != CL_SUCCESS)
633 return false;
634 rPlatformInfo.maName = OUString::createFromAscii(pName);
635
636 char pVendor[64];
637 nState = clGetPlatformInfo(nPlatformId, CL_PLATFORM_VENDOR, 64,
638 pVendor, nullptr);
639 if(nState != CL_SUCCESS)
640 return false;
641
642 rPlatformInfo.maVendor = OUString::createFromAscii(pVendor);
643
644 cl_uint nDevices;
645 nState = clGetDeviceIDs(nPlatformId, CL_DEVICE_TYPE_ALL, 0, nullptr, &nDevices);
646 if(nState != CL_SUCCESS)
647 return false;
648
649 // memory leak that does not matter
650 // memory is stored in static variable that lives through the whole program
651 cl_device_id* pDevices = new cl_device_id[nDevices];
652 nState = clGetDeviceIDs(nPlatformId, CL_DEVICE_TYPE_ALL, nDevices, pDevices, nullptr);
653 if(nState != CL_SUCCESS)
654 return false;
655
656 for(size_t i = 0; i < nDevices; ++i)
657 {
658 createDeviceInfo(pDevices[i], rPlatformInfo);
659 }
660
661 return true;
662}
663
664}
665
666const std::vector<OpenCLPlatformInfo>& fillOpenCLInfo()
667{
668 static std::vector<OpenCLPlatformInfo> aPlatforms;
669
670 // return early if we already initialized or can't use OpenCL
671 if (!aPlatforms.empty() || !canUseOpenCL())
672 return aPlatforms;
673
674 int status = clewInit(OPENCL_DLL_NAME);
675 if (status < 0)
676 return aPlatforms;
677
678 cl_uint nPlatforms;
679 cl_int nState = clGetPlatformIDs(0, nullptr, &nPlatforms);
680
681 if(nState != CL_SUCCESS)
682 return aPlatforms;
683
684 // memory leak that does not matter,
685 // memory is stored in static instance aPlatforms
686 cl_platform_id* pPlatforms = new cl_platform_id[nPlatforms];
687 nState = clGetPlatformIDs(nPlatforms, pPlatforms, nullptr);
688
689 if(nState != CL_SUCCESS)
690 return aPlatforms;
691
692 for(size_t i = 0; i < nPlatforms; ++i)
693 {
694 OpenCLPlatformInfo aPlatformInfo;
695 if(createPlatformInfo(pPlatforms[i], aPlatformInfo))
696 aPlatforms.push_back(aPlatformInfo);
697 }
698
699 return aPlatforms;
700}
701
702namespace {
703
704cl_device_id findDeviceIdByDeviceString(std::u16string_view rString, const std::vector<OpenCLPlatformInfo>& rPlatforms)
705{
706 for (const OpenCLPlatformInfo& rPlatform : rPlatforms)
707 {
708 for (const OpenCLDeviceInfo& rDeviceInfo : rPlatform.maDevices)
709 {
710 OUString aDeviceId = rDeviceInfo.maVendor + " " + rDeviceInfo.maName;
711 if (rString == aDeviceId)
712 {
713 return rDeviceInfo.device;
714 }
715 }
716 }
717
718 return nullptr;
719}
720
721void findDeviceInfoFromDeviceId(cl_device_id aDeviceId, size_t& rDeviceId, size_t& rPlatformId)
722{
723 cl_platform_id platformId;
724 cl_int nState = clGetDeviceInfo(aDeviceId, CL_DEVICE_PLATFORM,
725 sizeof(platformId), &platformId, nullptr);
726
727 if(nState != CL_SUCCESS)
728 return;
729
730 const std::vector<OpenCLPlatformInfo>& rPlatforms = fillOpenCLInfo();
731 for(size_t i = 0; i < rPlatforms.size(); ++i)
732 {
733 cl_platform_id platId = rPlatforms[i].platform;
734 if(platId != platformId)
735 continue;
736
737 for(size_t j = 0; j < rPlatforms[i].maDevices.size(); ++j)
738 {
739 cl_device_id id = rPlatforms[i].maDevices[j].device;
740 if(id == aDeviceId)
741 {
742 rDeviceId = j;
743 rPlatformId = i;
744 return;
745 }
746 }
747 }
748}
749
750}
751
753{
754 if( const char* env = getenv( "SC_FORCE_CALCULATION" ))
755 {
756 if( strcmp( env, "opencl" ) == 0 )
757 return true;
758 }
759 return !getenv("SAL_DISABLE_OPENCL") && officecfg::Office::Common::Misc::UseOpenCL::get();
760}
761
762bool switchOpenCLDevice(std::u16string_view aDevice, bool bAutoSelect, bool bForceEvaluation, OUString& rOutSelectedDeviceVersionIDString)
763{
764 if (!canUseOpenCL() || fillOpenCLInfo().empty())
765 return false;
766
767 cl_device_id pDeviceId = findDeviceIdByDeviceString(aDevice, fillOpenCLInfo());
768
769 if(!pDeviceId || bAutoSelect)
770 {
771 int status = clewInit(OPENCL_DLL_NAME);
772 if (status < 0)
773 return false;
774
775 OUString url(OStringToOUString(getCacheFolder(), RTL_TEXTENCODING_UTF8));
776 OUString path;
777 osl::FileBase::getSystemPathFromFileURL(url,path);
778 ds_device aSelectedDevice = getDeviceSelection(path, bForceEvaluation);
779 if ( aSelectedDevice.eType != DeviceType::OpenCLDevice)
780 return false;
781 pDeviceId = aSelectedDevice.aDeviceID;
782 }
783
784 if(gpuEnv.mpDevID == pDeviceId)
785 {
786 // we don't need to change anything
787 // still the same device
788 return pDeviceId != nullptr;
789 }
790
791 cl_context context;
792 cl_platform_id platformId;
793
794 {
795 OpenCLZone zone;
796 cl_int nState = clGetDeviceInfo(pDeviceId, CL_DEVICE_PLATFORM,
797 sizeof(platformId), &platformId, nullptr);
798
799 cl_context_properties cps[3];
800 cps[0] = CL_CONTEXT_PLATFORM;
801 cps[1] = reinterpret_cast<cl_context_properties>(platformId);
802 cps[2] = 0;
803 context = clCreateContext( cps, 1, &pDeviceId, nullptr, nullptr, &nState );
804 if (nState != CL_SUCCESS)
805 SAL_WARN("opencl", "clCreateContext failed: " << errorString(nState));
806
807 if(nState != CL_SUCCESS || context == nullptr)
808 {
809 if(context != nullptr)
810 clReleaseContext(context);
811
812 SAL_WARN("opencl", "failed to set/switch opencl device");
813 return false;
814 }
815 SAL_INFO("opencl", "Created context " << context << " for platform " << platformId << ", device " << pDeviceId);
816
817 OString sDeviceID = getDeviceInfoString(pDeviceId, CL_DEVICE_VENDOR) + " " + getDeviceInfoString(pDeviceId, CL_DRIVER_VERSION);
818 rOutSelectedDeviceVersionIDString = OStringToOUString(sDeviceID, RTL_TEXTENCODING_UTF8);
819 }
820
821 setOpenCLCmdQueuePosition(0); // Call this just to avoid the method being deleted from unused function deleter.
822
824
825 OpenCLEnv env;
826 env.mpOclPlatformID = platformId;
827 env.mpOclContext = context;
828 env.mpOclDevsID = pDeviceId;
829
830 initOpenCLAttr(&env);
831
832 return !initOpenCLRunEnv(0);
833}
834
835void getOpenCLDeviceInfo(size_t& rDeviceId, size_t& rPlatformId)
836{
837 if (!canUseOpenCL())
838 return;
839
840 int status = clewInit(OPENCL_DLL_NAME);
841 if (status < 0)
842 return;
843
844 cl_device_id id = gpuEnv.mpDevID;
845 findDeviceInfoFromDeviceId(id, rDeviceId, rPlatformId);
846}
847
848void getOpenCLDeviceName(OUString& rDeviceName, OUString& rPlatformName)
849{
850 if (!canUseOpenCL())
851 return;
852
853 int status = clewInit(OPENCL_DLL_NAME);
854 if (status < 0)
855 return;
856
857 cl_device_id deviceId = gpuEnv.mpDevID;
858 cl_platform_id platformId;
859 if( clGetDeviceInfo(deviceId, CL_DEVICE_PLATFORM, sizeof(platformId), &platformId, nullptr) != CL_SUCCESS )
860 return;
861
862 char deviceName[DEVICE_NAME_LENGTH] = {0};
863 if( clGetDeviceInfo(deviceId, CL_DEVICE_NAME, sizeof(deviceName), deviceName, nullptr) != CL_SUCCESS )
864 return;
865 char platformName[64];
866 if( clGetPlatformInfo(platformId, CL_PLATFORM_NAME, 64, platformName, nullptr) != CL_SUCCESS )
867 return;
868 rDeviceName = OUString::createFromAscii(deviceName);
869 rPlatformName = OUString::createFromAscii(platformName);
870}
871
873{
874 if (nPos < 0 || nPos >= OPENCL_CMDQUEUE_SIZE)
875 // Out of range. Ignore this.
876 return;
877
879}
880
881const char* errorString(cl_int nError)
882{
883#define CASE(val) case CL_##val: return #val
884 switch (nError)
885 {
886 CASE(SUCCESS);
887 CASE(DEVICE_NOT_FOUND);
888 CASE(DEVICE_NOT_AVAILABLE);
889 CASE(COMPILER_NOT_AVAILABLE);
890 CASE(MEM_OBJECT_ALLOCATION_FAILURE);
891 CASE(OUT_OF_RESOURCES);
892 CASE(OUT_OF_HOST_MEMORY);
893 CASE(PROFILING_INFO_NOT_AVAILABLE);
894 CASE(MEM_COPY_OVERLAP);
895 CASE(IMAGE_FORMAT_MISMATCH);
896 CASE(IMAGE_FORMAT_NOT_SUPPORTED);
897 CASE(BUILD_PROGRAM_FAILURE);
898 CASE(MAP_FAILURE);
899 CASE(INVALID_VALUE);
900 CASE(INVALID_DEVICE_TYPE);
901 CASE(INVALID_PLATFORM);
902 CASE(INVALID_DEVICE);
903 CASE(INVALID_CONTEXT);
904 CASE(INVALID_QUEUE_PROPERTIES);
905 CASE(INVALID_COMMAND_QUEUE);
906 CASE(INVALID_HOST_PTR);
907 CASE(INVALID_MEM_OBJECT);
908 CASE(INVALID_IMAGE_FORMAT_DESCRIPTOR);
909 CASE(INVALID_IMAGE_SIZE);
910 CASE(INVALID_SAMPLER);
911 CASE(INVALID_BINARY);
912 CASE(INVALID_BUILD_OPTIONS);
913 CASE(INVALID_PROGRAM);
914 CASE(INVALID_PROGRAM_EXECUTABLE);
915 CASE(INVALID_KERNEL_NAME);
916 CASE(INVALID_KERNEL_DEFINITION);
917 CASE(INVALID_KERNEL);
918 CASE(INVALID_ARG_INDEX);
919 CASE(INVALID_ARG_VALUE);
920 CASE(INVALID_ARG_SIZE);
921 CASE(INVALID_KERNEL_ARGS);
922 CASE(INVALID_WORK_DIMENSION);
923 CASE(INVALID_WORK_GROUP_SIZE);
924 CASE(INVALID_WORK_ITEM_SIZE);
925 CASE(INVALID_GLOBAL_OFFSET);
926 CASE(INVALID_EVENT_WAIT_LIST);
927 CASE(INVALID_EVENT);
928 CASE(INVALID_OPERATION);
929 CASE(INVALID_GL_OBJECT);
930 CASE(INVALID_BUFFER_SIZE);
931 CASE(INVALID_MIP_LEVEL);
932 CASE(INVALID_GLOBAL_WORK_SIZE);
933 default:
934 return "Unknown OpenCL error code";
935 }
936#undef CASE
937}
938
940{
941 return gpuEnv.mpDevID && gpuEnv.mpContext;
942}
943
944}
945
947{
948 OpenCLZone zone;
949
950 if ( !bIsInited )
951 {
952 return;
953 }
954
955 for (_cl_command_queue* & i : openclwrapper::gpuEnv.mpCmdQueue)
956 {
957 if (i)
958 {
959 clReleaseCommandQueue(i);
960 i = nullptr;
961 }
962 }
964
966 {
967 clReleaseContext( openclwrapper::gpuEnv.mpContext );
969 }
970 bIsInited = false;
971 gpuInfo->mnIsUserCreated = 0;
972}
973
974/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const char * pName
#define SAL_CONFIGFILE(name)
sal_Int32 nState
const sal_uInt16 idx[]
sal_Int32 nIndex
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
const css::uno::Reference< css::xml::crypto::XSecurityEnvironment > & env
std::unique_ptr< sal_Int32[]> pData
int i
bool switchOpenCLDevice(std::u16string_view aDevice, bool bAutoSelect, bool bForceEvaluation, OUString &rOutSelectedDeviceVersionIDString)
Used to set or switch between OpenCL devices.
const char * errorString(cl_int nError)
Return a textual representation of an OpenCL error code.
static bool initializeCommandQueue(GPUEnv &aGpuEnv)
const std::vector< OpenCLPlatformInfo > & fillOpenCLInfo()
void setOpenCLCmdQueuePosition(int nPos)
Set the current command queue position in case of multiple command queues for a given device.
void getOpenCLDeviceName(OUString &rDeviceName, OUString &rPlatformName)
bool buildProgramFromBinary(const char *buildOption, GPUEnv *gpuInfo, const char *filename, int idx)
void setKernelEnv(KernelEnv *envInfo)
sal_uInt64 kernelFailures
bool generatBinFromKernelSource(cl_program program, const char *clFileName)
void getOpenCLDeviceInfo(size_t &rDeviceId, size_t &rPlatformId)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
ds_device const & getDeviceSelection(std::u16string_view sProfilePath, bool bForceSelection)
OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
#define OPENCL_DLL_NAME
cl_device_id mpOclDevsID
#define CASE(val)
cl_context mpOclContext
#define PLATFORM_VERSION_LENGTH
#define DEVICE_NAME_LENGTH
#define DRIVER_VERSION_LENGTH
void releaseOpenCLEnv(openclwrapper::GPUEnv *gpuInfo)
cl_platform_id mpOclPlatformID
#define CHECK_OPENCL(status, name)
#define MAX_CLFILE_NUM
#define OPENCL_CMDQUEUE_SIZE
ParserContextSharedPtr mpContext
static OpenCLConfig get()
cl_device_id device
cl_platform_id platform
std::vector< OpenCLDeviceInfo > maDevices
cl_device_id aDeviceID
cl_program mpArryPrograms[MAX_CLFILE_NUM]
cl_platform_id mpPlatformID
static bool isOpenCLEnabled()
cl_command_queue mpCmdQueue[OPENCL_CMDQUEUE_SIZE]
cl_command_queue mpkCmdQueue
unsigned char sal_uInt8
std::unique_ptr< char[]> aBuffer