diff --git a/datalogtool/src/main/native/cpp/App.cpp b/datalogtool/src/main/native/cpp/App.cpp index a4b466b57c..6b3f7d0e88 100644 --- a/datalogtool/src/main/native/cpp/App.cpp +++ b/datalogtool/src/main/native/cpp/App.cpp @@ -104,6 +104,8 @@ static void DisplayMainMenu() { ImGui::Text("v%s", GetWPILibVersion()); ImGui::Separator(); ImGui::Text("Save location: %s", glass::GetStorageDir().c_str()); + ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, + ImGui::GetIO().Framerate); if (ImGui::Button("Close")) { ImGui::CloseCurrentPopup(); } diff --git a/glass/src/app/native/cpp/main.cpp b/glass/src/app/native/cpp/main.cpp index ef729f64b3..b257b683e1 100644 --- a/glass/src/app/native/cpp/main.cpp +++ b/glass/src/app/native/cpp/main.cpp @@ -269,6 +269,8 @@ int main(int argc, char** argv) { ImGui::Text("v%s", GetWPILibVersion()); ImGui::Separator(); ImGui::Text("Save location: %s", glass::GetStorageDir().c_str()); + ImGui::Text("%.3f ms/frame (%.1f FPS)", + 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); if (ImGui::Button("Close")) { ImGui::CloseCurrentPopup(); } diff --git a/outlineviewer/src/main/native/cpp/main.cpp b/outlineviewer/src/main/native/cpp/main.cpp index 20f4dcc12e..660a3e1ee0 100644 --- a/outlineviewer/src/main/native/cpp/main.cpp +++ b/outlineviewer/src/main/native/cpp/main.cpp @@ -183,6 +183,8 @@ static void DisplayGui() { ImGui::Text("v%s", GetWPILibVersion()); ImGui::Separator(); ImGui::Text("Save location: %s", glass::GetStorageDir().c_str()); + ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, + ImGui::GetIO().Framerate); if (ImGui::Button("Close")) { ImGui::CloseCurrentPopup(); } diff --git a/roborioteamnumbersetter/src/main/native/cpp/App.cpp b/roborioteamnumbersetter/src/main/native/cpp/App.cpp index e6421d5ec0..eefb52b3a3 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/App.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/App.cpp @@ -127,6 +127,8 @@ static void DisplayGui() { static_cast(multicastResolver->HasImplementation())); ImGui::Separator(); ImGui::Text("Save location: %s", glass::GetStorageDir().c_str()); + ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, + ImGui::GetIO().Framerate); if (ImGui::Button("Close")) { ImGui::CloseCurrentPopup(); } diff --git a/wpigui/src/main/native/cpp/wpigui.cpp b/wpigui/src/main/native/cpp/wpigui.cpp index a93ca2b164..6fd39ae7e3 100644 --- a/wpigui/src/main/native/cpp/wpigui.cpp +++ b/wpigui/src/main/native/cpp/wpigui.cpp @@ -4,9 +4,13 @@ #include "wpigui.h" +#include + #include +#include #include #include +#include #include #include @@ -88,6 +92,8 @@ static void IniReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, impl->userScale = num; } else if (std::strncmp(lineStr, "style=", 6) == 0) { impl->style = num; + } else if (std::strncmp(lineStr, "fps=", 4) == 0) { + impl->fps = num; } } @@ -98,9 +104,10 @@ static void IniWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, } out_buf->appendf( "[MainWindow][GLOBAL]\nwidth=%d\nheight=%d\nmaximized=%d\n" - "xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\n\n", + "xpos=%d\nypos=%d\nuserScale=%d\nstyle=%d\nfps=%d\n\n", gContext->width, gContext->height, gContext->maximized ? 1 : 0, - gContext->xPos, gContext->yPos, gContext->userScale, gContext->style); + gContext->xPos, gContext->yPos, gContext->userScale, gContext->style, + gContext->fps); } void gui::CreateContext() { @@ -331,6 +338,8 @@ bool gui::Initialize(const char* title, int width, int height, void gui::Main() { // Main loop while (!glfwWindowShouldClose(gContext->window) && !gContext->exit) { + double startTime = glfwGetTime(); + // Poll and handle events (inputs, window resize, etc.) glfwPollEvents(); gContext->isPlatformRendering = true; @@ -351,6 +360,15 @@ void gui::Main() { io.WantSaveIniSettings = false; // reset flag } } + + // if FPS limiting, sleep until next frame time + if (gContext->fps != 0) { + double sleepTime = (1.0 / gContext->fps) - (glfwGetTime() - startTime); + if (sleepTime > 1e-6) { + std::this_thread::sleep_for( + std::chrono::microseconds(static_cast(sleepTime * 1e6))); + } + } } // Save (if custom save) @@ -477,6 +495,10 @@ void gui::SetStyle(Style style) { } } +void gui::SetFPS(int fps) { + gContext->fps = fps; +} + void gui::SetClearColor(ImVec4 color) { gContext->clearColor = color; } @@ -539,6 +561,27 @@ void gui::EmitViewMenu() { ImGui::EndMenu(); } + if (ImGui::BeginMenu("Frame Rate")) { + bool selected; + selected = gContext->fps == 0; + if (ImGui::MenuItem("vsync", nullptr, &selected)) { + gContext->fps = 0; + } + selected = gContext->fps == 30; + if (ImGui::MenuItem("30 fps", nullptr, &selected)) { + gContext->fps = 30; + } + selected = gContext->fps == 60; + if (ImGui::MenuItem("60 fps", nullptr, &selected)) { + gContext->fps = 60; + } + selected = gContext->fps == 120; + if (ImGui::MenuItem("120 fps", nullptr, &selected)) { + gContext->fps = 120; + } + ImGui::EndMenu(); + } + if (!gContext->saveSettings) { ImGui::MenuItem("Reset UI on Exit?", nullptr, &gContext->resetOnExit); } diff --git a/wpigui/src/main/native/include/wpigui.h b/wpigui/src/main/native/include/wpigui.h index db7429174c..ac4ce550dc 100644 --- a/wpigui/src/main/native/include/wpigui.h +++ b/wpigui/src/main/native/include/wpigui.h @@ -162,6 +162,13 @@ enum Style { kStyleClassic = 0, kStyleDark, kStyleLight }; */ void SetStyle(Style style); +/** + * Sets the FPS limit. Using this function makes this setting persistent. + * + * @param fps FPS (0=vsync) + */ +void SetFPS(int fps); + /** * Sets the clear (background) color. * diff --git a/wpigui/src/main/native/include/wpigui_internal.h b/wpigui/src/main/native/include/wpigui_internal.h index f4d352bd38..e9735f6f89 100644 --- a/wpigui/src/main/native/include/wpigui_internal.h +++ b/wpigui/src/main/native/include/wpigui_internal.h @@ -24,6 +24,7 @@ struct SavedSettings { int yPos = -1; int userScale = 2; int style = 0; + int fps = 120; }; constexpr int kFontScaledLevels = 9;