diff --git a/src/experimental/platform/ux/gui.cc b/src/experimental/platform/ux/gui.cc index b0ffbadf..2892b185 100644 --- a/src/experimental/platform/ux/gui.cc +++ b/src/experimental/platform/ux/gui.cc @@ -28,6 +28,7 @@ #include #include #include "experimental/platform/helpers.h" +#include "experimental/platform/sim/sim_profiler.h" #include "experimental/platform/sim/step_control.h" #include "experimental/platform/ux/imgui_widgets.h" #include "experimental/platform/ux/interaction.h" @@ -275,6 +276,9 @@ ImVec4 ConfigureDockingLayout() { ImGui::DockBuilderSplitNode(inspector, ImGuiDir_Down, kStatsRelHeight, &properties, &inspector); + ImGuiID profiler = 0; + ImGui::DockBuilderSplitNode(main, ImGuiDir_Right, 0.42f, &profiler, &main); + ImGui::DockBuilderDockWindow("Dockspace", main); ImGui::DockBuilderDockWindow("Options", options); ImGui::DockBuilderDockWindow("Explorer", inspector); @@ -282,6 +286,7 @@ ImVec4 ConfigureDockingLayout() { ImGui::DockBuilderDockWindow("Inspector", inspector); ImGui::DockBuilderDockWindow("Properties", properties); ImGui::DockBuilderDockWindow("Stats", stats); + ImGui::DockBuilderDockWindow("Profiler", profiler); ImGui::DockBuilderFinish(root); } @@ -1392,4 +1397,56 @@ void StatsGui(const mjModel* model, const mjData* data, bool paused, ImGui::Columns(); } +void ProfilerGui(const mjModel* model, mjData* data, SimProfiler* profiler) { + ImGui::SetWindowFontScale(0.8f); + ImVec2 avail = ImGui::GetContentRegionAvail(); + const float pad = ImGui::GetStyle().ItemSpacing.x; + const float aspect = avail.y > 0 ? avail.x / avail.y : 1.0f; + + ImVec2 plot_size; + int cols; + if (aspect < 0.8f) { + plot_size.x = avail.x; + plot_size.y = (avail.y - pad * 3.0f) * 0.25f; + cols = 1; + } else if (aspect < 1.8f) { + plot_size.x = (avail.x - pad) * 0.5f; + plot_size.y = (avail.y - pad) * 0.5f; + cols = 2; + } else { + plot_size.x = (avail.x - pad * 3.0f) * 0.25f; + plot_size.y = avail.y; + cols = 4; + } + + int current_col = 0; + auto advance = [&]() { + current_col++; + if (current_col < cols) { + ImGui::SameLine(); + } else { + current_col = 0; + } + }; + + if (cols == 2) { + // In 2x2 layout, vertically stack charts with the same x-axis. + CountsGui(model, data, plot_size); + advance(); + profiler->DimensionsGraph(plot_size); + advance(); + ConvergenceGui(model, data, plot_size); + advance(); + profiler->CpuTimeGraph(plot_size); + } else { + CountsGui(model, data, plot_size); + advance(); + ConvergenceGui(model, data, plot_size); + advance(); + profiler->DimensionsGraph(plot_size); + advance(); + profiler->CpuTimeGraph(plot_size); + } +} + } // namespace mujoco::platform diff --git a/src/experimental/platform/ux/gui.h b/src/experimental/platform/ux/gui.h index cc2ae08f..3599a93d 100644 --- a/src/experimental/platform/ux/gui.h +++ b/src/experimental/platform/ux/gui.h @@ -28,6 +28,7 @@ #include #include +#include "experimental/platform/sim/sim_profiler.h" #include "experimental/platform/sim/step_control.h" namespace mujoco::platform { @@ -148,6 +149,9 @@ void ConvergenceGui(const mjModel* model, mjData* data, void CountsGui(const mjModel* model, mjData* data, ImVec2 plot_size = ImVec2(-1, 0)); +// UX for Profiler panel combining Solver and Performance metrics. +void ProfilerGui(const mjModel* model, mjData* data, SimProfiler* profiler); + // UX for displaying basic simulation information. Note that the pause state and // FPS needs to be tracked by the caller and passed here to be displayed. void StatsGui(const mjModel* model, const mjData* data, bool paused, float fps); diff --git a/src/experimental/platform/ux/imgui_widgets.cc b/src/experimental/platform/ux/imgui_widgets.cc index 305f292c..cf33bc8d 100644 --- a/src/experimental/platform/ux/imgui_widgets.cc +++ b/src/experimental/platform/ux/imgui_widgets.cc @@ -382,10 +382,10 @@ ImPlotFlags ImPlot_SetupPlotFlags(ImVec2 plot_size) { ImPlotFlags flags = ImPlotFlags_None; if (plot_size.x > 0 && plot_size.y > 0) { const float min_dim = std::min(plot_size.x, plot_size.y); - if (min_dim < 300) { + if (min_dim < 150) { flags |= ImPlotFlags_NoTitle; } - if (min_dim < 200) { + if (min_dim < 140) { flags |= ImPlotFlags_NoLegend; } } @@ -395,7 +395,7 @@ ImPlotFlags ImPlot_SetupPlotFlags(ImVec2 plot_size) { void ImPlot_SetupTimeAxis(ImVec2 plot_size, const char* label, ImPlotAxisFlags extra_flags) { ImPlotAxisFlags flags = extra_flags; - if (plot_size.x > 0 && plot_size.x < 300) { + if (plot_size.x > 0 && plot_size.x < 180) { flags |= ImPlotAxisFlags_NoTickLabels; } ImPlot::SetupAxis(ImAxis_X1, label, flags); @@ -404,7 +404,7 @@ void ImPlot_SetupTimeAxis(ImVec2 plot_size, const char* label, void ImPlot_SetupValueAxis(ImVec2 plot_size, const char* label, const char* format, ImPlotAxisFlags extra_flags) { ImPlotAxisFlags flags = extra_flags; - if (plot_size.y > 0 && plot_size.y < 150) { + if (plot_size.y > 0 && plot_size.y < 90) { flags |= ImPlotAxisFlags_NoTickLabels; } ImPlot::SetupAxis(ImAxis_Y1, label, flags); @@ -418,7 +418,7 @@ void ImPlot_SetupFixedAxis(ImVec2 plot_size, double y_min, double y_max, const double* tick_values, const char* const* tick_labels, int n_ticks) { ImPlotAxisFlags flags = ImPlotAxisFlags_None; - if (plot_size.y > 0 && plot_size.y < 150) { + if (plot_size.y > 0 && plot_size.y < 90) { flags |= ImPlotAxisFlags_NoTickLabels; } ImPlot::SetupAxis(ImAxis_Y1, label, flags); diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index f30c6551..44442808 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -625,14 +625,12 @@ void App::HandleKeyboardEvents() { ToggleWindow(tmp_.help); } else if (ImGui_IsChordJustPressed(ImGuiKey_F2)) { ToggleWindow(tmp_.stats); + } else if (ImGui_IsChordJustPressed(ImGuiKey_F3)) { + ToggleWindow(tmp_.profiler); } else if (ImGui_IsChordJustPressed(ImGuiKey_F6)) { vis_options_.frame = (vis_options_.frame + 1) % mjNFRAME; } else if (ImGui_IsChordJustPressed(ImGuiKey_F7)) { vis_options_.label = (vis_options_.label + 1) % mjNLABEL; - } else if (ImGui_IsChordJustPressed(ImGuiKey_F9)) { - tmp_.chart_solver = !tmp_.chart_solver; - } else if (ImGui_IsChordJustPressed(ImGuiKey_F10)) { - tmp_.chart_performance = !tmp_.chart_performance; } else if (ImGui_IsChordJustPressed(ImGuiKey_F11)) { tmp_.full_screen = !tmp_.full_screen; } else if (ImGui_IsChordJustPressed(ImGuiKey_H)) { @@ -887,30 +885,10 @@ void App::BuildGui() { ImGui::End(); } - if (tmp_.chart_performance) { - ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver); - if (ImGui::Begin("Performance", &tmp_.chart_performance)) { - auto layout = platform::ImPlot_ComputePairLayout(); - profiler_.CpuTimeGraph(layout.plot_size); - if (layout.direction == platform::ImPlotLayoutDirection::kHorizontal) { - ImGui::SameLine(); - } - profiler_.DimensionsGraph(layout.plot_size); - } - ImGui::End(); - } - - if (tmp_.chart_solver) { - ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver); - if (ImGui::Begin("Solver", &tmp_.chart_solver)) { - auto layout = platform::ImPlot_ComputePairLayout(); - platform::CountsGui(model(), data(), layout.plot_size); - if (layout.direction == platform::ImPlotLayoutDirection::kHorizontal) { - ImGui::SameLine(); - } - platform::ConvergenceGui(model(), data(), layout.plot_size); + if (tmp_.profiler) { + if (ImGui::Begin("Profiler", &tmp_.profiler, + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + platform::ProfilerGui(model(), data(), &profiler_); } ImGui::End(); } @@ -1311,10 +1289,9 @@ void App::HelpGui() { ImGui::Text("Help"); ImGui::Text("Stats"); + ImGui::Text("Profiler"); ImGui::Text("Cycle Frames"); ImGui::Text("Cycle Labels"); - ImGui::Text("Solver Charts"); - ImGui::Text("Perf. Charts"); ImGui::Text("Toggle Fullscreen"); ImGui::Text("Free Camera"); ImGui::Text("Toggle Pause"); @@ -1338,10 +1315,9 @@ void App::HelpGui() { ImGui::Indent(indent); ImGui::Text("F1"); ImGui::Text("F2"); + ImGui::Text("F3"); ImGui::Text("F6"); ImGui::Text("F7"); - ImGui::Text("F9"); - ImGui::Text("F10"); ImGui::Text("F11"); ImGui::Text("Esc"); ImGui::Text("Spc"); @@ -1733,11 +1709,8 @@ void App::MainMenuGui() { } if (ImGui::BeginMenu("Charts")) { - if (ImGui::MenuItem("Solver", "F9")) { - tmp_.chart_solver = !tmp_.chart_solver; - } - if (ImGui::MenuItem("Performance", "F10")) { - tmp_.chart_performance = !tmp_.chart_performance; + if (ImGui::MenuItem("Profiler", "F3")) { + ToggleWindow(tmp_.profiler); } ImGui::EndMenu(); } diff --git a/src/experimental/studio/app.h b/src/experimental/studio/app.h index 52059644..4f2b734c 100644 --- a/src/experimental/studio/app.h +++ b/src/experimental/studio/app.h @@ -120,8 +120,7 @@ class App { // Windows. bool help = false; bool stats = false; - bool chart_solver = false; - bool chart_performance = false; + bool profiler = false; bool picture_in_picture = false; bool options_panel = true; bool inspector_panel = true;