/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include namespace { constexpr openspace::properties::Property::PropertyInfo FrametimeInfo = { "FrametimeType", "Type of the frame time display", "This value determines the units in which the frame time is displayed." }; constexpr openspace::properties::Property::PropertyInfo ClearCacheInfo = { "ClearCache", "Clear Cache", "Clears the cache of this DashboardItemFramerate item. If the selected option " "does not use any caching, this trigger does not do anything." }; constexpr const char* ValueDtAvg = "Average Deltatime"; constexpr const char* ValueDtExtremes = "Deltatime extremes"; constexpr const char* ValueDtStandardDeviation = "Deltatime standard deviation"; constexpr const char* ValueDtCov = "Deltatime coefficient of variation"; constexpr const char* ValueFps = "Frames per second"; constexpr const char* ValueFpsAvg = "Average frames per second"; constexpr const char* ValueNone = "None"; [[ nodiscard ]] char* formatDt(std::vector& buffer) { return fmt::format_to( buffer.data(), "Avg. Frametime: {:.2f} ms\0", openspace::global::windowDelegate->averageDeltaTime() * 1000.0 ); } [[ nodiscard ]] char* formatDtExtremes(std::vector& buffer, double minFrametimeCache, double maxFrametimeCache) { return fmt::format_to( buffer.data(), "Last frametimes between: {:.2f} and {:.2f} ms\n" "Overall between: {:.2f} and {:.2f} ms\0", openspace::global::windowDelegate->minDeltaTime() * 1000.0, openspace::global::windowDelegate->maxDeltaTime() * 1000.0, minFrametimeCache, maxFrametimeCache ); } [[ nodiscard ]] char* formatDtStandardDeviation(std::vector& buffer) { return fmt::format_to( buffer.data(), "Frametime standard deviation : {:.2f} ms\0", openspace::global::windowDelegate->deltaTimeStandardDeviation() * 1000.0 ); } [[ nodiscard ]] char* formatDtCoefficientOfVariation(std::vector& buffer) { return fmt::format_to( buffer.data(), "Frametime coefficient of variation : {:.2f} %\0", openspace::global::windowDelegate->deltaTimeStandardDeviation() / openspace::global::windowDelegate->averageDeltaTime() * 100.0 ); } [[ nodiscard ]] char* formatFps(std::vector& buffer) { return fmt::format_to( buffer.data(), "FPS: {:3.2f}\0", 1.0 / openspace::global::windowDelegate->deltaTime() ); } [[ nodiscard ]] char* formatAverageFps(std::vector& buffer) { return fmt::format_to( buffer.data(), "Avg. FPS: {:3.2f}\0", 1.0 / openspace::global::windowDelegate->averageDeltaTime() ); } [[ nodiscard ]] char* format(std::vector& buffer, openspace::DashboardItemFramerate::FrametimeType frametimeType, double minFrametimeCache, double maxFrametimeCache) { using namespace openspace; switch (frametimeType) { case DashboardItemFramerate::FrametimeType::DtTimeAvg: return formatDt(buffer); case DashboardItemFramerate::FrametimeType::DtTimeExtremes: return formatDtExtremes(buffer, minFrametimeCache, maxFrametimeCache); case DashboardItemFramerate::FrametimeType::DtStandardDeviation: return formatDtStandardDeviation(buffer); case DashboardItemFramerate::FrametimeType::DtCoefficientOfVariation: return formatDtCoefficientOfVariation(buffer); case DashboardItemFramerate::FrametimeType::FPS: return formatFps(buffer); case DashboardItemFramerate::FrametimeType::FPSAvg: return formatAverageFps(buffer); default: throw ghoul::MissingCaseException(); } } } // namespace namespace openspace { documentation::Documentation DashboardItemFramerate::Documentation() { using namespace documentation; return { "DashboardItem Framerate", "base_dashboarditem_framerate", { { "Type", new StringEqualVerifier("DashboardItemFramerate"), Optional::No }, { FrametimeInfo.identifier, new StringInListVerifier({ ValueDtAvg, ValueDtExtremes, ValueDtStandardDeviation, ValueDtCov, ValueFps, ValueFpsAvg, ValueNone }), Optional::Yes, FrametimeInfo.description } } }; } DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictionary) : DashboardTextItem(dictionary) , _frametimeType(FrametimeInfo, properties::OptionProperty::DisplayType::Dropdown) , _clearCache(ClearCacheInfo) { documentation::testSpecificationAndThrow( Documentation(), dictionary, "DashboardItemFramerate" ); _frametimeType.addOptions({ { static_cast(FrametimeType::DtTimeAvg), ValueDtAvg }, { static_cast(FrametimeType::DtTimeExtremes), ValueDtExtremes }, { static_cast(FrametimeType::DtStandardDeviation), ValueDtStandardDeviation }, { static_cast(FrametimeType::DtCoefficientOfVariation), ValueDtCov }, { static_cast(FrametimeType::FPS), ValueFps }, { static_cast(FrametimeType::FPSAvg), ValueFpsAvg }, { static_cast(FrametimeType::None), ValueNone } }); if (dictionary.hasKey(FrametimeInfo.identifier)) { const std::string& v = dictionary.value(FrametimeInfo.identifier); if (v == ValueDtAvg) { _frametimeType = static_cast(FrametimeType::DtTimeAvg); } else if (v == ValueDtExtremes) { _frametimeType = static_cast(FrametimeType::DtTimeExtremes); } else if (v == ValueDtStandardDeviation) { _frametimeType = static_cast(FrametimeType::DtStandardDeviation); } else if (v == ValueDtCov) { _frametimeType = static_cast(FrametimeType::DtCoefficientOfVariation); } else if (v == ValueFps) { _frametimeType = static_cast(FrametimeType::FPS); } else if (v == ValueFpsAvg) { _frametimeType = static_cast(FrametimeType::FPSAvg); } else { _frametimeType = static_cast(FrametimeType::None); } } else { _frametimeType = static_cast(FrametimeType::FPSAvg); } addProperty(_frametimeType); _clearCache.onChange([this]() { _shouldClearCache = true; }); addProperty(_clearCache); _buffer.resize(128); } void DashboardItemFramerate::render(glm::vec2& penPosition) { ZoneScoped if (_shouldClearCache) { _minDeltaTimeCache = 1.0; _maxDeltaTimeCache = -1.0; _shouldClearCache = false; } _minDeltaTimeCache = std::min( _minDeltaTimeCache, global::windowDelegate->minDeltaTime() * 1000.0 ); _maxDeltaTimeCache = std::max( _maxDeltaTimeCache, global::windowDelegate->maxDeltaTime() * 1000.0 ); FrametimeType frametimeType = FrametimeType(_frametimeType.value()); std::fill(_buffer.begin(), _buffer.end(), char(0)); char* end = format( _buffer, frametimeType, _minDeltaTimeCache, _maxDeltaTimeCache ); std::string_view output = std::string_view(_buffer.data(), end - _buffer.data()); int nLines = output.empty() ? 0 : static_cast((std::count(output.begin(), output.end(), '\n') + 1)); ghoul::fontrendering::FontRenderer::defaultRenderer().render( *_font, penPosition, output ); penPosition.y -= _font->height() * static_cast(nLines); } glm::vec2 DashboardItemFramerate::size() const { ZoneScoped const FrametimeType t = FrametimeType(_frametimeType.value()); char* end = format(_buffer, t, _minDeltaTimeCache, _maxDeltaTimeCache); std::string_view output = std::string_view(_buffer.data(), end - _buffer.data()); if (output.empty()) { return { 0.f, 0.f }; } return _font->boundingBox(output); } } // namespace openspace