diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b2e047..04f11252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 21.03+ (???) ------------------------------------------------------------------------ +- Feature: [#451] Optionally show an FPS counter at the top of the screen. - Fix: [#804] Enter key not confirming save prompt. - Fix: [#809] Audio calculation not using the z axis. - Fix: [#825] Potential crash when opening town rename prompt. diff --git a/data/language/en-GB.yml b/data/language/en-GB.yml index 30ffb175..b817e218 100644 --- a/data/language/en-GB.yml +++ b/data/language/en-GB.yml @@ -2213,3 +2213,5 @@ strings: 2158: "Enable sandbox mode" 2159: "Allow manual driving" 2160: "Allow building while paused" + 2161: "Show FPS counter" + 2162: "{SMALLFONT}{COLOUR BLACK}This shows a counter at the top of the screen, indicating the number of frames drawn per second." diff --git a/src/OpenLoco/Config.cpp b/src/OpenLoco/Config.cpp index 06942848..a698dfe7 100644 --- a/src/OpenLoco/Config.cpp +++ b/src/OpenLoco/Config.cpp @@ -93,6 +93,8 @@ namespace OpenLoco::Config _new_config.autosave_frequency = config["autosave_frequency"].as(); if (config["autosave_amount"]) _new_config.autosave_amount = config["autosave_amount"].as(); + if (config["showFPS"]) + _new_config.showFPS = config["showFPS"].as(); return _new_config; } @@ -141,6 +143,7 @@ namespace OpenLoco::Config node["zoom_to_cursor"] = _new_config.zoom_to_cursor; node["autosave_frequency"] = _new_config.autosave_frequency; node["autosave_amount"] = _new_config.autosave_amount; + node["showFPS"] = _new_config.showFPS; std::ofstream stream(configPath); if (stream.is_open()) diff --git a/src/OpenLoco/Config.h b/src/OpenLoco/Config.h index df4bc4f7..5e41f76b 100644 --- a/src/OpenLoco/Config.h +++ b/src/OpenLoco/Config.h @@ -156,6 +156,7 @@ namespace OpenLoco::Config bool zoom_to_cursor = true; int32_t autosave_frequency = 1; int32_t autosave_amount = 12; + bool showFPS = false; }; #pragma pack(pop) diff --git a/src/OpenLoco/Drawing/FPSCounter.cpp b/src/OpenLoco/Drawing/FPSCounter.cpp new file mode 100644 index 00000000..f861a03b --- /dev/null +++ b/src/OpenLoco/Drawing/FPSCounter.cpp @@ -0,0 +1,61 @@ +#include "FPSCounter.h" +#include "../Graphics/Colour.h" +#include "../Graphics/Gfx.h" +#include "../Localisation/StringManager.h" +#include "../Ui.h" + +#include +#include + +namespace OpenLoco::Drawing +{ + using Clock_t = std::chrono::high_resolution_clock; + using TimePoint_t = Clock_t::time_point; + + static TimePoint_t _referenceTime; + static uint32_t _currentFrameCount; + static float _currentFPS; + + static float measureFPS() + { + _currentFrameCount++; + + auto currentTime = Clock_t::now(); + auto elapsed = std::chrono::duration_cast(currentTime - _referenceTime).count() / 1000.0; + + if (elapsed > 1.0) + { + _currentFPS = _currentFrameCount / elapsed; + _currentFrameCount = 0; + _referenceTime = currentTime; + } + + return _currentFPS; + } + + void drawFPS() + { + // Measure FPS + const float fps = measureFPS(); + + // Format string + char buffer[64]; + buffer[0] = ControlCodes::font_bold; + buffer[1] = ControlCodes::outline; + buffer[2] = ControlCodes::colour_white; + + const char* formatString = (_currentFPS >= 10.0f ? "%.0f" : "%.1f"); + snprintf(&buffer[3], std::size(buffer) - 3, formatString, fps); + + auto& dpi = Gfx::screenDpi(); + + // Draw text + const int stringWidth = Gfx::getStringWidth(buffer); + const auto x = Ui::width() / 2 - (stringWidth / 2); + const auto y = 2; + Gfx::drawString(&dpi, x, y, Colour::black, buffer); + + // Make area dirty so the text doesn't get drawn over the last + Gfx::setDirtyBlocks(x - 16, y - 4, x + 16, 16); + } +} diff --git a/src/OpenLoco/Drawing/FPSCounter.h b/src/OpenLoco/Drawing/FPSCounter.h new file mode 100644 index 00000000..35431a0f --- /dev/null +++ b/src/OpenLoco/Drawing/FPSCounter.h @@ -0,0 +1,4 @@ +namespace OpenLoco::Drawing +{ + void drawFPS(); +} diff --git a/src/OpenLoco/Localisation/StringIds.h b/src/OpenLoco/Localisation/StringIds.h index 9a2a89f9..839360bb 100644 --- a/src/OpenLoco/Localisation/StringIds.h +++ b/src/OpenLoco/Localisation/StringIds.h @@ -1500,4 +1500,6 @@ namespace OpenLoco::StringIds constexpr string_id cheat_enable_sandbox_mode = 2158; constexpr string_id cheat_allow_manual_driving = 2159; constexpr string_id cheat_allow_building_while_paused = 2160; + constexpr string_id option_show_fps_counter = 2161; + constexpr string_id option_show_fps_counter_tooltip = 2162; } diff --git a/src/OpenLoco/Ui.cpp b/src/OpenLoco/Ui.cpp index 7dee8bce..6edcfb8f 100755 --- a/src/OpenLoco/Ui.cpp +++ b/src/OpenLoco/Ui.cpp @@ -31,6 +31,7 @@ #include "Config.h" #include "Console.h" +#include "Drawing/FPSCounter.h" #include "GameCommands.h" #include "Graphics/Gfx.h" #include "Gui.h" @@ -432,6 +433,12 @@ namespace OpenLoco::Ui } } + // Draw FPS counter? + if (Config::getNew().showFPS) + { + Drawing::drawFPS(); + } + // Copy pixels from the virtual screen buffer to the surface auto& dpi = Gfx::screenDpi(); if (dpi.bits != nullptr) diff --git a/src/OpenLoco/Windows/Options.cpp b/src/OpenLoco/Windows/Options.cpp index e56317fc..03978b0a 100644 --- a/src/OpenLoco/Windows/Options.cpp +++ b/src/OpenLoco/Windows/Options.cpp @@ -166,7 +166,7 @@ namespace OpenLoco::Ui::Options namespace Display { - static const Gfx::ui_size_t _window_size = { 400, 184 }; + static const Gfx::ui_size_t _window_size = { 400, 199 }; namespace Widx { @@ -179,6 +179,7 @@ namespace OpenLoco::Ui::Options display_scale, display_scale_down_btn, display_scale_up_btn, + show_fps, landscape_smoothing, gridlines_on_landscape, vehicles_min_scale, @@ -197,20 +198,18 @@ namespace OpenLoco::Ui::Options makeWidget({ 235, 64 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::display_resolution_label_format), makeWidget({ 379, 65 }, { 11, 10 }, widget_type::wt_11, 1, StringIds::dropdown), makeStepperWidgets({ 235, 79 }, { 156, 12 }, widget_type::wt_17, 1, StringIds::empty), - makeWidget({ 10, 99 }, { 346, 12 }, widget_type::checkbox, 1, StringIds::landscape_smoothing, StringIds::landscape_smoothing_tip), - makeWidget({ 10, 114 }, { 346, 12 }, widget_type::checkbox, 1, StringIds::gridlines_on_landscape, StringIds::gridlines_on_landscape_tip), - makeWidget({ 235, 133 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty, StringIds::vehicles_min_scale_tip), - makeWidget({ 379, 134 }, { 11, 10 }, widget_type::wt_11, 1, StringIds::dropdown, StringIds::vehicles_min_scale_tip), - makeWidget({ 235, 148 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty, StringIds::station_names_min_scale_tip), - makeWidget({ 379, 149 }, { 11, 10 }, widget_type::wt_11, 1, StringIds::dropdown, StringIds::station_names_min_scale_tip), - makeWidget({ 235, 163 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty), - makeWidget({ 379, 164 }, { 11, 10 }, widget_type::wt_11, 1, StringIds::dropdown), + makeWidget({ 10, 99 }, { 346, 12 }, widget_type::checkbox, 1, StringIds::option_show_fps_counter, StringIds::option_show_fps_counter_tooltip), + makeWidget({ 10, 114 }, { 346, 12 }, widget_type::checkbox, 1, StringIds::landscape_smoothing, StringIds::landscape_smoothing_tip), + makeWidget({ 10, 129 }, { 346, 12 }, widget_type::checkbox, 1, StringIds::gridlines_on_landscape, StringIds::gridlines_on_landscape_tip), + makeDropdownWidgets({ 235, 148 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty, StringIds::vehicles_min_scale_tip), + makeDropdownWidgets({ 235, 163 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty, StringIds::station_names_min_scale_tip), + makeDropdownWidgets({ 235, 178 }, { 156, 12 }, widget_type::wt_18, 1, StringIds::empty), widgetEnd(), }; static window_event_list _events; - static constexpr uint64_t enabledWidgets = Common::enabledWidgets | (1 << Display::Widx::landscape_smoothing) | (1 << Display::Widx::gridlines_on_landscape) | (1 << Display::Widx::vehicles_min_scale) | (1 << Display::Widx::vehicles_min_scale_btn) | (1 << Display::Widx::station_names_min_scale) | (1 << Display::Widx::station_names_min_scale_btn) | (1 << Display::Widx::construction_marker) | (1 << Display::Widx::construction_marker_btn) | (1 << Display::Widx::display_scale_up_btn) | (1 << Display::Widx::display_scale_down_btn); + static constexpr uint64_t enabledWidgets = Common::enabledWidgets | (1 << Widx::show_fps) | (1 << Display::Widx::landscape_smoothing) | (1 << Display::Widx::gridlines_on_landscape) | (1 << Display::Widx::vehicles_min_scale) | (1 << Display::Widx::vehicles_min_scale_btn) | (1 << Display::Widx::station_names_min_scale) | (1 << Display::Widx::station_names_min_scale_btn) | (1 << Display::Widx::construction_marker) | (1 << Display::Widx::construction_marker_btn) | (1 << Display::Widx::display_scale_up_btn) | (1 << Display::Widx::display_scale_down_btn); // 0x004BFB8C static void onMouseUp(window* w, widget_index wi) @@ -230,6 +229,15 @@ namespace OpenLoco::Ui::Options Options::tabOnMouseUp(w, wi); return; + case Widx::show_fps: + { + auto& cfg = OpenLoco::Config::getNew(); + cfg.showFPS ^= 1; + OpenLoco::Config::writeNewConfig(); + Gfx::invalidateScreen(); + return; + } + case Widx::landscape_smoothing: { auto& cfg = OpenLoco::Config::get(); @@ -556,6 +564,12 @@ namespace OpenLoco::Ui::Options w->widgets[Widx::vehicles_min_scale].text = scale_string_ids[Config::get().vehicles_min_scale]; w->widgets[Widx::station_names_min_scale].text = scale_string_ids[Config::get().station_names_min_scale]; + w->activated_widgets &= ~(1 << Widx::show_fps); + if (Config::getNew().showFPS) + { + w->activated_widgets |= (1 << Widx::show_fps); + } + w->activated_widgets &= ~(1 << Widx::landscape_smoothing); if ((Config::get().flags & Config::flags::landscape_smoothing) == 0) { diff --git a/src/OpenLoco/openloco.vcxproj b/src/OpenLoco/openloco.vcxproj index 1007eeea..d69e9f6d 100644 --- a/src/OpenLoco/openloco.vcxproj +++ b/src/OpenLoco/openloco.vcxproj @@ -24,6 +24,7 @@ + @@ -206,6 +207,7 @@ +