diff options
| author | Hannes Schulze <haschu0103@outlook.de> | 2025-02-06 18:24:59 +0100 |
|---|---|---|
| committer | Hannes Schulze <haschu0103@outlook.de> | 2025-02-06 18:24:59 +0100 |
| commit | d060e3a50c987d10799ccc739bdbf9d7d7a13c10 (patch) | |
| tree | 4b5585383ada85608b57bcf76e5828817962fddf | |
| parent | dbed1c0fa5599b1b96c221b89b10f1e350351645 (diff) | |
implement settings
25 files changed, 334 insertions, 47 deletions
diff --git a/app/mac/view-controller.mm b/app/mac/view-controller.mm index 8695634..e2e71f9 100644 --- a/app/mac/view-controller.mm +++ b/app/mac/view-controller.mm @@ -22,7 +22,6 @@ { auto view = [[SKView alloc] init]; [view setIgnoresSiblingOrder:YES]; - [view setShowsFPS:YES]; [view presentScene:_scene]; [self setView:view]; diff --git a/lib/nes/app/CMakeLists.txt b/lib/nes/app/CMakeLists.txt index c0d6909..df0769e 100644 --- a/lib/nes/app/CMakeLists.txt +++ b/lib/nes/app/CMakeLists.txt @@ -4,7 +4,8 @@ target_sources( application.hh application.cc action.hh - file-browser.hh) + file-browser.hh + preferences.hh) add_subdirectory(input) add_subdirectory(graphics) diff --git a/lib/nes/app/application.cc b/lib/nes/app/application.cc index 01d3f0b..015e641 100644 --- a/lib/nes/app/application.cc +++ b/lib/nes/app/application.cc @@ -21,15 +21,25 @@ namespace nes::app popup->render(r); } + if (preferences_.get_fps_counter()) + { + r.render_rect(30, 29, 2, 1, color::fixed_black); + auto const attrs = text_attributes{} + .set_max_width(2) + .set_alignment(text_alignment::right); + r.render_text_format(32, 29, color::fixed_white, attrs, "{}", fps_counter.get_fps()); + } + base.switch_buffers(); } application::application(display& display, input_device_keyboard& keyboard, file_browser& file_browser) - : display_{ display } - , input_manager_{ keyboard } + : input_manager_{ keyboard } , file_browser_{ file_browser } + , display_{ preferences_, display } , screen_title_{ keyboard } , screen_browser_{ keyboard, file_browser } + , screen_settings_{ input_manager_, preferences_ } , screen_error_{ keyboard } , screen_confirm_quit_{ keyboard } { @@ -38,6 +48,8 @@ namespace nes::app auto application::frame(std::chrono::microseconds const elapsed_time) -> void { + display_.fps_counter.frame(elapsed_time); + if (display_.popup) { auto const a = display_.popup->process_events(); @@ -99,21 +111,18 @@ namespace nes::app } case action::type::go_to_title: { - console_.clear(); - display_.screen = &screen_title_; - display_.popup = nullptr; + go_to_screen(&screen_title_); break; } case action::type::go_to_browser: + case action::type::confirm_quit: { - console_.clear(); - display_.screen = &screen_browser_; - display_.popup = nullptr; + go_to_screen(&screen_browser_); break; } case action::type::go_to_settings: { - // TODO + go_to_screen(&screen_settings_); break; } case action::type::go_to_help: @@ -132,13 +141,6 @@ namespace nes::app display_.screen = nullptr; break; } - case action::type::confirm_quit: - { - display_.popup = nullptr; - display_.screen = &screen_browser_; - console_.clear(); - break; - } case action::type::launch_game: { u8 buffer[sys::cartridge::max_file_size]; @@ -176,4 +178,11 @@ namespace nes::app screen_error_.set_action(action); display_.popup = &screen_error_; } + + auto application::go_to_screen(screen* screen) -> void + { + console_.clear(); + display_.screen = screen; + display_.popup = nullptr; + } } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/application.hh b/lib/nes/app/application.hh index 66b5206..c23f654 100644 --- a/lib/nes/app/application.hh +++ b/lib/nes/app/application.hh @@ -3,12 +3,15 @@ #include "nes/app/input/input-manager.hh" #include "nes/app/ui/screen-title.hh" #include "nes/app/ui/screen-browser.hh" +#include "nes/app/ui/screen-settings.hh" #include "nes/app/ui/screen-freeze.hh" #include "nes/app/ui/screen-error.hh" #include "nes/app/ui/screen-confirm-quit.hh" +#include "nes/app/preferences.hh" #include "nes/sys/nes.hh" #include "nes/common/containers/box.hh" #include "nes/common/display.hh" +#include "nes/common/fps-counter.hh" #include "nes/common/types.hh" #include <chrono> @@ -28,13 +31,17 @@ namespace nes::app /// buffers. class display_proxy final : public display { + preferences& preferences_; + public: + fps_counter fps_counter; display& base; screen* popup{ nullptr }; screen* screen{ nullptr }; - explicit display_proxy(display& base) - : base{ base } + explicit display_proxy(preferences& preferences, display& base) + : preferences_{ preferences } + , base{ base } { } @@ -43,18 +50,20 @@ namespace nes::app auto switch_buffers() -> void override; }; - display_proxy display_; input_manager input_manager_; + preferences preferences_; file_browser& file_browser_; + display_proxy display_; box<sys::nes> console_{}; screen_title screen_title_; screen_browser screen_browser_; + screen_settings screen_settings_; screen_freeze screen_freeze_; screen_error screen_error_; screen_confirm_quit screen_confirm_quit_; public: - explicit application(display&, input_device_keyboard&, file_browser& file_browser); + explicit application(display&, input_device_keyboard&, file_browser&); application(application const&) = delete; application(application&&) = delete; @@ -70,5 +79,6 @@ namespace nes::app private: auto handle_action(action const&) -> void; auto show_error(std::string_view message, status error, action const& action = action::close_popup()) -> void; + auto go_to_screen(screen*) -> void; }; } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/graphics/renderer.cc b/lib/nes/app/graphics/renderer.cc index 564125b..4f0d562 100644 --- a/lib/nes/app/graphics/renderer.cc +++ b/lib/nes/app/graphics/renderer.cc @@ -64,7 +64,7 @@ namespace nes::app } auto renderer::render_text( - i32 x, i32 const y, std::string_view const str, color const c, text_attributes const attributes) -> void + i32 x, i32 const y, std::string_view const str, color const c, text_attributes const attributes) -> u32 { // Enforce max length char buffer[width]; @@ -121,9 +121,10 @@ namespace nes::app // Draw the text for (auto i = u32{ 0 }; i < length; ++i) { - auto const character = static_cast<u32>(buffer[i]); - render_mask_tile(x + static_cast<i32>(i), y, resolve_character(character), c); + render_mask_tile(x + static_cast<i32>(i), y, resolve_character(buffer[i]), c); } + + return length; } auto renderer::render_rect(i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void @@ -217,7 +218,7 @@ namespace nes::app return rgb::from_hex(0x000000); } - auto renderer::resolve_character(u32 const c) const -> mask_tile + auto renderer::resolve_character(char const c) const -> mask_tile { switch (c) { @@ -260,6 +261,10 @@ namespace nes::app case '9': return tiles::digit_9; case '&': return tiles::symbol_ampersand; case '\'': return tiles::symbol_apostrophe; + case '@': return tiles::symbol_at; + case '\\': return tiles::symbol_backslash; + case '{': return tiles::symbol_brace_open; + case '}': return tiles::symbol_brace_close; case '[': return tiles::symbol_bracket_open; case ']': return tiles::symbol_bracket_close; case ':': return tiles::symbol_colon; @@ -269,18 +274,21 @@ namespace nes::app case '!': return tiles::symbol_exclamation_mark; case '>': return tiles::symbol_greater; case '#': return tiles::symbol_hashtag; + case '^': return tiles::symbol_hat; case '<': return tiles::symbol_less; case '-': return tiles::symbol_minus; case '(': return tiles::symbol_paren_open; case ')': return tiles::symbol_paren_close; case '%': return tiles::symbol_percent; case '.': return tiles::symbol_period; + case '|': return tiles::symbol_pipe; case '+': return tiles::symbol_plus; case '?': return tiles::symbol_question_mark; case '"': return tiles::symbol_quotation_mark; case ';': return tiles::symbol_semicolon; case '/': return tiles::symbol_slash; case '*': return tiles::symbol_star; + case '_': return tiles::symbol_underscore; default: return tiles::symbol_unknown; } } diff --git a/lib/nes/app/graphics/renderer.hh b/lib/nes/app/graphics/renderer.hh index d4d7cc4..2e24ad3 100644 --- a/lib/nes/app/graphics/renderer.hh +++ b/lib/nes/app/graphics/renderer.hh @@ -46,7 +46,7 @@ namespace nes::app /// Render an image at the given tile coordinates. auto render_image(i32 x, i32 y, image_view) -> void; /// Render a string of text at the given tile coordinates (one tile per character). - auto render_text(i32 x, i32 y, std::string_view, color, text_attributes = text_attributes{}) -> void; + auto render_text(i32 x, i32 y, std::string_view, color, text_attributes = text_attributes{}) -> u32; template<typename... Args> auto render_text_format( @@ -55,8 +55,7 @@ namespace nes::app { string_buffer<width> buffer{}; buffer.append_format(fmt, args...); - render_text(x, y, buffer.get_result(), c, attrs); - return static_cast<u32>(buffer.get_result().length()); + return render_text(x, y, buffer.get_result(), c, attrs); } template<typename... Args> @@ -75,6 +74,6 @@ namespace nes::app private: auto resolve_color(color) const -> rgb; - auto resolve_character(u32) const -> mask_tile; + auto resolve_character(char) const -> mask_tile; }; } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/graphics/tiles/CMakeLists.txt b/lib/nes/app/graphics/tiles/CMakeLists.txt index eced5ca..5dced68 100644 --- a/lib/nes/app/graphics/tiles/CMakeLists.txt +++ b/lib/nes/app/graphics/tiles/CMakeLists.txt @@ -40,6 +40,10 @@ nes_generate_tiles( digit_8.png digit_9.png symbol_ampersand.png + symbol_at.png + symbol_backslash.png + symbol_brace_open.png + symbol_brace_close.png symbol_apostrophe.png symbol_bracket_open.png symbol_bracket_close.png @@ -50,18 +54,21 @@ nes_generate_tiles( symbol_exclamation_mark.png symbol_greater.png symbol_hashtag.png + symbol_hat.png symbol_less.png symbol_minus.png symbol_paren_open.png symbol_paren_close.png symbol_percent.png symbol_period.png + symbol_pipe.png symbol_plus.png symbol_question_mark.png symbol_quotation_mark.png symbol_semicolon.png symbol_slash.png symbol_star.png + symbol_underscore.png symbol_unknown.png) nes_generate_tiles( diff --git a/lib/nes/app/graphics/tiles/symbol_at.png b/lib/nes/app/graphics/tiles/symbol_at.png Binary files differnew file mode 100644 index 0000000..e399054 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_at.png diff --git a/lib/nes/app/graphics/tiles/symbol_backslash.png b/lib/nes/app/graphics/tiles/symbol_backslash.png Binary files differnew file mode 100644 index 0000000..8d98ef0 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_backslash.png diff --git a/lib/nes/app/graphics/tiles/symbol_brace_close.png b/lib/nes/app/graphics/tiles/symbol_brace_close.png Binary files differnew file mode 100644 index 0000000..bb24228 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_brace_close.png diff --git a/lib/nes/app/graphics/tiles/symbol_brace_open.png b/lib/nes/app/graphics/tiles/symbol_brace_open.png Binary files differnew file mode 100644 index 0000000..32bdb99 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_brace_open.png diff --git a/lib/nes/app/graphics/tiles/symbol_hat.png b/lib/nes/app/graphics/tiles/symbol_hat.png Binary files differnew file mode 100644 index 0000000..ab21714 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_hat.png diff --git a/lib/nes/app/graphics/tiles/symbol_pipe.png b/lib/nes/app/graphics/tiles/symbol_pipe.png Binary files differnew file mode 100644 index 0000000..9db3443 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_pipe.png diff --git a/lib/nes/app/graphics/tiles/symbol_underscore.png b/lib/nes/app/graphics/tiles/symbol_underscore.png Binary files differnew file mode 100644 index 0000000..1823c95 --- /dev/null +++ b/lib/nes/app/graphics/tiles/symbol_underscore.png diff --git a/lib/nes/app/input/input-device-controller.hh b/lib/nes/app/input/input-device-controller.hh index 69dd2a8..93147cd 100644 --- a/lib/nes/app/input/input-device-controller.hh +++ b/lib/nes/app/input/input-device-controller.hh @@ -2,7 +2,6 @@ #include "nes/app/input/input-device.hh" #include "nes/common/containers/list.hh" -#include <string_view> namespace nes::app { @@ -23,9 +22,6 @@ namespace nes::app /// controller already selected. virtual auto is_reliable() const -> bool = 0; - /// Human-readable name for the controller. - virtual auto get_name() const -> std::string_view = 0; - /// Get the (persistent) player index for the controller, if already set before. virtual auto get_index() const -> index = 0; diff --git a/lib/nes/app/input/input-device-keyboard.hh b/lib/nes/app/input/input-device-keyboard.hh index 23ee65c..ef89c59 100644 --- a/lib/nes/app/input/input-device-keyboard.hh +++ b/lib/nes/app/input/input-device-keyboard.hh @@ -43,6 +43,8 @@ namespace nes::app return res; } + auto get_name() const -> std::string_view final { return "Keyboard"; } + protected: explicit input_device_keyboard() = default; }; diff --git a/lib/nes/app/input/input-device.hh b/lib/nes/app/input/input-device.hh index cc03057..008825d 100644 --- a/lib/nes/app/input/input-device.hh +++ b/lib/nes/app/input/input-device.hh @@ -1,6 +1,7 @@ #pragma once #include "nes/sys/types/button-mask.hh" +#include <string_view> namespace nes::app { @@ -15,8 +16,12 @@ namespace nes::app auto operator=(input_device const&) -> input_device& = delete; auto operator=(input_device&&) -> input_device& = delete; + /// Read the controller button state. virtual auto read_buttons() -> sys::button_mask = 0; + /// Human-readable name for the device. + virtual auto get_name() const -> std::string_view = 0; + protected: explicit input_device() = default; }; diff --git a/lib/nes/app/input/input-manager.cc b/lib/nes/app/input/input-manager.cc index ceb6690..e88ac84 100644 --- a/lib/nes/app/input/input-manager.cc +++ b/lib/nes/app/input/input-manager.cc @@ -15,6 +15,7 @@ namespace nes::app return res; } + auto get_name() const -> std::string_view override { return "(None)"; } auto read_buttons() -> sys::button_mask override { return sys::button_mask{}; } }; } // namespace @@ -36,6 +37,10 @@ namespace nes::app // This controller was previously used and assigned an index -> replace the current device with this // (known) controller. auto const i = static_cast<u32>(index); + if (keyboard_index_ == index) + { + keyboard_index_ = input_device_controller::index::unused; + } if (inputs_[i].controller) { inputs_[i].controller->set_index(input_device_controller::index::unused); @@ -56,6 +61,43 @@ namespace nes::app fill_unassigned_inputs(); } + auto input_manager::toggle_input(u32 const i) -> void + { + // If currently none/keyboard, use the first available controller. + auto curr_was_prev = inputs_[i].controller == nullptr; + if (curr_was_prev && keyboard_index_ == input_device_controller::index::unused) + { + // If currently none and keyboard is available, switch to keyboard. + keyboard_index_ = static_cast<input_device_controller::index>(i); + inputs_[i].controller = nullptr; + inputs_[i].device = keyboard_; + return; + } + + // Reset old controller index + if (inputs_[i].controller) { inputs_[i].controller->set_index(input_device_controller::index::unused); } + if (keyboard_index_ == static_cast<input_device_controller::index>(i)) { keyboard_index_ = input_device_controller::index::unused; } + + for (auto& controller : controllers_) + { + if (&controller == inputs_[i].controller) + { + curr_was_prev = true; + } + else if (curr_was_prev && controller.get_index() == input_device_controller::index::unused) + { + inputs_[i].controller = &controller; + inputs_[i].controller->set_index(static_cast<input_device_controller::index>(i)); + inputs_[i].device = &controller; + return; + } + } + + // No successor found, switch to none. + inputs_[i].controller = nullptr; + inputs_[i].device = &input_device_dummy::get_instance(); + } + auto input_manager::fill_unassigned_inputs() -> void { auto it = controllers_.begin(); @@ -87,6 +129,7 @@ namespace nes::app // We're all out of controllers -> fall back to the keyboard (if this is the first missing controller) // or nothing. inputs_[i].device = fallback; + if (fallback == keyboard_) { keyboard_index_ = static_cast<input_device_controller::index>(i); } fallback = &input_device_dummy::get_instance(); // Only use the keyboard as a fallback once. } } diff --git a/lib/nes/app/input/input-manager.hh b/lib/nes/app/input/input-manager.hh index b0b60e8..7a63326 100644 --- a/lib/nes/app/input/input-manager.hh +++ b/lib/nes/app/input/input-manager.hh @@ -16,6 +16,7 @@ namespace nes::app static constexpr auto input_count = u32{ 2 }; input_device_keyboard* keyboard_; + input_device_controller::index keyboard_index_{ input_device_controller::index::unused }; list<input_device_controller> controllers_; struct { @@ -34,6 +35,9 @@ namespace nes::app auto get_input_1() const -> input_device& { return *inputs_[0].device; } auto get_input_2() const -> input_device& { return *inputs_[1].device; } + auto toggle_input_1() -> void { toggle_input(0); } + auto toggle_input_2() -> void { toggle_input(1); } + auto get_keyboard() const -> input_device_keyboard& { return *keyboard_; } auto get_controllers() const -> list<input_device_controller> const& { return controllers_; } @@ -41,6 +45,7 @@ namespace nes::app auto remove_controller(input_device_controller&) -> void; private: + auto toggle_input(u32) -> void; auto fill_unassigned_inputs() -> void; }; } // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/preferences.hh b/lib/nes/app/preferences.hh new file mode 100644 index 0000000..0c13755 --- /dev/null +++ b/lib/nes/app/preferences.hh @@ -0,0 +1,21 @@ +#pragma once + +namespace nes::app +{ + /// Manages application preferences. + class preferences + { + bool fps_counter_{ false }; + + public: + explicit preferences() = default; + + preferences(preferences const&) = delete; + preferences(preferences&&) = delete; + auto operator=(preferences const&) -> preferences& = delete; + auto operator=(preferences&&) -> preferences& = delete; + + auto get_fps_counter() const -> bool { return fps_counter_; } + auto set_fps_counter(bool const v) -> void { fps_counter_ = v; } + }; +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/ui/screen-settings.cc b/lib/nes/app/ui/screen-settings.cc index 4d32662..98070d7 100644 --- a/lib/nes/app/ui/screen-settings.cc +++ b/lib/nes/app/ui/screen-settings.cc @@ -1,5 +1,98 @@ -// -// Created by Hannes Schulze on 06.02.25. -// +#include "nes/app/ui/screen-settings.hh" +#include "nes/app/input/input-manager.hh" +#include "nes/app/preferences.hh" -#include "screen-settings.hh" +namespace nes::app +{ + auto screen_settings::selection_impl::render_item( + renderer& renderer, item const& item, i32 const x, i32 const y, u32 const width, color const c) const -> void + { + auto const render = [&](std::string_view const label, std::string_view const value) + { + auto const label_length = renderer.render_text(x, y, label, c); + auto const value_attrs = text_attributes{} + .set_alignment(text_alignment::right) + .set_ellipsize_mode(ellipsize_mode::end) + .set_max_width(width - label_length - 2); + renderer.render_text(x + static_cast<i32>(width), y, value, color::foreground_secondary, value_attrs); + }; + switch (item) + { + case item::controller_1: + render("Controller 1:", input_manager_.get_input_1().get_name()); + break; + case item::controller_2: + render("Controller 2:", input_manager_.get_input_2().get_name()); + break; + case item::fps_counter: + render("FPS Counter:", preferences_.get_fps_counter() ? "Yes" : "No"); + break; + } + } + + auto screen_settings::selection_impl::load_page(item (&items)[page_size], u32) -> u32 + { + items[0] = item::controller_1; + items[1] = item::controller_2; + items[2] = item::fps_counter; + return 3; + } + + screen_settings::screen_settings(input_manager& input_manager, preferences& preferences) + : input_manager_{ input_manager } + , preferences_{ preferences } + , selection_{ input_manager, preferences } + { + selection_.set_page_count(1); + } + + auto screen_settings::render(renderer& renderer) -> void + { + renderer.render_fill(color::background_primary); + renderer.render_rect(0, 0, 32, 3, color::background_secondary); + + auto const page_length = renderer.render_text_format( + 30, 1, color::foreground_secondary, text_attributes{}.set_alignment(text_alignment::right), + "Page {}/{}", selection_.get_current_page() + 1, selection_.get_page_count()); + + renderer.render_text( + 2, 1, "Settings", color::foreground_primary, + text_attributes{}.set_max_width(26 - page_length).set_ellipsize_mode(ellipsize_mode::middle)); + + selection_.render(renderer); + } + + auto screen_settings::process_events() -> action + { + while (auto const event = input_manager_.get_keyboard().poll_event()) + { + if (event == input_event::key_down(key::space)) + { + selection_.go_next(); + } + else if (event == input_event::key_down(key::arrow_up)) + { + selection_.go_up(); + } + else if (event == input_event::key_down(key::arrow_down)) + { + selection_.go_down(); + } + else if (event == input_event::key_down(key::backspace)) + { + return action::go_to_title(); + } + else if (event == input_event::key_down(key::enter)) + { + switch (*selection_.get_selected()) + { + case item::controller_1: input_manager_.toggle_input_1(); break; + case item::controller_2: input_manager_.toggle_input_2(); break; + case item::fps_counter: preferences_.set_fps_counter(!preferences_.get_fps_counter()); break; + } + } + } + + return action{}; + } +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/app/ui/screen-settings.hh b/lib/nes/app/ui/screen-settings.hh index 8f510ca..8910c5c 100644 --- a/lib/nes/app/ui/screen-settings.hh +++ b/lib/nes/app/ui/screen-settings.hh @@ -1,16 +1,51 @@ -// -// Created by Hannes Schulze on 06.02.25. -// +#pragma once -#ifndef SCREEN_SETTINGS_HH -#define SCREEN_SETTINGS_HH +#include "nes/app/ui/screen.hh" +#include "nes/app/ui/selection.hh" +namespace nes::app +{ + class input_manager; + class preferences; + /// Screen used to configure various emulator options. + class screen_settings final : public screen + { + static constexpr auto page_size = u32{ 8 }; -class screen_settings { + enum class item + { + controller_1, + controller_2, + fps_counter, + }; -}; + class selection_impl final : public selection<item, page_size> + { + input_manager& input_manager_; + preferences& preferences_; + public: + explicit selection_impl(input_manager& input_manager, preferences& preferences) + : selection{ 2, 5, 28 } + , input_manager_{ input_manager } + , preferences_{ preferences } + { + } + protected: + auto render_item(renderer&, item const&, i32 x, i32 y, u32 width, color) const -> void override; + auto load_page(item(&)[page_size], u32 page) -> u32 override; + }; -#endif //SCREEN_SETTINGS_HH + input_manager& input_manager_; + preferences& preferences_; + selection_impl selection_; + + public: + explicit screen_settings(input_manager&, preferences&); + + auto render(renderer&) -> void override; + auto process_events() -> action override; + }; +} // namespace nes::app
\ No newline at end of file diff --git a/lib/nes/common/CMakeLists.txt b/lib/nes/common/CMakeLists.txt index 3920d36..9a7cc6b 100644 --- a/lib/nes/common/CMakeLists.txt +++ b/lib/nes/common/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources( debug.hh rgb.hh display.hh - status.hh) + status.hh + fps-counter.hh + fps-counter.cc) add_subdirectory(containers)
\ No newline at end of file diff --git a/lib/nes/common/fps-counter.cc b/lib/nes/common/fps-counter.cc new file mode 100644 index 0000000..ef6169b --- /dev/null +++ b/lib/nes/common/fps-counter.cc @@ -0,0 +1,29 @@ +#include "nes/common/fps-counter.hh" + +namespace nes +{ + auto fps_counter::frame(std::chrono::microseconds const elapsed_time) -> void + { + if (elapsed_time.count() <= 0) { return; } + + if (buffer_position_ == 0) + { + // Update fps count. + auto sum = u32{ 0 }; + for (auto i = u32{ 0 }; i < buffer_size; ++i) { sum += frame_times_[i]; } + if (sum > 0) + { + buffered_fps_ = (buffer_size * 1000000) / sum; + } + else + { + buffered_fps_ = 0; + } + } + + // Add the frame time to the buffer, replacing any existing content in the ring buffer. + auto const frame_time = static_cast<u32>(elapsed_time.count()); + frame_times_[buffer_position_] = frame_time; + buffer_position_ = (buffer_position_ + 1) % buffer_size; + } +} // namespace nes
\ No newline at end of file diff --git a/lib/nes/common/fps-counter.hh b/lib/nes/common/fps-counter.hh new file mode 100644 index 0000000..96db98c --- /dev/null +++ b/lib/nes/common/fps-counter.hh @@ -0,0 +1,23 @@ +#pragma once + +#include "nes/common/types.hh" +#include <chrono> + +namespace nes +{ + /// Counts frames. + class fps_counter + { + static constexpr auto buffer_size = u32{ 20 }; + + u32 frame_times_[buffer_size]{}; // The time until the next frame for the last <buffer_size> frames. + u32 buffer_position_{ 0 }; // Position in the ring buffer. + u32 buffered_fps_{ 0 }; // Delay updates to FPS to one update per round in the ring buffer. + + public: + explicit fps_counter() = default; + + auto frame(std::chrono::microseconds elapsed_time) -> void; + auto get_fps() const -> u32 { return buffered_fps_; } + }; +} // namespace nes
\ No newline at end of file |
