summaryrefslogtreecommitdiffstats
path: root/desktop/source/app/opencl.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /desktop/source/app/opencl.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'desktop/source/app/opencl.cxx')
-rw-r--r--desktop/source/app/opencl.cxx257
1 files changed, 257 insertions, 0 deletions
diff --git a/desktop/source/app/opencl.cxx b/desktop/source/app/opencl.cxx
new file mode 100644
index 000000000..fb5c3723e
--- /dev/null
+++ b/desktop/source/app/opencl.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/*
+ * This module exists to validate the OpenCL implementation,
+ * where necessary during startup; and before we load or
+ * calculate using OpenCL.
+ */
+
+#include <app.hxx>
+
+#include <config_version.h>
+#include <config_feature_opencl.h>
+#include <config_folders.h>
+
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svl/documentlockfile.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/table/XCell2.hpp>
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheets.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+
+#if HAVE_FEATURE_OPENCL
+#include <opencl/openclwrapper.hxx>
+#endif
+#include <opencl/OpenCLZone.hxx>
+
+#include <osl/file.hxx>
+#include <osl/process.h>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace desktop {
+
+#if HAVE_FEATURE_OPENCL
+
+static bool testOpenCLDriver()
+{
+ // A simple OpenCL test run in a separate process in order to test
+ // whether the driver crashes (asserts,etc.) when trying to use OpenCL.
+ SAL_INFO("opencl", "Starting CL driver test");
+
+ OUString testerURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/opencltest");
+ rtl::Bootstrap::expandMacros(testerURL); //TODO: detect failure
+
+ OUString deviceName, platformName;
+ openclwrapper::getOpenCLDeviceName( deviceName, platformName );
+ rtl_uString* args[] = { deviceName.pData, platformName.pData };
+ sal_Int32 numArgs = 2;
+
+ oslProcess process;
+ oslSecurity security = osl_getCurrentSecurity();
+ oslProcessError error = osl_executeProcess(testerURL.pData, args, numArgs,
+ osl_Process_SEARCHPATH | osl_Process_HIDDEN, security,
+ nullptr, nullptr, 0, &process );
+ osl_freeSecurityHandle( security );
+ if( error != osl_Process_E_None )
+ {
+ SAL_WARN( "opencl", "failed to start CL driver test: " << error );
+ return false;
+ }
+ // If the driver takes more than 10 seconds, it's probably broken/useless.
+ TimeValue timeout( 10, 0 );
+ error = osl_joinProcessWithTimeout( process, &timeout );
+ if( error == osl_Process_E_None )
+ {
+ oslProcessInfo info;
+ info.Size = sizeof( info );
+ error = osl_getProcessInfo( process, osl_Process_EXITCODE, &info );
+ if( error == osl_Process_E_None )
+ {
+ if( info.Code == 0 )
+ {
+ SAL_INFO( "opencl", "CL driver test passed" );
+ osl_freeProcessHandle( process );
+ return true;
+ }
+ else
+ {
+ SAL_WARN( "opencl", "CL driver test failed - disabling: " << info.Code );
+ osl_freeProcessHandle( process );
+ return false;
+ }
+ }
+ }
+ SAL_WARN( "opencl", "CL driver test did not finish - disabling: " << error );
+ osl_terminateProcess( process );
+ osl_freeProcessHandle( process );
+ return false;
+}
+
+static bool testOpenCLCompute(const Reference< XDesktop2 > &xDesktop, const OUString &rURL)
+{
+ bool bSuccess = false;
+ css::uno::Reference< css::lang::XComponent > xComponent;
+
+ sal_uInt64 nKernelFailures = openclwrapper::kernelFailures;
+
+ SAL_INFO("opencl", "Starting CL test spreadsheet");
+
+ // A stale lock file would make the loading fail, so make sure to remove it.
+ try {
+ ::svt::DocumentLockFile lockFile( rURL );
+ lockFile.RemoveFileDirectly();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ try {
+ css::uno::Reference< css::frame::XComponentLoader > xLoader(xDesktop, css::uno::UNO_QUERY_THROW);
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue("Hidden",
+ true) };
+
+ xComponent.set(xLoader->loadComponentFromURL(rURL, "_blank", 0, aArgs));
+
+ // What an unpleasant API to use.
+ css::uno::Reference< css::sheet::XCalculatable > xCalculatable( xComponent, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::sheet::XSpreadsheetDocument > xSpreadDoc( xComponent, css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), css::uno::UNO_SET_THROW );
+ css::uno::Reference< css::container::XIndexAccess > xIndex( xSheets, css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::sheet::XSpreadsheet > xSheet( xIndex->getByIndex(0), css::uno::UNO_QUERY_THROW);
+
+ // So we insert our MAX call at the end on a named range.
+ css::uno::Reference< css::table::XCell2 > xThresh( xSheet->getCellByPosition(1,1), css::uno::UNO_QUERY_THROW ); // B2
+ double fThreshold = xThresh->getValue();
+
+ // We need pure OCL formulae all the way through the
+ // dependency chain, or we fall-back.
+ xCalculatable->calculateAll();
+
+ // So we insert our MAX call at the end on a named range.
+ css::uno::Reference< css::table::XCell2 > xCell( xSheet->getCellByPosition(1,0), css::uno::UNO_QUERY_THROW );
+ xCell->setFormula("=MAX(results)");
+ double fResult = xCell->getValue();
+
+ // Ensure the maximum variance is below our tolerance.
+ if (fResult > fThreshold)
+ {
+ SAL_WARN("opencl", "OpenCL results unstable - disabling; result: "
+ << fResult << " vs. " << fThreshold);
+ }
+ else
+ {
+ SAL_INFO("opencl", "calculating smoothly; result: " << fResult);
+ bSuccess = true;
+ }
+ }
+ catch (const css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("opencl", "OpenCL testing failed - disabling");
+ }
+
+ if (nKernelFailures != openclwrapper::kernelFailures)
+ {
+ // tdf#100883 - defeat SEH exception handling fallbacks.
+ SAL_WARN("opencl", "OpenCL kernels failed to compile, "
+ "or took SEH exceptions "
+ << nKernelFailures << " != " << openclwrapper::kernelFailures);
+ bSuccess = false;
+ }
+
+ if (!bSuccess)
+ OpenCLZone::hardDisable();
+ if (xComponent.is())
+ xComponent->dispose();
+
+
+ return bSuccess;
+}
+
+void Desktop::CheckOpenCLCompute(const Reference< XDesktop2 > &xDesktop)
+{
+ if (!openclwrapper::canUseOpenCL() || Application::IsSafeModeEnabled())
+ return;
+
+ SAL_INFO("opencl", "Initiating test of OpenCL device");
+ OpenCLZone aZone;
+ OpenCLInitialZone aInitialZone;
+
+ OUString aDevice = officecfg::Office::Calc::Formula::Calculation::OpenCLDevice::get();
+ OUString aSelectedCLDeviceVersionID;
+ if (!openclwrapper::switchOpenCLDevice(
+ &aDevice,
+ officecfg::Office::Calc::Formula::Calculation::OpenCLAutoSelect::get(),
+ false /* bForceEvaluation */,
+ aSelectedCLDeviceVersionID))
+ {
+ SAL_WARN("opencl", "Failed to initialize OpenCL for test");
+ OpenCLZone::hardDisable();
+ return;
+ }
+
+ // Append our app version as well.
+ aSelectedCLDeviceVersionID += "--" LIBO_VERSION_DOTTED;
+
+ // Append timestamp of the file.
+ OUString aURL("$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/opencl/cl-test.ods");
+ rtl::Bootstrap::expandMacros(aURL);
+
+ DirectoryItem aItem;
+ (void)DirectoryItem::get( aURL, aItem );
+ FileStatus aFileStatus( osl_FileStatus_Mask_ModifyTime );
+ (void)aItem.getFileStatus( aFileStatus );
+ TimeValue aTimeVal = aFileStatus.getModifyTime();
+ aSelectedCLDeviceVersionID += "--" +
+ OUString::number(aTimeVal.Seconds);
+
+ if (aSelectedCLDeviceVersionID == officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::get())
+ return;
+
+ // OpenCL device changed - sanity check it and disable if bad.
+
+ sal_Int32 nOrigMinimumSize = officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get();
+ { // set the minimum group size to something small for quick testing.
+ std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(3 /* small */, xBatch);
+ xBatch->commit();
+ }
+
+ // Hopefully at least basic functionality always works and broken OpenCL implementations break
+ // only when they are used to compute something. If this assumptions turns out to be not true,
+ // the driver check needs to be moved sooner.
+ bool bSucceeded = testOpenCLDriver() && testOpenCLCompute(xDesktop, aURL);
+
+ { // restore the minimum group size
+ std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(nOrigMinimumSize, xBatch);
+ officecfg::Office::Common::Misc::SelectedOpenCLDeviceIdentifier::set(aSelectedCLDeviceVersionID, xBatch);
+ xBatch->commit();
+ }
+
+ if (!bSucceeded)
+ OpenCLZone::hardDisable();
+}
+#endif // HAVE_FEATURE_OPENCL
+
+} // end namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */