Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / app / opencl.cxx
blob5ef73dd70cf9ecae7a23ee5071dee63e031c51f5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
9 /*
10 * This module exists to validate the OpenCL implementation,
11 * where necessary during startup; and before we load or
12 * calculate using OpenCL.
15 #include <app.hxx>
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>
38 #endif
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;
48 namespace desktop {
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;
66 oslProcess process;
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 );
75 return false;
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 )
82 oslProcessInfo info;
83 info.Size = sizeof( info );
84 error = osl_getProcessInfo( process, osl_Process_EXITCODE, &info );
85 if( error == osl_Process_E_None )
87 if( info.Code == 0 )
89 SAL_INFO( "opencl", "CL driver test passed" );
90 osl_freeProcessHandle( process );
91 return true;
93 else
95 SAL_WARN( "opencl", "CL driver test failed - disabling: " << info.Code );
96 osl_freeProcessHandle( process );
97 return false;
101 SAL_WARN( "opencl", "CL driver test did not finish - disabling: " << error );
102 osl_terminateProcess( process );
103 osl_freeProcessHandle( process );
104 return false;
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.
117 try {
118 ::svt::DocumentLockFile lockFile( rURL );
119 lockFile.RemoveFileDirectly();
121 catch (const css::uno::Exception&)
125 try {
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);
160 else
162 SAL_INFO("opencl", "calculating smoothly; result: " << fResult);
163 bSuccess = true;
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);
177 bSuccess = false;
180 if (!bSuccess)
181 OpenCLZone::hardDisable();
182 if (xComponent.is())
183 xComponent->dispose();
186 return bSuccess;
189 void Desktop::CheckOpenCLCompute(const Reference< XDesktop2 > &xDesktop)
191 if (!openclwrapper::canUseOpenCL() || Application::IsSafeModeEnabled())
192 return;
194 SAL_INFO("opencl", "Initiating test of OpenCL device");
195 OpenCLZone aZone;
196 OpenCLZone::enterInitialTest();
198 OUString aDevice = officecfg::Office::Calc::Formula::Calculation::OpenCLDevice::get();
199 OUString aSelectedCLDeviceVersionID;
200 if (!openclwrapper::switchOpenCLDevice(
201 &aDevice,
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();
208 return;
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);
218 DirectoryItem aItem;
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);
234 xBatch->commit();
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);
246 xBatch->commit();
249 if (!bSucceeded)
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: */