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 <comphelper/propertyvalue.hxx>
28 #include <svl/documentlockfile.hxx>
29 #include <comphelper/diagnose_ex.hxx>
31 #include <com/sun/star/table/XCell2.hpp>
32 #include <com/sun/star/sheet/XCalculatable.hpp>
33 #include <com/sun/star/sheet/XSpreadsheet.hpp>
34 #include <com/sun/star/sheet/XSpreadsheets.hpp>
35 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
37 #if HAVE_FEATURE_OPENCL
38 #include <opencl/openclwrapper.hxx>
40 #include <opencl/OpenCLZone.hxx>
42 #include <osl/file.hxx>
43 #include <osl/process.h>
45 using namespace ::osl
;
46 using namespace ::com::sun::star::uno
;
47 using namespace ::com::sun::star::frame
;
51 #if HAVE_FEATURE_OPENCL
53 static bool testOpenCLDriver()
55 // A simple OpenCL test run in a separate process in order to test
56 // whether the driver crashes (asserts,etc.) when trying to use OpenCL.
57 SAL_INFO("opencl", "Starting CL driver test");
59 OUString
testerURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER
"/opencltest");
60 rtl::Bootstrap::expandMacros(testerURL
); //TODO: detect failure
62 OUString deviceName
, platformName
;
63 openclwrapper::getOpenCLDeviceName( deviceName
, platformName
);
64 rtl_uString
* args
[] = { deviceName
.pData
, platformName
.pData
};
65 sal_Int32 numArgs
= 2;
68 oslSecurity security
= osl_getCurrentSecurity();
69 oslProcessError error
= osl_executeProcess(testerURL
.pData
, args
, numArgs
,
70 osl_Process_SEARCHPATH
| osl_Process_HIDDEN
, security
,
71 nullptr, nullptr, 0, &process
);
72 osl_freeSecurityHandle( security
);
73 if( error
!= osl_Process_E_None
)
75 SAL_WARN( "opencl", "failed to start CL driver test: " << error
);
78 // If the driver takes more than 10 seconds, it's probably broken/useless.
79 TimeValue
timeout( 10, 0 );
80 error
= osl_joinProcessWithTimeout( process
, &timeout
);
81 if( error
== osl_Process_E_None
)
84 info
.Size
= sizeof( info
);
85 error
= osl_getProcessInfo( process
, osl_Process_EXITCODE
, &info
);
86 if( error
== osl_Process_E_None
)
90 SAL_INFO( "opencl", "CL driver test passed" );
91 osl_freeProcessHandle( process
);
96 SAL_WARN( "opencl", "CL driver test failed - disabling: " << info
.Code
);
97 osl_freeProcessHandle( process
);
102 SAL_WARN( "opencl", "CL driver test did not finish - disabling: " << error
);
103 osl_terminateProcess( process
);
104 osl_freeProcessHandle( process
);
108 static bool testOpenCLCompute(const Reference
< XDesktop2
> &xDesktop
, const OUString
&rURL
)
110 bool bSuccess
= false;
111 css::uno::Reference
< css::lang::XComponent
> xComponent
;
113 sal_uInt64 nKernelFailures
= openclwrapper::kernelFailures
;
115 SAL_INFO("opencl", "Starting CL test spreadsheet");
117 // A stale lock file would make the loading fail, so make sure to remove it.
119 ::svt::DocumentLockFile
lockFile( rURL
);
120 lockFile
.RemoveFileDirectly();
122 catch (const css::uno::Exception
&)
127 css::uno::Reference
< css::frame::XComponentLoader
> xLoader(xDesktop
, css::uno::UNO_QUERY_THROW
);
129 css::uno::Sequence
< css::beans::PropertyValue
> aArgs
{ comphelper::makePropertyValue("Hidden",
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 OpenCLInitialZone aInitialZone
;
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())
229 // OpenCL device changed - sanity check it and disable if bad.
231 sal_Int32 nOrigMinimumSize
= officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get();
232 { // set the minimum group size to something small for quick testing.
233 std::shared_ptr
<comphelper::ConfigurationChanges
> xBatch(comphelper::ConfigurationChanges::create());
234 officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(3 /* small */, xBatch
);
238 // Hopefully at least basic functionality always works and broken OpenCL implementations break
239 // only when they are used to compute something. If this assumptions turns out to be not true,
240 // the driver check needs to be moved sooner.
241 bool bSucceeded
= testOpenCLDriver() && testOpenCLCompute(xDesktop
, aURL
);
243 { // restore the minimum group size
244 std::shared_ptr
<comphelper::ConfigurationChanges
> xBatch(comphelper::ConfigurationChanges::create());
245 officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(nOrigMinimumSize
, xBatch
);
246 officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::set(aSelectedCLDeviceVersionID
, xBatch
);
251 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: */