From 5a0252d8d0f3527ca186183373e071cae95caf79 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Fri, 5 Jan 2024 14:47:08 +0100 Subject: [PATCH] start screen refactor --- src/main.cpp | 117 +++++++++++++++++++++++++++++-------------- src/main_screen.cpp | 40 +++------------ src/main_screen.hpp | 9 +++- src/screen.hpp | 11 +++- src/start_screen.cpp | 6 ++- src/start_screen.hpp | 5 +- 6 files changed, 114 insertions(+), 74 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ec2bdd7..3375f82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,7 +21,8 @@ int main(int argc, char** argv) { std::cerr << "Failed to set '" << SDL_HINT_VIDEO_ALLOW_SCREENSAVER << "' to 1\n"; } - auto last_time = std::chrono::steady_clock::now(); + auto last_time_render = std::chrono::steady_clock::now(); + auto last_time_tick = std::chrono::steady_clock::now(); // actual setup if (SDL_Init(SDL_INIT_VIDEO) < 0) { @@ -68,54 +69,96 @@ int main(int argc, char** argv) { bool quit = false; while (!quit) { auto new_time = std::chrono::steady_clock::now(); - //const float time_delta {std::chrono::duration(new_time - last_time).count()}; - last_time = new_time; - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_EVENT_QUIT) { - quit = true; + const float time_delta_tick = std::chrono::duration(new_time - last_time_tick).count(); + const float time_delta_render = std::chrono::duration(new_time - last_time_render).count(); + + bool tick = time_delta_tick >= screen->nextTick(); + bool render = time_delta_render >= screen->nextRender(); + + if (tick) { + Screen* ret_screen = screen->tick(time_delta_tick, quit); + if (ret_screen != nullptr) { + screen.reset(ret_screen); + } + last_time_tick = new_time; + } + + // can do both in the same loop + if (render) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_QUIT) { + quit = true; + break; + } + + if (screen->handleEvent(event)) { + continue; + } + + ImGui_ImplSDL3_ProcessEvent(&event); + } + if (quit) { break; } - if (screen->handleEvent(event)) { - continue; + //float fps_target = 60.f; + //if (SDL_GetWindowFlags(window.get()) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) { + //fps_target = 30.f; + //} + + ImGui_ImplSDLRenderer3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + { // render + Screen* ret_screen = screen->render(time_delta_render, quit); + if (ret_screen != nullptr) { + screen.reset(ret_screen); + } } - ImGui_ImplSDL3_ProcessEvent(&event); - } - if (quit) { - break; + ImGui::Render(); + ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData()); + + SDL_RenderPresent(renderer.get()); + // clearing after present is (should) more performant, but first frame is a mess + SDL_SetRenderDrawColor(renderer.get(), 0x10, 0x10, 0x10, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer.get()); + + last_time_render = new_time; } - //float fps_target = 60.f; - //if (SDL_GetWindowFlags(window.get()) & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) { - //fps_target = 30.f; - //} + //// TODO: seperate out render and tick + //const float time_to_next_loop = std::min(screen->nextRender(), screen->nextTick()); - ImGui_ImplSDLRenderer3_NewFrame(); - ImGui_ImplSDL3_NewFrame(); - ImGui::NewFrame(); + //std::this_thread::sleep_for( // time left to get to 60fps + //std::chrono::duration(time_to_next_loop) + //- std::chrono::duration(std::chrono::steady_clock::now() - new_time) // time used for rendering + //); - //ImGui::ShowDemoWindow(); - Screen* ret_screen = screen->poll(quit); - if (ret_screen != nullptr) { - screen.reset(ret_screen); + if (render || tick) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // yield for 1ms + } else { + +#if 0 + // pretty hacky and spins if close to next update + // if next loop >= 1ms away, wait 1ms + if (time_delta_tick+0.001f < screen->nextTick() && time_delta_render+0.001f < screen->nextRender()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // yield for 1ms + } +#else + // dynamic sleep, sleeps the reminder till next update + std::this_thread::sleep_for(std::chrono::duration( + std::min( + screen->nextTick() - time_delta_tick, + screen->nextRender() - time_delta_render + ) + )); +#endif } - - ImGui::Render(); - ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData()); - - SDL_RenderPresent(renderer.get()); - // clearing after present is (should) more performant, but first frame is a mess - SDL_SetRenderDrawColor(renderer.get(), 0x10, 0x10, 0x10, SDL_ALPHA_OPAQUE); - SDL_RenderClear(renderer.get()); - - std::this_thread::sleep_for( // time left to get to 60fps - std::chrono::duration(0.0166f) // 60fps frame duration - - std::chrono::duration(std::chrono::steady_clock::now() - new_time) // time used for rendering - ); } return 0; diff --git a/src/main_screen.cpp b/src/main_screen.cpp index e57231d..5079a24 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -81,11 +81,7 @@ bool MainScreen::handleEvent(SDL_Event& e) { return false; } -Screen* MainScreen::poll(bool& quit) { - auto new_time = std::chrono::high_resolution_clock::now(); - const float time_delta {std::chrono::duration(new_time - last_time).count()}; - last_time = new_time; - +Screen* MainScreen::render(float time_delta, bool& quit) { quit = !tc.iterate(); tcm.iterate(time_delta); @@ -123,34 +119,14 @@ Screen* MainScreen::poll(bool& quit) { ImGui::ShowDemoWindow(); } -#if 0 - { // texture tests - const size_t width = 8; - const size_t height = 8; -#define W 0xff, 0xff, 0xff, 0xff -#define B 0x00, 0x00, 0x00, 0xff -#define P 0xff, 0x00, 0xff, 0xff - static uint8_t raw_pixel[width*height*4] { - P, W, W, W, W, W, W, P, - W, W, W, W, W, W, W, W, - W, W, W, W, W, W, W, W, - W, W, W, B, B, W, W, W, - W, W, W, B, B, W, W, W, - W, W, W, W, W, W, W, W, - W, W, W, W, W, W, W, W, - P, W, W, W, W, W, W, P, - }; + return nullptr; +} - static uint64_t texture = sdlrtu.uploadRGBA(raw_pixel, width, height); - - if (ImGui::Begin("test texture")) { - ImGui::Text("test texture windoajsdf"); - - ImGui::Image(reinterpret_cast(texture), {width*10, height*10}); - } - ImGui::End(); - } -#endif +Screen* MainScreen::tick(float time_delta, bool& quit) { + _min_tick_interval = std::min( + tc.toxIterationInterval()/1000.f, + 0.03f // HACK: 30ms upper bound, should be the same as tox but will change + ); return nullptr; } diff --git a/src/main_screen.hpp b/src/main_screen.hpp index 8327697..ed092e8 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -73,6 +73,13 @@ struct MainScreen final : public Screen { // return nullptr if not next // sets bool quit to true if exit - Screen* poll(bool&) override; + Screen* render(float time_delta, bool&) override; + Screen* tick(float time_delta, bool&) override; + + float _render_interval {1.f/60.f}; + float _min_tick_interval {0.f}; + + float nextRender(void) override { return _render_interval; } + float nextTick(void) override { return _min_tick_interval; } }; diff --git a/src/screen.hpp b/src/screen.hpp index 238f014..9512122 100644 --- a/src/screen.hpp +++ b/src/screen.hpp @@ -2,14 +2,21 @@ #include +// all time values are in seconds struct Screen { virtual ~Screen(void) = default; // return true if handled - virtual bool handleEvent(SDL_Event& e) { return false; } + virtual bool handleEvent(SDL_Event&) { return false; } // return nullptr if not next // sets bool quit to true if exit - virtual Screen* poll(bool& quit) = 0; + // both render and tick get called in the selfreported intervals + virtual Screen* render(float time_delta, bool& quit) = 0; // pure since this is a graphical app + virtual Screen* tick(float time_delta, bool& quit) = 0; + + // TODO: const? + virtual float nextRender(void) { return 1.f/60.f; } + virtual float nextTick(void) { return 0.03f; } }; diff --git a/src/start_screen.cpp b/src/start_screen.cpp index 1faa911..f7ff6a5 100644 --- a/src/start_screen.cpp +++ b/src/start_screen.cpp @@ -11,7 +11,7 @@ StartScreen::StartScreen(SDL_Renderer* renderer) : _renderer(renderer) { } -Screen* StartScreen::poll(bool&) { +Screen* StartScreen::render(float, bool&) { // TODO: imgui tox profile selector? @@ -137,3 +137,7 @@ Screen* StartScreen::poll(bool&) { return nullptr; } +Screen* StartScreen::tick(float, bool&) { + return nullptr; +} + diff --git a/src/start_screen.hpp b/src/start_screen.hpp index 5175f97..429a501 100644 --- a/src/start_screen.hpp +++ b/src/start_screen.hpp @@ -30,6 +30,9 @@ struct StartScreen final : public Screen { // return nullptr if not next // sets bool quit to true if exit - Screen* poll(bool&) override; + Screen* render(float, bool&) override; + Screen* tick(float, bool&) override; + + // use default nextRender and nextTick };