diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index a676302f1..5c505f7f7 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -94,6 +94,13 @@ bool EmulatorWindow::Initialize() { window_->on_key_down.AddListener([this](KeyEvent* e) { bool handled = true; switch (e->key_code()) { + case 0x48: { // h + if (e->is_shift_pressed()) { + window_->FPSTextScale(); + } else { + window_->ToggleFPS(); + } + } break; case 0x4F: { // o if (e->is_ctrl_pressed()) { FileOpen(); diff --git a/src/xenia/ui/window.cc b/src/xenia/ui/window.cc index c039cec0a..c4a0f6324 100644 --- a/src/xenia/ui/window.cc +++ b/src/xenia/ui/window.cc @@ -15,6 +15,7 @@ #include "xenia/base/assert.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" +#include "xenia/base/profiling.h" #include "xenia/ui/imgui_drawer.h" namespace xe { @@ -183,6 +184,32 @@ void Window::OnPaint(UIEvent* e) { tick_frequency)); fps_update_time_ticks_ = now_ticks; fps_frame_count_ = 0; +#if XE_OPTION_PROFILING + // This means FPS counter will not work with profiling disabled (e.g. on + // Linux) + float fToMs = + MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + uint64_t nFlipTicks = 0; + { + std::lock_guard lock(MicroProfileGetMutex()); + MicroProfile& S = *MicroProfileGet(); + nFlipTicks = S.nFlipTicks; + } + float fMs = fToMs * nFlipTicks; + + if (fMs != 0.0f) { + game_fps_ = static_cast(1000.0f / fMs); + } +#endif + + title_fps_text_ = base_title_; + title_fps_text_ += L" | "; + title_fps_text_ += std::to_wstring(game_fps_); + title_fps_text_ += L" FPS"; + set_title(title_fps_text_, false); + + osd_fps_text_ = std::to_string(game_fps_); + osd_fps_text_ += " FPS"; } GraphicsContextLock context_lock(context_.get()); @@ -213,6 +240,20 @@ void Window::OnPaint(UIEvent* e) { ForEachListener([e](auto listener) { listener->OnPaint(e); }); on_paint(e); + if (display_fps_) { + ImGui::Begin("FPS", (bool*)0, {0.0f, 0.0f}, 0.0f, + ImGuiWindowFlags_::ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_::ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_::ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_::ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_::ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_::ImGuiWindowFlags_NoInputs); + ImGui::SetWindowFontScale(fps_font_scale_); + ImGui::Text("%s", osd_fps_text_.c_str()); + ImGui::SetWindowSize({0.0f, 0.0f}); // Resize to fit content + ImGui::End(); + } + // Flush ImGui buffers before we swap. ImGui::Render(); imgui_drawer_->RenderDrawLists(); diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index 2cb5c8c34..8ead44152 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -48,14 +48,25 @@ class Window { virtual void DisableMainMenu() = 0; const std::wstring& title() const { return title_; } - virtual bool set_title(const std::wstring& title) { + virtual bool set_title(const std::wstring& title, + bool set_base_title = true) { if (title == title_) { return false; } title_ = title; + if (set_base_title) { + base_title_ = title; + } return true; } + void ToggleFPS() { display_fps_ = !display_fps_; } + + void FPSTextScale() { + fps_font_scale_ = fps_font_scale_ * 2.0f; + fps_font_scale_ = (fps_font_scale_ > 4.0f) ? 1.0f : fps_font_scale_; + } + virtual bool SetIcon(const void* buffer, size_t size) = 0; void ResetIcon() { SetIcon(nullptr, 0); } @@ -169,6 +180,7 @@ class Window { Loop* loop_ = nullptr; std::unique_ptr main_menu_; std::wstring title_; + std::wstring base_title_; int32_t width_ = 0; int32_t height_ = 0; bool has_focus_ = true; @@ -184,6 +196,13 @@ class Window { uint64_t fps_frame_count_ = 0; uint64_t last_paint_time_ticks_ = 0; + bool display_fps_ = false; + uint32_t game_fps_ = 0; + float fps_font_scale_ = 1.0f; + + std::wstring title_fps_text_; + std::string osd_fps_text_; + bool modifier_shift_pressed_ = false; bool modifier_cntrl_pressed_ = false; bool modifier_alt_pressed_ = false; diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc index 2e94f6ddc..166fd599e 100644 --- a/src/xenia/ui/window_gtk.cc +++ b/src/xenia/ui/window_gtk.cc @@ -99,8 +99,8 @@ void GTKWindow::OnClose() { super::OnClose(); } -bool GTKWindow::set_title(const std::wstring& title) { - if (!super::set_title(title)) { +bool GTKWindow::set_title(const std::wstring& title, bool set_base_title) { + if (!super::set_title(title, set_base_title)) { return false; } gtk_window_set_title(GTK_WINDOW(window_), (gchar*)title.c_str()); diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h index 22921a5e1..ed970d47c 100644 --- a/src/xenia/ui/window_gtk.h +++ b/src/xenia/ui/window_gtk.h @@ -35,7 +35,8 @@ class GTKWindow : public Window { void EnableMainMenu() override {} void DisableMainMenu() override {} - bool set_title(const std::wstring& title) override; + bool set_title(const std::wstring& title, + bool set_base_title = true) override; bool SetIcon(const void* buffer, size_t size) override; diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index 2912ec4a0..2f29d83c6 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -193,8 +193,8 @@ void Win32Window::DisableMainMenu() { } } -bool Win32Window::set_title(const std::wstring& title) { - if (!super::set_title(title)) { +bool Win32Window::set_title(const std::wstring& title, bool set_base_title) { + if (!super::set_title(title, set_base_title)) { return false; } SetWindowText(hwnd_, title.c_str()); diff --git a/src/xenia/ui/window_win.h b/src/xenia/ui/window_win.h index d885042ea..612acbc4b 100644 --- a/src/xenia/ui/window_win.h +++ b/src/xenia/ui/window_win.h @@ -33,7 +33,8 @@ class Win32Window : public Window { void EnableMainMenu() override; void DisableMainMenu() override; - bool set_title(const std::wstring& title) override; + bool set_title(const std::wstring& title, + bool set_base_title = true) override; bool SetIcon(const void* buffer, size_t size) override;