From fc28b98db99300852635d75ed048ca7cde1bb50c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 19 Jul 2019 09:55:41 +0200 Subject: [PATCH] Add basic instrumentation options to the renderengine and globebrowsing module Instrumentation is disabled on default, but can be enabled in CMake --- CMakeLists.txt | 5 ++ include/openspace/rendering/renderengine.h | 18 ++++- modules/globebrowsing/CMakeLists.txt | 5 ++ modules/globebrowsing/globebrowsingmodule.cpp | 80 +++++++++++++++++++ modules/globebrowsing/globebrowsingmodule.h | 25 ++++++ modules/globebrowsing/src/layer.cpp | 7 +- modules/globebrowsing/src/layer.h | 3 +- modules/globebrowsing/src/layergroup.cpp | 7 +- modules/globebrowsing/src/layergroup.h | 3 +- modules/globebrowsing/src/layermanager.cpp | 6 +- modules/globebrowsing/src/layermanager.h | 3 +- modules/globebrowsing/src/renderableglobe.cpp | 24 +++++- modules/globebrowsing/src/renderableglobe.h | 4 + modules/globebrowsing/src/tileprovider.cpp | 12 ++- modules/globebrowsing/src/tileprovider.h | 4 +- src/rendering/renderengine.cpp | 75 ++++++++++++++--- 16 files changed, 255 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc5a7104df..724729bcad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,6 +285,11 @@ if (OPENSPACE_WITH_ABUFFER_RENDERER) target_compile_definitions(openspace-core PUBLIC "OPENSPACE_WITH_ABUFFER_RENDERER") endif () +option(OPENSPACE_WITH_INSTRUMENTATION "Add instrumentation options" OFF) +if (OPENSPACE_WITH_INSTRUMENTATION) + target_compile_definitions(openspace-core PUBLIC "OPENSPACE_WITH_INSTRUMENTATION") +endif () + # Just in case, create the bin directory add_custom_command( diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 779e6cf926..3ab7b9baaf 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -187,7 +187,21 @@ private: properties::TriggerProperty _takeScreenshot; bool _shouldTakeScreenshot = false; properties::BoolProperty _applyWarping; - properties::BoolProperty _showFrameNumber; + properties::BoolProperty _showFrameInformation; +#ifdef OPENSPACE_WITH_INSTRUMENTATION + struct FrameInfo { + uint64_t iFrame; + double deltaTime; + double avgDeltaTime; + }; + + struct { + std::vector frames; + uint64_t lastSavedFrame = 0; + uint16_t saveEveryNthFrame = 2048; + } _frameInfo; + properties::BoolProperty _saveFrameInformation; +#endif // OPENSPACE_WITH_INSTRUMENTATION properties::BoolProperty _disableMasterRendering; properties::FloatProperty _globalBlackOutFactor; @@ -205,7 +219,7 @@ private: std::vector _programs; - std::shared_ptr _fontBig; + std::shared_ptr _fontFrameInfo; std::shared_ptr _fontInfo; std::shared_ptr _fontDate; std::shared_ptr _fontLog; diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index 5d2bbafadf..7d46e40e1a 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -109,6 +109,11 @@ create_new_module( ${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES} ) +option(OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION "Instrumentation for GlobeBrowsing Performance" OFF) +if (OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION) + target_compile_definitions(openspace-module-globebrowsing INTERFACE "OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION") +endif () + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gdal_data DESTINATION modules/globebrowsing) if (WIN32) diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index e5cc4fd5f4..081fedebdc 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -101,6 +101,14 @@ namespace { "The maximum size of the MemoryAwareTileCache, on the CPU and GPU." }; +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + constexpr const openspace::properties::Property::PropertyInfo InstrumentationInfo = { + "SaveInstrumentationInfo", + "Save Instrumentation Info", + "If enabled, the instrumentation data is saved to disk at the end of the frame." + }; +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + openspace::GlobeBrowsingModule::Capabilities parseSubDatasets(char** subDatasets, int nSubdatasets) @@ -163,12 +171,24 @@ GlobeBrowsingModule::GlobeBrowsingModule() , _wmsCacheLocation(WMSCacheLocationInfo, "${BASE}/cache_gdal") , _wmsCacheSizeMB(WMSCacheSizeInfo, 1024) , _tileCacheSizeMB(TileCacheSizeInfo, 1024) +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + , _saveInstrumentation(InstrumentationInfo, false) +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION { addProperty(_wmsCacheEnabled); addProperty(_offlineMode); addProperty(_wmsCacheLocation); addProperty(_wmsCacheSizeMB); addProperty(_tileCacheSizeMB); + +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + _saveInstrumentation.onChange([&]() { + if (_saveInstrumentation) { + _frameInfo.lastSavedFrame = global::renderEngine.frameNumber(); + } + }); + addProperty(_saveInstrumentation); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION } void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { @@ -235,6 +255,43 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { // Render global::callback::render.emplace_back([&]() { _tileCache->update(); }); + // Postdraw +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + global::callback::postDraw.emplace_back([&]() { + // >= as we might have multiple frames per postDraw call (stereo rendering, + // fisheye, etc) + const uint16_t next = _frameInfo.lastSavedFrame + _frameInfo.saveEveryNthFrame; + const bool shouldSave = _saveInstrumentation && + global::renderEngine.frameNumber() >= next; + if (shouldSave) { + using K = const globebrowsing::RenderableGlobe*; + using V = std::vector; + for (const std::pair& i : _frameInfo.frames) { + std::string filename = fmt::format( + "_inst_globebrowsing_{}_{}_{}.txt", + i.first->owner()->identifier(), // Owner of the renderable has a name + _frameInfo.lastSavedFrame, + _frameInfo.saveEveryNthFrame + ); + std::ofstream file(absPath("${BIN}/" + filename)); + for (const FrameInfo& f : i.second) { + std::string line = fmt::format( + "{}\t{}\t{}\t{}", + f.iFrame, + f.nTilesRenderedLocal, + f.nTilesRenderedGlobal, + f.nTilesUploaded + ); + file << line << '\n'; + } + } + + _frameInfo.frames.clear(); + _frameInfo.lastSavedFrame = global::renderEngine.frameNumber(); + } + }); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + // Deinitialize global::callback::deinitialize.emplace_back([&]() { GdalWrapper::destroy(); }); @@ -729,4 +786,27 @@ uint64_t GlobeBrowsingModule::wmsCacheSize() const { return size * 1024 * 1024; } +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION +void GlobeBrowsingModule::addFrameInfo(globebrowsing::RenderableGlobe* globe, + uint32_t nTilesRenderedLocal, + uint32_t nTilesRenderedGlobal, + uint32_t nTilesUploaded) +{ + auto it = _frameInfo.frames.find(globe); + if (it == _frameInfo.frames.end()) { + _frameInfo.frames[globe] = std::vector(); + _frameInfo.frames[globe].reserve(_frameInfo.saveEveryNthFrame); + } + else { + it->second.push_back({ + global::renderEngine.frameNumber(), + nTilesRenderedLocal, + nTilesRenderedGlobal, + nTilesUploaded + }); + } +} + +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + } // namespace openspace diff --git a/modules/globebrowsing/globebrowsingmodule.h b/modules/globebrowsing/globebrowsingmodule.h index fc96cd91c4..89e397d8ab 100644 --- a/modules/globebrowsing/globebrowsingmodule.h +++ b/modules/globebrowsing/globebrowsingmodule.h @@ -93,6 +93,11 @@ public: std::string wmsCacheLocation() const; uint64_t wmsCacheSize() const; // bytes +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + void addFrameInfo(globebrowsing::RenderableGlobe* globe, uint32_t nTilesRenderedLocal, + uint32_t nTilesRenderedGlobal, uint32_t nTilesUploaded); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + protected: void internalInitialize(const ghoul::Dictionary&) override; @@ -134,6 +139,26 @@ private: std::map _capabilitiesMap; std::multimap _urlList; + +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + struct FrameInfo { + uint64_t iFrame = 0; + uint32_t nTilesRenderedLocal = 0; + uint32_t nTilesRenderedGlobal = 0; + uint32_t nTilesUploaded = 0; + }; + + struct { + std::unordered_map< + globebrowsing::RenderableGlobe*, + std::vector + > frames; + + uint64_t lastSavedFrame = 0; + const uint16_t saveEveryNthFrame = 2048; + } _frameInfo; + properties::BoolProperty _saveInstrumentation; +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION }; } // namespace openspace diff --git a/modules/globebrowsing/src/layer.cpp b/modules/globebrowsing/src/layer.cpp index 52e69b7731..e07f632bbb 100644 --- a/modules/globebrowsing/src/layer.cpp +++ b/modules/globebrowsing/src/layer.cpp @@ -341,9 +341,12 @@ void Layer::onChange(std::function callback) { _onChangeCallback = std::move(callback); } -void Layer::update() { +int Layer::update() { if (_tileProvider) { - tileprovider::update(*_tileProvider); + return tileprovider::update(*_tileProvider); + } + else { + return 0; } } diff --git a/modules/globebrowsing/src/layer.h b/modules/globebrowsing/src/layer.h index 4658468b90..908217611f 100644 --- a/modules/globebrowsing/src/layer.h +++ b/modules/globebrowsing/src/layer.h @@ -64,7 +64,8 @@ public: void onChange(std::function callback); - void update(); + // Return: number of tiles that were updated + int update(); glm::ivec2 tilePixelStartOffset() const; glm::ivec2 tilePixelSizeDifference() const; diff --git a/modules/globebrowsing/src/layergroup.cpp b/modules/globebrowsing/src/layergroup.cpp index f6088e8869..e0aff6f0af 100644 --- a/modules/globebrowsing/src/layergroup.cpp +++ b/modules/globebrowsing/src/layergroup.cpp @@ -91,15 +91,18 @@ void LayerGroup::deinitialize() { } } -void LayerGroup::update() { +int LayerGroup::update() { + int res = 0; _activeLayers.clear(); for (const std::unique_ptr& layer : _layers) { if (layer->enabled()) { - layer->update(); + res += layer->update(); _activeLayers.push_back(layer.get()); } } + + return res; } Layer* LayerGroup::addLayer(const ghoul::Dictionary& layerDict) { diff --git a/modules/globebrowsing/src/layergroup.h b/modules/globebrowsing/src/layergroup.h index 923cef7011..7db9a718de 100644 --- a/modules/globebrowsing/src/layergroup.h +++ b/modules/globebrowsing/src/layergroup.h @@ -48,7 +48,8 @@ struct LayerGroup : public properties::PropertyOwner { void deinitialize(); /// Updates all layers tile providers within this group - void update(); + /// Return: Number of tiles that were updated + int update(); Layer* addLayer(const ghoul::Dictionary& layerDict); void deleteLayer(const std::string& layerName); diff --git a/modules/globebrowsing/src/layermanager.cpp b/modules/globebrowsing/src/layermanager.cpp index 0f442d8bcf..ae677ada58 100644 --- a/modules/globebrowsing/src/layermanager.cpp +++ b/modules/globebrowsing/src/layermanager.cpp @@ -111,10 +111,12 @@ std::array LayerManager::layerGroups( return res; } -void LayerManager::update() { +int LayerManager::update() { + int res = 0; for (std::unique_ptr& layerGroup : _layerGroups) { - layerGroup->update(); + res += layerGroup->update(); } + return res; } void LayerManager::reset(bool includeDisabled) { diff --git a/modules/globebrowsing/src/layermanager.h b/modules/globebrowsing/src/layermanager.h index de7fbb4c62..36de309488 100644 --- a/modules/globebrowsing/src/layermanager.h +++ b/modules/globebrowsing/src/layermanager.h @@ -63,7 +63,8 @@ public: std::array layerGroups() const; - void update(); + // Return: Number of tiles updated + int update(); void reset(bool includeDisabled = false); void onChange(std::function callback); diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 4cccfedacd..51c263d9c3 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -47,6 +47,13 @@ #include #include +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION +#include +#include +openspace::GlobeBrowsingModule* _module = nullptr; + +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + namespace { // Global flags to modify the RenderableGlobe constexpr const bool LimitLevelByAvailableData = true; @@ -595,10 +602,13 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) if (dictionary.hasKeyAndValue(KeyLabels)) { _labelsDictionary = dictionary.value(KeyLabels); } + +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + _module = global::moduleEngine.module(); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION } void RenderableGlobe::initializeGL() { - if (!_labelsDictionary.empty()) { _globeLabelsComponent.initialize(_labelsDictionary, this); addPropertySubOwner(_globeLabelsComponent); @@ -745,7 +755,11 @@ void RenderableGlobe::update(const UpdateData& data) { _layerManager.reset(); _debugProperties.resetTileProviders = false; } +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + _nUploadedTiles = _layerManager.update(); +#else _layerManager.update(); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION if (_nLayersIsDirty) { std::array lgs = @@ -1034,6 +1048,14 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&) { } _localRenderer.program->deactivate(); +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + _module->addFrameInfo( + this, + std::min(localCount, ChunkBufferSize), + std::min(globalCount, ChunkBufferSize), + _nUploadedTiles + ); +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION if (_debugProperties.showChunkBounds || _debugProperties.showChunkAABB) { for (int i = 0; i < std::min(globalCount, ChunkBufferSize); ++i) { diff --git a/modules/globebrowsing/src/renderableglobe.h b/modules/globebrowsing/src/renderableglobe.h index 93022b532c..7886c0d3e2 100644 --- a/modules/globebrowsing/src/renderableglobe.h +++ b/modules/globebrowsing/src/renderableglobe.h @@ -274,6 +274,10 @@ private: // Labels GlobeLabelsComponent _globeLabelsComponent; ghoul::Dictionary _labelsDictionary; + +#ifdef OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION + int _nUploadedTiles = 0; +#endif // OPENSPACE_MODULE_GLOBEBROWSING_INSTRUMENTATION }; } // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider.cpp b/modules/globebrowsing/src/tileprovider.cpp index addef1e23a..ad72426685 100644 --- a/modules/globebrowsing/src/tileprovider.cpp +++ b/modules/globebrowsing/src/tileprovider.cpp @@ -162,15 +162,17 @@ void initAsyncTileDataReader(DefaultTileProvider& t, TileTextureInitData initDat ); } -void initTexturesFromLoadedData(DefaultTileProvider& t) { +bool initTexturesFromLoadedData(DefaultTileProvider& t) { if (t.asyncTextureDataProvider) { std::optional tile = t.asyncTextureDataProvider->popFinishedRawTile(); if (tile) { const cache::ProviderTileKey key = { tile->tileIndex, t.uniqueIdentifier }; ghoul_assert(!t.tileCache->exist(key), "Tile must not be existing in cache"); t.tileCache->createTileAndPut(key, std::move(tile.value())); + return true; } } + return false; } @@ -1073,7 +1075,7 @@ TileDepthTransform depthTransform(TileProvider& tp) { -void update(TileProvider& tp) { +int update(TileProvider& tp) { switch (tp.type) { case Type::DefaultTileProvider: { DefaultTileProvider& t = static_cast(tp); @@ -1082,7 +1084,7 @@ void update(TileProvider& tp) { } t.asyncTextureDataProvider->update(); - initTexturesFromLoadedData(t); + bool hasUploaded = initTexturesFromLoadedData(t); if (t.asyncTextureDataProvider->shouldBeDeleted()) { t.asyncTextureDataProvider = nullptr; @@ -1091,6 +1093,9 @@ void update(TileProvider& tp) { tileTextureInitData(t.layerGroupID, t.padTiles, t.tilePixelSize) ); } + if (hasUploaded) { + return 1; + } break; } case Type::SingleImageTileProvider: @@ -1132,6 +1137,7 @@ void update(TileProvider& tp) { default: throw ghoul::MissingCaseException(); } + return 0; } diff --git a/modules/globebrowsing/src/tileprovider.h b/modules/globebrowsing/src/tileprovider.h index c367638374..c7e3cd5196 100644 --- a/modules/globebrowsing/src/tileprovider.h +++ b/modules/globebrowsing/src/tileprovider.h @@ -222,8 +222,10 @@ TileDepthTransform depthTransform(TileProvider& tp); /** * This method should be called once per frame. Here, TileProviders * are given the opportunity to update their internal state. + * + * \return The number of tiles that have been updated in this call */ -void update(TileProvider& tp); +int update(TileProvider& tp); /** * Provides a uniform way of all TileProviders to reload or diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 4498e1f9cf..cbdb090ad0 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -144,11 +144,20 @@ namespace { }; constexpr openspace::properties::Property::PropertyInfo ShowFrameNumberInfo = { - "ShowFrameNumber", - "Show Frame Number", - "If this value is enabled, the current frame number is rendered into the window." + "ShowFrameInformation", + "Show Frame Information", + "If this value is enabled, the current frame number and frame times are rendered " + "into the window." }; +#ifdef OPENSPACE_WITH_INSTRUMENTATION + constexpr openspace::properties::Property::PropertyInfo SaveFrameInfo = { + "SaveFrameInformation", + "Save Frame Information", + "Saves the frame information to disk" + }; +#endif // OPENSPACE_WITH_INSTRUMENTATION + constexpr openspace::properties::Property::PropertyInfo DisableMasterInfo = { "DisableMasterRendering", "Disable Master Rendering", @@ -239,7 +248,10 @@ RenderEngine::RenderEngine() , _showCameraInfo(ShowCameraInfo, true) , _takeScreenshot(TakeScreenshotInfo) , _applyWarping(ApplyWarpingInfo, false) - , _showFrameNumber(ShowFrameNumberInfo, false) + , _showFrameInformation(ShowFrameNumberInfo, false) +#ifdef OPENSPACE_WITH_INSTRUMENTATION + , _saveFrameInformation(SaveFrameInfo, false) +#endif // OPENSPACE_WITH_INSTRUMENTATION , _disableMasterRendering(DisableMasterInfo, false) , _globalBlackOutFactor(GlobalBlackoutFactorInfo, 1.f, 0.f, 1.f) , _nAaSamples(AaSamplesInfo, 4, 1, 8) @@ -317,7 +329,15 @@ RenderEngine::RenderEngine() _takeScreenshot.onChange([this](){ _shouldTakeScreenshot = true; }); addProperty(_takeScreenshot); - addProperty(_showFrameNumber); + addProperty(_showFrameInformation); +#ifdef OPENSPACE_WITH_INSTRUMENTATION + _saveFrameInformation.onChange([&]() { + if (_saveFrameInformation) { + _frameInfo.lastSavedFrame = frameNumber(); + } + }); + addProperty(_saveFrameInformation); +#endif // OPENSPACE_WITH_INSTRUMENTATION addProperty(_globalRotation); addProperty(_screenSpaceRotation); @@ -415,12 +435,12 @@ void RenderEngine::initializeGL() { // development global::windowDelegate.setNearFarClippingPlane(0.001f, 1000.f); - //Set horizontal FOV value with whatever the field of view (in degrees) is of the + // Set horizontal FOV value with whatever the field of view (in degrees) is of the // initialized window _horizFieldOfView = static_cast(global::windowDelegate.getHorizFieldOfView()); - constexpr const float FontSizeBig = 50.f; - _fontBig = global::fontManager.font(KeyFontMono, FontSizeBig); + constexpr const float FontSizeFrameinfo = 32.f; + _fontFrameInfo = global::fontManager.font(KeyFontMono, FontSizeFrameinfo); constexpr const float FontSizeTime = 15.f; _fontDate = global::fontManager.font(KeyFontMono, FontSizeTime); constexpr const float FontSizeMono = 10.f; @@ -582,13 +602,18 @@ void RenderEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMat ); } - if (_showFrameNumber) { + if (_showFrameInformation) { glm::vec2 penPosition = glm::vec2( fontResolution().x / 2 - 50, fontResolution().y / 3 ); - RenderFont(*_fontBig, penPosition, std::to_string(_frameNumber)); + std::string fn = std::to_string(_frameNumber); + std::string dt = std::to_string(global::windowDelegate.deltaTime()); + std::string avgDt = std::to_string(global::windowDelegate.averageDeltaTime()); + + std::string res = "Frame: " + fn + '\n' + "Dt: " + dt + '\n' + "Avg Dt: " + avgDt; + RenderFont(*_fontFrameInfo, penPosition, res); } ++_frameNumber; @@ -794,6 +819,36 @@ void RenderEngine::postDraw() { scene()->allSceneGraphNodes() ); } + + if (_saveFrameInformation) { + _frameInfo.frames.push_back({ + frameNumber(), + global::windowDelegate.deltaTime(), + global::windowDelegate.averageDeltaTime() + }); + } + + +#ifdef OPENSPACE_WITH_INSTRUMENTATION + const uint16_t next = _frameInfo.lastSavedFrame + _frameInfo.saveEveryNthFrame; + const bool shouldSave = _saveFrameInformation && frameNumber() >= next; + if (shouldSave) { + std::string filename = fmt::format( + "_inst_renderengine_{}_{}.txt", + _frameInfo.lastSavedFrame, _frameInfo.saveEveryNthFrame + ); + std::ofstream file(absPath("${BIN}/" + filename)); + for (const FrameInfo& i : _frameInfo.frames) { + std::string line = fmt::format( + "{}\t{}\t{}", i.iFrame, i.deltaTime, i.avgDeltaTime + ); + file << line << '\n'; + } + + _frameInfo.frames.clear(); + _frameInfo.lastSavedFrame = frameNumber(); + } +#endif // OPENSPACE_WITH_INSTRUMENTATION } Scene* RenderEngine::scene() {