// Top-level overlay chrome shared across panels: which panels are visible, the // global "show debug details" switch, rolling frame-timing stats, and the main // menu bar that drives them. Keeping this in one place lets the individual panels // stay focused on their own pipeline while presenting a consistent shell. #pragma once #include namespace coop { // Visibility + verbosity shared by all panels. Panels read `debug_details` to // gate verbose diagnostics; the main loop reads the per-panel flags to decide // what to draw. struct UiState { bool show_controllers = true; bool show_injection = true; bool show_video = true; bool show_audio = true; bool show_log = true; bool debug_details = false; // off = general status; on = full diagnostics }; // The overlay panels, for the shared default layout below. enum class Panel { Injection, // left column, full height (room for hook diagnostics) Controllers, // center column, top Video, // center column, below Controllers Audio, // center column, below Video Log, // right column, full height (max room for the log stream) }; // Position + size the next ImGui window per the default 3-column layout, so panels // open without overlapping or needing a manual resize. First-use only, unless a // layout reset was just requested (then it re-applies once). Call right before the // panel's ImGui::Begin. void apply_panel_layout(Panel panel); // "View -> Reset layout" sets this; it forces apply_panel_layout to re-place every // panel on the next frame. apply_layout_end_frame() clears it after the panels draw. void request_layout_reset(); void apply_layout_end_frame(); // Rolling frame-timing over a ~1 s window, recomputed each window so the status // bar can show a stable FPS plus the min/max frame time (jitter) underneath it. class FrameStats { public: // Number of frame samples kept for the graphs (~2 s at 120 FPS). static constexpr int kHistory = 240; void tick(float dt_ms) { if (dt_ms < cur_min_) { cur_min_ = dt_ms; } if (dt_ms > cur_max_) { cur_max_ = dt_ms; } accum_ms_ += dt_ms; ++frames_; if (accum_ms_ >= 1000.0f && frames_ > 0) { avg_ms_ = accum_ms_ / static_cast(frames_); min_ms_ = cur_min_; max_ms_ = cur_max_; cur_min_ = 1.0e9f; cur_max_ = 0.0f; accum_ms_ = 0.0f; frames_ = 0; } history_[hist_pos_] = dt_ms; hist_pos_ = (hist_pos_ + 1) % kHistory; if (hist_count_ < kHistory) { ++hist_count_; } } // --- 1 s windowed aggregates (stable readout for the menu bar) --------- [[nodiscard]] float avg_ms() const { return avg_ms_; } [[nodiscard]] float min_ms() const { return min_ms_; } [[nodiscard]] float max_ms() const { return max_ms_; } [[nodiscard]] float fps() const { return avg_ms_ > 0.0f ? 1000.0f / avg_ms_ : 0.0f; } // --- Sample history (for graphs) --------------------------------------- [[nodiscard]] int history_size() const { return hist_count_; } // Copy the frame-time samples (ms) into `out` oldest-to-newest; `out` must // hold at least kHistory floats. Returns the number written. int copy_frame_ms(float* out) const { const int start = (hist_pos_ - hist_count_ + kHistory * 2) % kHistory; for (int i = 0; i < hist_count_; ++i) { out[i] = history_[(start + i) % kHistory]; } return hist_count_; } // min / max / mean over the whole retained history (order-independent). void history_stats(float& min_ms, float& max_ms, float& avg_ms) const { if (hist_count_ == 0) { min_ms = max_ms = avg_ms = 0.0f; return; } float mn = 1.0e9f, mx = 0.0f, sum = 0.0f; for (int i = 0; i < hist_count_; ++i) { const float v = history_[i]; mn = std::min(mn, v); mx = std::max(mx, v); sum += v; } min_ms = mn; max_ms = mx; avg_ms = sum / static_cast(hist_count_); } private: float accum_ms_ = 0.0f; int frames_ = 0; float cur_min_ = 1.0e9f; float cur_max_ = 0.0f; float avg_ms_ = 0.0f; float min_ms_ = 0.0f; float max_ms_ = 0.0f; float history_[kHistory] = {}; int hist_pos_ = 0; int hist_count_ = 0; }; // Draw the main menu bar (app name, View menu of panel toggles + debug switch, // and a right-aligned FPS readout). Returns the menu bar height so the caller can // keep panels clear of it on first layout. float draw_main_menu_bar(UiState& ui, const FrameStats& stats); } // namespace coop