mirror of
https://github.com/OPM/ResInsight.git
synced 2026-11-13 09:37:29 -06:00
#15098: Add headless mode with software OpenGL rendering
This commit is contained in:
1 parent
7b5a977401
commit
278248b71d
10 files changed
+280
-35
No files matched your search
@@ -239,6 +239,14 @@ if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(ResInsight PRIVATE xcb)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Delay-load opengl32.dll so that the statically linked OpenGL calls can be
|
||||
# redirected to the software rasterizer opengl32sw.dll in headless mode, see
|
||||
# riaDelayLoadHook in RiaMainTools.cpp
|
||||
target_link_libraries(ResInsight PRIVATE delayimp)
|
||||
target_link_options(ResInsight PRIVATE /DELAYLOAD:opengl32.dll)
|
||||
endif()
|
||||
|
||||
# ##############################################################################
|
||||
# Unity builds
|
||||
# ##############################################################################
|
||||
@@ -421,6 +429,14 @@ if(RESINSIGHT_PRIVATE_INSTALL)
|
||||
RUNTIME_DEPENDENCIES PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
|
||||
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
|
||||
)
|
||||
|
||||
# The software OpenGL rasterizer is loaded dynamically (used by --headless),
|
||||
# so it is not picked up by the RUNTIME_DEPENDENCIES scan above
|
||||
install(
|
||||
FILES $<TARGET_FILE_DIR:ResInsight>/opengl32sw.dll
|
||||
DESTINATION ${RESINSIGHT_INSTALL_FOLDER}
|
||||
OPTIONAL
|
||||
)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
|
||||
@@ -86,6 +86,7 @@ void riaFilteredMessageHandler( QtMsgType type, const QMessageLogContext& contex
|
||||
|
||||
RiaApplication* createApplication( int& argc, char* argv[] )
|
||||
{
|
||||
bool isHeadless = false;
|
||||
for ( int i = 1; i < argc; ++i )
|
||||
{
|
||||
if ( !qstrcmp( argv[i], "--console" ) || !qstrcmp( argv[i], "--unittest" ) )
|
||||
@@ -96,7 +97,18 @@ RiaApplication* createApplication( int& argc, char* argv[] )
|
||||
return new RiaConsoleApplication( argc, argv );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( !qstrcmp( argv[i], "--headless" ) )
|
||||
{
|
||||
isHeadless = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isHeadless )
|
||||
{
|
||||
RiaMainTools::enableHeadlessRendering();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_GRPC
|
||||
return new RiaGrpcGuiApplication( argc, argv );
|
||||
#else
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "RiaMainTools.h"
|
||||
#include "RiaBaseDefs.h"
|
||||
#include "RiaFileLogger.h"
|
||||
#include "RiaGuiApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaOpenTelemetryManager.h"
|
||||
#include "RiaPreferencesOpenTelemetry.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
@@ -73,6 +75,74 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#include <delayimp.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// In headless mode Qt creates its OpenGL context using the software rasterizer opengl32sw.dll
|
||||
/// (Qt::AA_UseSoftwareOpenGL), while the statically linked OpenGL calls in the visualization
|
||||
/// framework would go to the system opengl32.dll where no context is current, causing an endless
|
||||
/// loop in glGetError(). opengl32.dll is therefore delay-loaded (see CMakeLists.txt) and redirected
|
||||
/// here to the same software rasterizer.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
FARPROC WINAPI riaDelayLoadHook( unsigned dliNotify, PDelayLoadInfo pdli )
|
||||
{
|
||||
if ( dliNotify == dliNotePreLoadLibrary && RiaGuiApplication::isHeadless() &&
|
||||
_stricmp( pdli->szDll, "opengl32.dll" ) == 0 )
|
||||
{
|
||||
if ( HMODULE softwareOpenGl = LoadLibraryW( L"opengl32sw.dll" ) )
|
||||
{
|
||||
return reinterpret_cast<FARPROC>( softwareOpenGl );
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" const PfnDliHook __pfnDliNotifyHook2 = riaDelayLoadHook;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// ResInsight is built as a GUI application on Windows, so stdout is only available when redirected to a file or
|
||||
/// pipe. Attach to the console of the launching process (e.g. PowerShell or cmd) so headless log output is visible.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void riaAttachToParentConsole()
|
||||
{
|
||||
// Only files and pipes are real redirections. A GUI process launched from a console (e.g. Windows Terminal) can
|
||||
// inherit the console handles of the parent without being attached to the console, and writing to those blocks
|
||||
auto isRedirected = []( DWORD stdHandle )
|
||||
{
|
||||
HANDLE handle = GetStdHandle( stdHandle );
|
||||
if ( handle == nullptr || handle == INVALID_HANDLE_VALUE ) return false;
|
||||
|
||||
const DWORD fileType = GetFileType( handle );
|
||||
return fileType == FILE_TYPE_DISK || fileType == FILE_TYPE_PIPE;
|
||||
};
|
||||
|
||||
const bool stdOutRedirected = isRedirected( STD_OUTPUT_HANDLE );
|
||||
const bool stdErrRedirected = isRedirected( STD_ERROR_HANDLE );
|
||||
if ( stdOutRedirected && stdErrRedirected ) return;
|
||||
|
||||
if ( !AttachConsole( ATTACH_PARENT_PROCESS ) ) return;
|
||||
|
||||
FILE* stream = nullptr;
|
||||
if ( !stdOutRedirected ) freopen_s( &stream, "CONOUT$", "w", stdout );
|
||||
if ( !stdErrRedirected ) freopen_s( &stream, "CONOUT$", "w", stderr );
|
||||
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace internal
|
||||
{
|
||||
#if RIA_HAS_STD_STACKTRACE
|
||||
@@ -516,4 +586,24 @@ void deleteStaleSettingsLockFiles()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Configure software OpenGL rendering for headless mode, allowing 3D views to be rendered and
|
||||
/// grabbed without a GPU and without showing any windows. Must be called before the QApplication
|
||||
/// object is created.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void enableHeadlessRendering()
|
||||
{
|
||||
// Use software OpenGL rendering so 3D views can be rendered without a GPU. On Windows this
|
||||
// makes Qt load opengl32sw.dll (Mesa llvmpipe). Must be set before the QApplication exists.
|
||||
QApplication::setAttribute( Qt::AA_UseSoftwareOpenGL );
|
||||
#ifndef WIN32
|
||||
// On Linux the attribute has no effect, force Mesa software rendering instead
|
||||
qputenv( "LIBGL_ALWAYS_SOFTWARE", "1" );
|
||||
#else
|
||||
riaAttachToParentConsole();
|
||||
#endif
|
||||
|
||||
RiaGuiApplication::enableHeadlessMode();
|
||||
}
|
||||
|
||||
} // namespace RiaMainTools
|
||||
@@ -26,6 +26,7 @@ namespace RiaMainTools
|
||||
void initializeSingletons();
|
||||
void releaseSingletonAndFactoryObjects();
|
||||
void deleteStaleSettingsLockFiles();
|
||||
void enableHeadlessRendering();
|
||||
|
||||
// True once crash logging has started. The crash handler pumps the Qt event loop while flushing
|
||||
// telemetry, so timer-driven application logic must check this flag and stay idle to avoid
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "RiaBaseDefs.h"
|
||||
#include "RiaFilePathTools.h"
|
||||
#include "RiaFontCache.h"
|
||||
#include "RiaGuiApplication.h"
|
||||
#include "RiaImportEclipseCaseTools.h"
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaNetworkTools.h"
|
||||
@@ -1555,7 +1556,8 @@ void RiaApplication::applyPreferences()
|
||||
project()->rootPlotTemplateItem()->updateConnectedEditors();
|
||||
}
|
||||
|
||||
caf::ProgressInfoStatic::setEnabled( RiaPreferencesSystem::current()->showProgressBar() );
|
||||
// No windows are shown in headless mode, so progress dialogs must stay hidden independent of preferences
|
||||
caf::ProgressInfoStatic::setEnabled( !RiaGuiApplication::isHeadless() && RiaPreferencesSystem::current()->showProgressBar() );
|
||||
|
||||
if ( m_preferencesFileName.isEmpty() )
|
||||
{
|
||||
|
||||
@@ -158,6 +158,8 @@
|
||||
///
|
||||
//==================================================================================================
|
||||
|
||||
bool RiaGuiApplication::sm_headlessMode = false;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -166,6 +168,22 @@ bool RiaGuiApplication::isRunning()
|
||||
return dynamic_cast<RiaGuiApplication*>( RiaApplication::instance() ) != nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RiaGuiApplication::enableHeadlessMode()
|
||||
{
|
||||
sm_headlessMode = true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RiaGuiApplication::isHeadless()
|
||||
{
|
||||
return sm_headlessMode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -284,6 +302,9 @@ QString RiaGuiApplication::promptForProjectSaveAsFileName() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RiaGuiApplication::askUserToSaveModifiedProject()
|
||||
{
|
||||
// A modal dialog would hang a headless batch run
|
||||
if ( isHeadless() ) return true;
|
||||
|
||||
if ( RiaPreferencesSystem::current()->showProjectChangedDialog() && caf::PdmUiModelChangeDetector::instance()->isModelChanged() )
|
||||
{
|
||||
QMessageBox msgBox( activeMainWindow() );
|
||||
@@ -549,6 +570,9 @@ void RiaGuiApplication::initialize()
|
||||
{
|
||||
RiaApplication::initialize();
|
||||
|
||||
// Progress dialogs would pop up windows and can block a headless batch run
|
||||
if ( isHeadless() ) caf::ProgressInfoStatic::setEnabled( false );
|
||||
|
||||
applyGuiPreferences( nullptr );
|
||||
|
||||
// Create main windows
|
||||
@@ -576,6 +600,23 @@ void RiaGuiApplication::initialize()
|
||||
RiaLogging::appendLoggerInstance( std::move( logger ) );
|
||||
}
|
||||
|
||||
if ( isHeadless() )
|
||||
{
|
||||
// The message panels are never shown in headless mode, so also write to stdout to make batch runs diagnosable
|
||||
auto stdLogger = std::make_unique<RiaStdOutLogger>();
|
||||
|
||||
if ( m_logLevelFromCommandLine.has_value() )
|
||||
{
|
||||
stdLogger->setLevel( m_logLevelFromCommandLine.value() );
|
||||
}
|
||||
else
|
||||
{
|
||||
stdLogger->setLevel( int( RiaLogging::logLevelBasedOnPreferences() ) );
|
||||
}
|
||||
|
||||
RiaLogging::appendLoggerInstance( std::move( stdLogger ) );
|
||||
}
|
||||
|
||||
m_socketServer = new RiaSocketServer( this );
|
||||
}
|
||||
|
||||
@@ -671,10 +712,28 @@ RiaApplication::ApplicationStatus RiaGuiApplication::handleArguments( cvf::Progr
|
||||
// Use a logger writing to stdout instead of message panel
|
||||
// This is useful when executing regression tests on a build server, and this is the reason for creating the
|
||||
// logger when parsing the command line options
|
||||
auto stdLogger = std::make_unique<RiaStdOutLogger>();
|
||||
stdLogger->setLevel( int( RILogLevel::RI_LL_DEBUG ) );
|
||||
// In headless mode a stdout logger already exists, reuse it to avoid duplicated log lines
|
||||
RiaStdOutLogger* existingStdLogger = nullptr;
|
||||
for ( auto logger : RiaLogging::loggerInstances() )
|
||||
{
|
||||
if ( auto candidate = dynamic_cast<RiaStdOutLogger*>( logger ) )
|
||||
{
|
||||
existingStdLogger = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RiaLogging::appendLoggerInstance( std::move( stdLogger ) );
|
||||
if ( existingStdLogger )
|
||||
{
|
||||
existingStdLogger->setLevel( int( RILogLevel::RI_LL_DEBUG ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto stdLogger = std::make_unique<RiaStdOutLogger>();
|
||||
stdLogger->setLevel( int( RILogLevel::RI_LL_DEBUG ) );
|
||||
|
||||
RiaLogging::appendLoggerInstance( std::move( stdLogger ) );
|
||||
}
|
||||
|
||||
RiaRegressionTestRunner::instance()->executeRegressionTests( regressionTestPath, QStringList() );
|
||||
return ApplicationStatus::EXIT_COMPLETED;
|
||||
@@ -926,29 +985,53 @@ RiaApplication::ApplicationStatus RiaGuiApplication::handleArguments( cvf::Progr
|
||||
snapshotFolder = snapshotFolderFromCommandLine;
|
||||
}
|
||||
|
||||
QStringList snapshotErrors;
|
||||
|
||||
if ( snapshotPlots )
|
||||
{
|
||||
auto mainPlotWnd = mainPlotWindow();
|
||||
if ( mainPlotWnd )
|
||||
{
|
||||
mainPlotWnd->show();
|
||||
mainPlotWnd->raise();
|
||||
if ( !isHeadless() )
|
||||
{
|
||||
mainPlotWnd->show();
|
||||
mainPlotWnd->raise();
|
||||
}
|
||||
|
||||
processEvents();
|
||||
|
||||
RicSnapshotAllPlotsToFileFeature::exportSnapshotOfPlotsIntoFolder( snapshotFolder, snapshotWidth, snapshotHeight );
|
||||
auto result =
|
||||
RicSnapshotAllPlotsToFileFeature::exportSnapshotOfPlotsIntoFolder( snapshotFolder, snapshotWidth, snapshotHeight );
|
||||
if ( !result ) snapshotErrors.push_back( result.error() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// The plot window is only created when the project shows plots, so there is nothing to export
|
||||
RiaLogging::warning( "No plot window available, no plot snapshots exported" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( snapshotViews )
|
||||
{
|
||||
auto mainWnd = RiuMainWindow::instance();
|
||||
mainWnd->show();
|
||||
mainWnd->raise();
|
||||
if ( mainWnd )
|
||||
{
|
||||
if ( !isHeadless() )
|
||||
{
|
||||
mainWnd->show();
|
||||
mainWnd->raise();
|
||||
}
|
||||
|
||||
processEvents();
|
||||
processEvents();
|
||||
|
||||
RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolder, snapshotWidth, snapshotHeight );
|
||||
auto result =
|
||||
RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolder, snapshotWidth, snapshotHeight );
|
||||
if ( !result ) snapshotErrors.push_back( result.error() );
|
||||
}
|
||||
else
|
||||
{
|
||||
snapshotErrors.push_back( "No main window available for view snapshot export" );
|
||||
}
|
||||
}
|
||||
|
||||
auto mainPlotWnd = mainPlotWindow();
|
||||
@@ -959,6 +1042,14 @@ RiaApplication::ApplicationStatus RiaGuiApplication::handleArguments( cvf::Progr
|
||||
|
||||
if ( RiuMainWindow::instance() ) RiuMainWindow::instance()->loadWinGeoAndDockToolBarLayout();
|
||||
|
||||
if ( !snapshotErrors.empty() )
|
||||
{
|
||||
// Report a non-zero exit code, as a batch run with missing images is otherwise indistinguishable from success
|
||||
RiaLogging::error( snapshotErrors.join( ". " ).toStdString() );
|
||||
|
||||
return ApplicationStatus::EXIT_WITH_ERROR;
|
||||
}
|
||||
|
||||
return ApplicationStatus::EXIT_COMPLETED;
|
||||
}
|
||||
|
||||
@@ -1063,7 +1154,7 @@ RiuMainWindow* RiaGuiApplication::getOrCreateAndShowMainWindow()
|
||||
{
|
||||
createMainWindow();
|
||||
}
|
||||
else
|
||||
else if ( !isHeadless() )
|
||||
{
|
||||
m_mainWindow->show();
|
||||
|
||||
@@ -1141,7 +1232,7 @@ void RiaGuiApplication::createMainWindow()
|
||||
m_mainWindow->setDefaultWindowSize();
|
||||
m_mainWindow->setDefaultToolbarVisibility();
|
||||
m_mainWindow->loadWinGeoAndDockToolBarLayout();
|
||||
m_mainWindow->showWindow();
|
||||
if ( !isHeadless() ) m_mainWindow->showWindow();
|
||||
|
||||
// if there is an existing logger, reconnect to it
|
||||
|
||||
@@ -1199,18 +1290,21 @@ RiuPlotMainWindow* RiaGuiApplication::getOrCreateAndShowMainPlotWindow()
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_mainPlotWindow->isMinimized() )
|
||||
if ( !isHeadless() )
|
||||
{
|
||||
m_mainPlotWindow->showNormal();
|
||||
m_mainPlotWindow->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainPlotWindow->show();
|
||||
}
|
||||
if ( m_mainPlotWindow->isMinimized() )
|
||||
{
|
||||
m_mainPlotWindow->showNormal();
|
||||
m_mainPlotWindow->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainPlotWindow->show();
|
||||
}
|
||||
|
||||
m_mainPlotWindow->raise();
|
||||
m_mainPlotWindow->activateWindow();
|
||||
m_mainPlotWindow->raise();
|
||||
m_mainPlotWindow->activateWindow();
|
||||
}
|
||||
|
||||
return m_mainPlotWindow.get();
|
||||
}
|
||||
@@ -1339,6 +1433,19 @@ void RiaGuiApplication::clearAllSelections()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RiaGuiApplication::showFormattedTextInMessageBoxOrConsole( const QString& text )
|
||||
{
|
||||
// A modal dialog would hang a headless batch run
|
||||
if ( isHeadless() )
|
||||
{
|
||||
const std::string stdText = text.toStdString();
|
||||
std::cout << stdText;
|
||||
|
||||
// Make sure the text ends with a newline and reaches the console even if the application aborts later
|
||||
if ( stdText.empty() || stdText.back() != '\n' ) std::cout << '\n';
|
||||
std::cout.flush();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a message dialog with cut/paste friendly text
|
||||
QDialog dlg( widgetToUseAsParent() );
|
||||
dlg.setModal( true );
|
||||
@@ -1444,9 +1551,9 @@ void RiaGuiApplication::onProjectOpened()
|
||||
if ( !m_mainPlotWindow )
|
||||
{
|
||||
createMainPlotWindow();
|
||||
m_mainPlotWindow->show();
|
||||
if ( !isHeadless() ) m_mainPlotWindow->show();
|
||||
}
|
||||
else
|
||||
else if ( !isHeadless() )
|
||||
{
|
||||
m_mainPlotWindow->show();
|
||||
m_mainPlotWindow->raise();
|
||||
@@ -1478,7 +1585,7 @@ void RiaGuiApplication::onProjectOpened()
|
||||
// Make sure to process events before this function to avoid strange Qt crash
|
||||
RiuPlotMainWindowTools::refreshToolbars();
|
||||
|
||||
if ( m_project->showPlotWindow() && m_project->showPlotWindowOnTop() )
|
||||
if ( m_project->showPlotWindow() && m_project->showPlotWindowOnTop() && !isHeadless() )
|
||||
{
|
||||
m_mainPlotWindow->raise();
|
||||
m_mainPlotWindow->activateWindow();
|
||||
@@ -1873,7 +1980,8 @@ void RiaGuiApplication::runMultiCaseSnapshots( const QString& templateProj
|
||||
bool loadOk = loadProject( templateProjectFileName, ProjectLoadAction::PLA_NONE, &modifier );
|
||||
if ( loadOk )
|
||||
{
|
||||
RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolderName );
|
||||
auto result = RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolderName );
|
||||
if ( !result ) RiaLogging::error( result.error().toStdString() );
|
||||
}
|
||||
}
|
||||
m_mainWindow->dockManager()->restoreState( curState );
|
||||
|
||||
@@ -82,6 +82,9 @@ public:
|
||||
static bool isRunning();
|
||||
static RiaGuiApplication* instance();
|
||||
|
||||
static void enableHeadlessMode();
|
||||
static bool isHeadless();
|
||||
|
||||
RiaGuiApplication( int& argc, char** argv );
|
||||
~RiaGuiApplication() override;
|
||||
|
||||
@@ -172,4 +175,6 @@ private:
|
||||
QPointer<RiuPlotMainWindow> m_mainPlotWindow;
|
||||
|
||||
std::unique_ptr<RiuRecentFileActionProvider> m_recentFileActionProvider;
|
||||
|
||||
static bool sm_headlessMode;
|
||||
};
|
||||
@@ -71,6 +71,11 @@ bool RiaArgumentParser::parseArguments( cvf::ProgramOptions* progOpt )
|
||||
cvf::ProgramOptions::MULTI_VALUE );
|
||||
progOpt->registerOption( "size", "<width> <height>", "Set size of the main application window.", cvf::ProgramOptions::MULTI_VALUE );
|
||||
progOpt->registerOption( "console", "", "Launch as a console application without graphics" );
|
||||
progOpt->registerOption( "headless",
|
||||
"",
|
||||
"Run without showing any windows, using software OpenGL rendering. "
|
||||
"Combine with --savesnapshots to export images on machines without a GPU. "
|
||||
"On Linux without a display server, run under xvfb-run." );
|
||||
progOpt->registerOption( "server", "[<portnumber>]", "Launch as a GRPC server. Default port is 50051", cvf::ProgramOptions::SINGLE_VALUE );
|
||||
progOpt->registerOption( "portnumberfile", "[<filename>]", "Write the port number to this file.", cvf::ProgramOptions::SINGLE_VALUE );
|
||||
|
||||
|
||||
@@ -250,9 +250,10 @@ void RiaRegressionTestRunner::runRegressionTest()
|
||||
if ( regressionTestConfig.exportSnapshots3dViews )
|
||||
{
|
||||
QSize defaultSize = RiaRegressionTestRunner::regressionDefaultImageSize();
|
||||
RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( fullPathGeneratedFolder,
|
||||
defaultSize.width(),
|
||||
defaultSize.height() );
|
||||
auto result = RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( fullPathGeneratedFolder,
|
||||
defaultSize.width(),
|
||||
defaultSize.height() );
|
||||
if ( !result ) RiaLogging::error( result.error().toStdString() );
|
||||
|
||||
QApplication::processEvents();
|
||||
}
|
||||
@@ -260,10 +261,11 @@ void RiaRegressionTestRunner::runRegressionTest()
|
||||
if ( regressionTestConfig.exportSnapshotsPlots )
|
||||
{
|
||||
QSize defaultSize = RiaRegressionTestRunner::regressionDefaultImageSize();
|
||||
RicSnapshotAllPlotsToFileFeature::exportSnapshotOfPlotsIntoFolder( fullPathGeneratedFolder,
|
||||
defaultSize.width(),
|
||||
defaultSize.height(),
|
||||
true /*activate widget*/ );
|
||||
auto result = RicSnapshotAllPlotsToFileFeature::exportSnapshotOfPlotsIntoFolder( fullPathGeneratedFolder,
|
||||
defaultSize.width(),
|
||||
defaultSize.height(),
|
||||
true /*activate widget*/ );
|
||||
if ( !result ) RiaLogging::error( result.error().toStdString() );
|
||||
}
|
||||
|
||||
uint64_t usedMemoryBeforeClose = caf::MemoryInspector::getApplicationPhysicalMemoryUsageMiB();
|
||||
|
||||
@@ -1788,7 +1788,11 @@ void RiuMainWindow::slotSnapshotAllViewsToFile()
|
||||
|
||||
// Save images in snapshot catalog relative to project directory
|
||||
QString absolutePathToSnapshotDir = app->createAbsolutePathFromProjectRelativePath( "snapshots" );
|
||||
RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( absolutePathToSnapshotDir );
|
||||
|
||||
if ( auto result = RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( absolutePathToSnapshotDir ); !result )
|
||||
{
|
||||
RiaLogging::error( result.error().toStdString() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user