1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 * This module exists to validate the OpenCL implementation,
11 * where necessary during startup; and before we load or
12 * calculate using OpenCL.
17 #include <config_version.h>
18 #include <config_feature_opencl.h>
19 #include <config_folders.h>
21 #include <rtl/bootstrap.hxx>
22 #include <sal/log.hxx>
24 #include <officecfg/Office/Calc.hxx>
25 #include <officecfg/Office/Common.hxx>
27 #include <svl/documentlockfile.hxx>
28 #include <tools/diagnose_ex.h>
30 #include <com/sun/star/table/XCell2.hpp>
31 #include <com/sun/star/sheet/XCalculatable.hpp>
32 #include <com/sun/star/sheet/XSpreadsheet.hpp>
33 #include <com/sun/star/sheet/XSpreadsheets.hpp>
34 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
36 #if HAVE_FEATURE_OPENCL
37 #include <opencl/openclwrapper.hxx>
39 #include <opencl/OpenCLZone.hxx>
41 #include <osl/file.hxx>
42 #include <osl/process.h>
44 using namespace ::osl
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::frame
;
50 #if HAVE_FEATURE_OPENCL
52 static bool testOpenCLDriver()
54 // A simple OpenCL test run in a separate process in order to test
55 // whether the driver crashes (asserts,etc.) when trying to use OpenCL.
56 SAL_INFO("opencl", "Starting CL driver test");
58 OUString
testerURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER
"/opencltest");
59 rtl::Bootstrap::expandMacros(testerURL
); //TODO: detect failure
61 OUString deviceName
, platformName
;
62 openclwrapper::getOpenCLDeviceName( deviceName
, platformName
);
63 rtl_uString
* args
[] = { deviceName
.pData
, platformName
.pData
};
64 sal_Int32 numArgs
= 2;
67 oslSecurity security
= osl_getCurrentSecurity();
68 oslProcessError error
= osl_executeProcess(testerURL
.pData
, args
, numArgs
,
69 osl_Process_SEARCHPATH
| osl_Process_HIDDEN
, security
,
70 nullptr, nullptr, 0, &process
);
71 osl_freeSecurityHandle( security
);
72 if( error
!= osl_Process_E_None
)
74 SAL_WARN( "opencl", "failed to start CL driver test: " << error
);
77 // If the driver takes more than 10 seconds, it's probably broken/useless.
78 TimeValue
timeout( 10, 0 );
79 error
= osl_joinProcessWithTimeout( process
, &timeout
);
80 if( error
== osl_Process_E_None
)
83 info
.Size
= sizeof( info
);
84 error
= osl_getProcessInfo( process
, osl_Process_EXITCODE
, &info
);
85 if( error
== osl_Process_E_None
)
89 SAL_INFO( "opencl", "CL driver test passed" );
90 osl_freeProcessHandle( process
);
95 SAL_WARN( "opencl", "CL driver test failed - disabling: " << info
.Code
);
96 osl_freeProcessHandle( process
);
101 SAL_WARN( "opencl", "CL driver test did not finish - disabling: " << error
);
102 osl_terminateProcess( process
);
103 osl_freeProcessHandle( process
);
107 static bool testOpenCLCompute(const Reference
< XDesktop2
> &xDesktop
, const OUString
&rURL
)
109 bool bSuccess
= false;
110 css::uno::Reference
< css::lang::XComponent
> xComponent
;
112 sal_uInt64 nKernelFailures
= openclwrapper::kernelFailures
;
114 SAL_INFO("opencl", "Starting CL test spreadsheet");
116 // A stale lock file would make the loading fail, so make sure to remove it.
118 ::svt::DocumentLockFile
lockFile( rURL
);
119 lockFile
.RemoveFileDirectly();
121 catch (const css::uno::Exception
&)
126 css::uno::Reference
< css::frame::XComponentLoader
> xLoader(xDesktop
, css::uno::UNO_QUERY_THROW
);
128 css::uno::Sequence
< css::beans::PropertyValue
> aArgs(1);
129 aArgs
[0].Name
= "Hidden";
130 aArgs
[0].Value
<<= true;
132 xComponent
.set(xLoader
->loadComponentFromURL(rURL
, "_blank", 0, aArgs
));
134 // What an unpleasant API to use.
135 css::uno::Reference
< css::sheet::XCalculatable
> xCalculatable( xComponent
, css::uno::UNO_QUERY_THROW
);
136 css::uno::Reference
< css::sheet::XSpreadsheetDocument
> xSpreadDoc( xComponent
, css::uno::UNO_QUERY_THROW
);
137 css::uno::Reference
< css::sheet::XSpreadsheets
> xSheets( xSpreadDoc
->getSheets(), css::uno::UNO_SET_THROW
);
138 css::uno::Reference
< css::container::XIndexAccess
> xIndex( xSheets
, css::uno::UNO_QUERY_THROW
);
139 css::uno::Reference
< css::sheet::XSpreadsheet
> xSheet( xIndex
->getByIndex(0), css::uno::UNO_QUERY_THROW
);
141 // So we insert our MAX call at the end on a named range.
142 css::uno::Reference
< css::table::XCell2
> xThresh( xSheet
->getCellByPosition(1,1), css::uno::UNO_QUERY_THROW
); // B2
143 double fThreshold
= xThresh
->getValue();
145 // We need pure OCL formulae all the way through the
146 // dependency chain, or we fall-back.
147 xCalculatable
->calculateAll();
149 // So we insert our MAX call at the end on a named range.
150 css::uno::Reference
< css::table::XCell2
> xCell( xSheet
->getCellByPosition(1,0), css::uno::UNO_QUERY_THROW
);
151 xCell
->setFormula("=MAX(results)");
152 double fResult
= xCell
->getValue();
154 // Ensure the maximum variance is below our tolerance.
155 if (fResult
> fThreshold
)
157 SAL_WARN("opencl", "OpenCL results unstable - disabling; result: "
158 << fResult
<< " vs. " << fThreshold
);
162 SAL_INFO("opencl", "calculating smoothly; result: " << fResult
);
166 catch (const css::uno::Exception
&)
168 TOOLS_WARN_EXCEPTION("opencl", "OpenCL testing failed - disabling");
171 if (nKernelFailures
!= openclwrapper::kernelFailures
)
173 // tdf#100883 - defeat SEH exception handling fallbacks.
174 SAL_WARN("opencl", "OpenCL kernels failed to compile, "
175 "or took SEH exceptions "
176 << nKernelFailures
<< " != " << openclwrapper::kernelFailures
);
181 OpenCLZone::hardDisable();
183 xComponent
->dispose();
189 void Desktop::CheckOpenCLCompute(const Reference
< XDesktop2
> &xDesktop
)
191 if (!openclwrapper::canUseOpenCL() || Application::IsSafeModeEnabled())
194 SAL_INFO("opencl", "Initiating test of OpenCL device");
196 OpenCLZone::enterInitialTest();
198 OUString aDevice
= officecfg::Office::Calc::Formula::Calculation::OpenCLDevice::get();
199 OUString aSelectedCLDeviceVersionID
;
200 if (!openclwrapper::switchOpenCLDevice(
202 officecfg::Office::Calc::Formula::Calculation::OpenCLAutoSelect::get(),
203 false /* bForceEvaluation */,
204 aSelectedCLDeviceVersionID
))
206 SAL_WARN("opencl", "Failed to initialize OpenCL for test");
207 OpenCLZone::hardDisable();
211 // Append our app version as well.
212 aSelectedCLDeviceVersionID
+= "--" LIBO_VERSION_DOTTED
;
214 // Append timestamp of the file.
215 OUString
aURL("$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
"/opencl/cl-test.ods");
216 rtl::Bootstrap::expandMacros(aURL
);
219 (void)DirectoryItem::get( aURL
, aItem
);
220 FileStatus
aFileStatus( osl_FileStatus_Mask_ModifyTime
);
221 (void)aItem
.getFileStatus( aFileStatus
);
222 TimeValue aTimeVal
= aFileStatus
.getModifyTime();
223 aSelectedCLDeviceVersionID
+= "--" +
224 OUString::number(aTimeVal
.Seconds
);
226 if (aSelectedCLDeviceVersionID
!= officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::get())
228 // OpenCL device changed - sanity check it and disable if bad.
230 boost::optional
<sal_Int32
> nOrigMinimumSize
= officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get();
231 { // set the minimum group size to something small for quick testing.
232 std::shared_ptr
<comphelper::ConfigurationChanges
> xBatch(comphelper::ConfigurationChanges::create());
233 officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(3 /* small */, xBatch
);
237 // Hopefully at least basic functionality always works and broken OpenCL implementations break
238 // only when they are used to compute something. If this assumptions turns out to be not true,
239 // the driver check needs to be moved sooner.
240 bool bSucceeded
= testOpenCLDriver() && testOpenCLCompute(xDesktop
, aURL
);
242 { // restore the minimum group size
243 std::shared_ptr
<comphelper::ConfigurationChanges
> xBatch(comphelper::ConfigurationChanges::create());
244 officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(nOrigMinimumSize
, xBatch
);
245 officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::set(aSelectedCLDeviceVersionID
, xBatch
);
250 OpenCLZone::hardDisable();
253 #endif // HAVE_FEATURE_OPENCL
255 } // end namespace desktop
257 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */