From 78fab2ca397a80f739326b1d0bfe7f0832a9a324 Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Tue, 12 Mar 2024 13:38:10 -0700 Subject: [PATCH] Add frame timing option to viewer This changelist adds a frame timing option to the MaterialX Viewer, allowing basic performance profiling from within the application. To enable frame timing, launch the MaterialX Viewer with "--frameTiming true". --- source/MaterialXView/Main.cpp | 11 +++++++ source/MaterialXView/RenderPipelineGL.cpp | 3 ++ source/MaterialXView/RenderPipelineMetal.mm | 3 ++ source/MaterialXView/Viewer.cpp | 33 ++++++++++++++++++++- source/MaterialXView/Viewer.h | 24 +++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/source/MaterialXView/Main.cpp b/source/MaterialXView/Main.cpp index c703e48883..6524172610 100644 --- a/source/MaterialXView/Main.cpp +++ b/source/MaterialXView/Main.cpp @@ -41,6 +41,7 @@ const std::string options = " --bakeHeight [INTEGER] Specify the target height for texture baking (defaults to maximum image height of the source document)\n" " --bakeFilename [STRING] Specify the output document filename for texture baking\n" " --refresh [FLOAT] Specify the refresh period for the viewer in milliseconds (defaults to 50, set to -1 to disable)\n" + " --frameTiming [BOOLEAN] Specify whether the frame timing display is enabled (defaults to false)\n" " --remap [TOKEN1:TOKEN2] Specify the remapping from one token to another when MaterialX document is loaded\n" " --skip [NAME] Specify to skip elements matching the given name attribute\n" " --terminator [STRING] Specify to enforce the given terminator string for file prefixes\n" @@ -99,6 +100,7 @@ int main(int argc, char* const argv[]) int bakeHeight = 0; std::string bakeFilename; float refresh = 50.0f; + bool frameTiming = false; for (size_t i = 0; i < tokens.size(); i++) { @@ -212,6 +214,14 @@ int main(int argc, char* const argv[]) { parseToken(nextToken, "float", refresh); } + else if (token == "--frameTiming") + { + parseToken(nextToken, "boolean", frameTiming); + if (frameTiming) + { + refresh = 0; + } + } else if (token == "--remap") { mx::StringVec vec = mx::splitString(nextToken, ":"); @@ -285,6 +295,7 @@ int main(int argc, char* const argv[]) viewer->setBakeWidth(bakeWidth); viewer->setBakeHeight(bakeHeight); viewer->setBakeFilename(bakeFilename); + viewer->setFrameTiming(frameTiming); viewer->initialize(); if (!captureFilename.empty()) diff --git a/source/MaterialXView/RenderPipelineGL.cpp b/source/MaterialXView/RenderPipelineGL.cpp index 68ac0be040..eeb23868f7 100644 --- a/source/MaterialXView/RenderPipelineGL.cpp +++ b/source/MaterialXView/RenderPipelineGL.cpp @@ -296,6 +296,9 @@ mx::ImagePtr GLRenderPipeline::getShadowMap(int shadowMapSize) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); } + + // Reset frame timing after shadow generation. + _viewer->resetFrameTiming(); } return _viewer->_shadowMap; diff --git a/source/MaterialXView/RenderPipelineMetal.mm b/source/MaterialXView/RenderPipelineMetal.mm index 0dc85b8aa4..ccd8fbd5f8 100644 --- a/source/MaterialXView/RenderPipelineMetal.mm +++ b/source/MaterialXView/RenderPipelineMetal.mm @@ -387,6 +387,9 @@ [renderpassDesc release]; } + + // Reset frame timing after shadow generation. + _viewer->resetFrameTiming(); } _viewer->_shadowMap = _shadowMap[_viewer->_shadowSoftness % 2]; diff --git a/source/MaterialXView/Viewer.cpp b/source/MaterialXView/Viewer.cpp index c6a810c2d4..a9b66dd452 100644 --- a/source/MaterialXView/Viewer.cpp +++ b/source/MaterialXView/Viewer.cpp @@ -225,7 +225,12 @@ Viewer::Viewer(const std::string& materialFilename, _bakeRequested(false), _bakeWidth(0), _bakeHeight(0), - _bakeDocumentPerMaterial(false) + _bakeDocumentPerMaterial(false), + _frameTiming(false), + _timingLabel(nullptr), + _timingPanel(nullptr), + _timingText(nullptr), + _avgFrameTime(0.0) { // Resolve input filenames, taking both the provided search path and // current working directory into account. @@ -327,6 +332,21 @@ void Viewer::initialize() } }); + // Create frame timing display + if (_frameTiming) + { + _timingLabel = new ng::Label(_window, "Timing"); + _timingPanel = new ng::Widget(_window); + _timingPanel->set_layout(new ng::BoxLayout(ng::Orientation::Horizontal, + ng::Alignment::Middle, 0, 6)); + new ng::Label(_timingPanel, "Frame time:"); + _timingText = new ng::TextBox(_timingPanel); + _timingText->set_value("0"); + _timingText->set_units(" ms"); + _timingText->set_fixed_size(ng::Vector2i(80, 25)); + _timingText->set_alignment(ng::TextBox::Alignment::Right); + } + // Create geometry handler. mx::TinyObjLoaderPtr objLoader = mx::TinyObjLoader::create(); mx::CgltfLoaderPtr gltfLoader = mx::CgltfLoader::create(); @@ -2078,6 +2098,17 @@ void Viewer::draw_contents() #endif } + // Update frame timing. + if (_frameTiming) + { + const double DEFAULT_SMOOTHING_BIAS = 0.9; + double elapsedTime = _frameTimer.elapsedTime() * 1000.0; + double bias = (_avgFrameTime > 0.0) ? DEFAULT_SMOOTHING_BIAS : 0.0; + _avgFrameTime = bias * _avgFrameTime + (1.0 - bias) * elapsedTime; + _timingText->set_value(std::to_string((int) _avgFrameTime)); + _frameTimer.startTimer(); + } + // Capture the current frame. if (_captureRequested && !_turntableEnabled) { diff --git a/source/MaterialXView/Viewer.h b/source/MaterialXView/Viewer.h index ff0827d12a..2b9c0bec71 100644 --- a/source/MaterialXView/Viewer.h +++ b/source/MaterialXView/Viewer.h @@ -151,6 +151,22 @@ class Viewer : public ng::Screen _bakeFilename = bakeFilename; } + // Enable or disable frame timing. + void setFrameTiming(bool enable) + { + _frameTiming = enable; + } + + // Reset frame timing after a blocking event. + void resetFrameTiming() + { + if (_frameTiming) + { + _frameTimer.startTimer(); + _avgFrameTime = 0.0; + } + } + // Return true if all inputs should be shown in the property editor. bool getShowAllInputs() const { @@ -450,6 +466,14 @@ class Viewer : public ng::Screen unsigned int _bakeHeight; bool _bakeDocumentPerMaterial; mx::FilePath _bakeFilename; + + // Frame timing + bool _frameTiming; + ng::Label* _timingLabel; + ng::Widget* _timingPanel; + ng::TextBox* _timingText; + mx::ScopedTimer _frameTimer; + double _avgFrameTime; }; extern const mx::Vector3 DEFAULT_CAMERA_POSITION;