257 lines
9.4 KiB
C++
257 lines
9.4 KiB
C++
/* -*- 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 <comphelper/diagnose_ex.hxx>
|
|
|
|
#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(u"$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/opencltest"_ustr);
|
|
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(u"Hidden"_ustr,
|
|
true) };
|
|
|
|
xComponent.set(xLoader->loadComponentFromURL(rURL, u"_blank"_ustr, 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(u"=MAX(results)"_ustr);
|
|
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(u"$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/opencl/cl-test.ods"_ustr);
|
|
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: */
|