From 80947888a06767b06ce92b73bd7503ee2f4fd65c Mon Sep 17 00:00:00 2001 From: Muzychenko Andrey <33288308+k4zmu2a@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:38:59 +0300 Subject: [PATCH] Optimized table restart, external font loading. Fixed memory leak related to restart. Added window to table size adjustment on init, imperfect. --- SpaceCadetPinball/SpaceCadetPinball.cpp | 16 +- SpaceCadetPinball/options.cpp | 4 +- SpaceCadetPinball/pb.cpp | 6 - SpaceCadetPinball/pb.h | 1 - SpaceCadetPinball/render.cpp | 2 + SpaceCadetPinball/winmain.cpp | 255 +++++++++++++----------- SpaceCadetPinball/winmain.h | 2 +- 7 files changed, 141 insertions(+), 145 deletions(-) diff --git a/SpaceCadetPinball/SpaceCadetPinball.cpp b/SpaceCadetPinball/SpaceCadetPinball.cpp index aa08ec4..2ae61e0 100644 --- a/SpaceCadetPinball/SpaceCadetPinball.cpp +++ b/SpaceCadetPinball/SpaceCadetPinball.cpp @@ -5,25 +5,13 @@ #include "winmain.h" -int MainActual(LPCSTR lpCmdLine) -{ - // Todo: get rid of restart to change resolution. - int returnCode; - do - { - returnCode = winmain::WinMain(lpCmdLine); - } - while (winmain::RestartRequested()); - return returnCode; -} - int main(int argc, char* argv[]) { std::string cmdLine; for (int i = 1; i < argc; i++) cmdLine += argv[i]; - return MainActual(cmdLine.c_str()); + return winmain::WinMain(cmdLine.c_str()); } #if _WIN32 @@ -32,7 +20,7 @@ int main(int argc, char* argv[]) // Windows subsystem main int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { - return MainActual(lpCmdLine); + return winmain::WinMain(lpCmdLine); } // fopen to _wfopen adapter, for UTF-8 paths diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index 740dc3c..269963e 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -43,8 +43,8 @@ void options::InitPrimary() // Settings are loaded from disk on the first frame if (!imContext->SettingsLoaded) { - ImGui::NewFrame(); - ImGui::EndFrame(); + ImGui::LoadIniSettingsFromDisk(imContext->IO.IniFilename); + imContext->SettingsLoaded = true; } Options.Key = Options.KeyDft = diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index 6746580..1d68cb0 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -360,12 +360,6 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls) } } -void pb::window_size(int* width, int* height) -{ - *width = fullscrn::resolution_array[fullscrn::GetResolution()].TableWidth; - *height = fullscrn::resolution_array[fullscrn::GetResolution()].TableHeight; -} - void pb::pause_continue() { winmain::single_step ^= true; diff --git a/SpaceCadetPinball/pb.h b/SpaceCadetPinball/pb.h index 2e6c3d6..f9768e6 100644 --- a/SpaceCadetPinball/pb.h +++ b/SpaceCadetPinball/pb.h @@ -64,7 +64,6 @@ public: static void ballset(float dx, float dy); static void frame(float dtMilliSec); static void timed_frame(float timeNow, float timeDelta, bool drawBalls); - static void window_size(int* width, int* height); static void pause_continue(); static void loose_focus(); static void InputUp(GameInput input); diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index 27504c8..a7faa2e 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -132,6 +132,8 @@ void render::uninit() delete sprite_list[0]; while (!ball_list.empty()) delete ball_list[0]; + for (auto& ballBmp : ball_bitmap) + delete ballBmp; DebugOverlay::UnInit(); } diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index 4c57be4..22dee7b 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -47,9 +47,6 @@ WelfordState winmain::SleepState{}; int winmain::WinMain(LPCSTR lpCmdLine) { - restart = false; - bQuit = false; - std::set_new_handler(memalloc_failure); // SDL init @@ -101,117 +98,150 @@ int winmain::WinMain(LPCSTR lpCmdLine) SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); - // ImGui init - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiSDL::Initialize(renderer, 0, 0); - ImGui::StyleColorsDark(); - ImGuiIO& io = ImGui::GetIO(); - ImIO = &io; - auto prefPath = SDL_GetPrefPath("", "SpaceCadetPinball"); - auto iniPath = std::string(prefPath) + "imgui_pb.ini"; - io.IniFilename = iniPath.c_str(); - - // First step: just load the options - options::InitPrimary(); - - if (!Options.FontFileName.empty()) - { - ImGuiSDL::Deinitialize(); - io.Fonts->Clear(); - ImVector ranges; - translations::GetGlyphRange(&ranges); - ImFontConfig fontConfig{}; - - // ToDo: further tweak font options, maybe try imgui_freetype - fontConfig.OversampleV = 2; - fontConfig.OversampleH = 4; - - // ToDo: improve font file test, checking if file exists is not enough - auto fileName = Options.FontFileName.c_str(); - auto fileHandle = fopenu(fileName, "rb"); - if (fileHandle) - { - fclose(fileHandle); - - // ToDo: Bind font size to UI scale - if (!io.Fonts->AddFontFromFileTTF(fileName, 13.f, &fontConfig, ranges.Data)) - io.Fonts->AddFontDefault(); - } - else - io.Fonts->AddFontDefault(); - - io.Fonts->Build(); - ImGuiSDL::Initialize(renderer, 0, 0); - } - - // ImGui_ImplSDL2_Init is private, we are not actually using ImGui OpenGl backend - ImGui_ImplSDL2_InitForOpenGL(window, nullptr); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad; - - // Data search order: WD, executable path, user pref path, platform specific paths. auto basePath = SDL_GetBasePath(); - std::vector searchPaths + do { + restart = false; + + // ImGui init + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + ImIO = &io; + auto iniPath = std::string(prefPath) + "imgui_pb.ini"; + io.IniFilename = iniPath.c_str(); + + // First option initialization step: just load settings from .ini. Needs ImGui context. + options::InitPrimary(); + + if (!Options.FontFileName.empty()) { - "", - basePath, - prefPath - } - }; - searchPaths.insert(searchPaths.end(), std::begin(PlatformDataPaths), std::end(PlatformDataPaths)); - pb::SelectDatFile(searchPaths); + ImVector ranges; + translations::GetGlyphRange(&ranges); + ImFontConfig fontConfig{}; - // Second step: run updates depending on FullTiltMode - options::InitSecondary(); + // ToDo: further tweak font options, maybe try imgui_freetype + fontConfig.OversampleV = 2; + fontConfig.OversampleH = 4; - if (!Sound::Init(Options.SoundChannels, Options.Sounds, Options.SoundVolume)) - Options.Sounds = false; - - if (!pb::quickFlag && !midi::music_init(Options.MusicVolume)) - Options.Music = false; - - if (pb::init()) - { - std::string message = "The .dat file is missing.\n" - "Make sure that the game data is present in any of the following locations:\n"; - for (auto path : searchPaths) - { - if (path) + // ToDo: improve font file test, checking if file exists is not enough + auto fontLoaded = false; + auto fileName = Options.FontFileName.c_str(); + auto fileHandle = fopenu(fileName, "rb"); + if (fileHandle) { - message = message + (path[0] ? path : "working directory") + "\n"; + fclose(fileHandle); + + // ToDo: Bind font size to UI scale + if (io.Fonts->AddFontFromFileTTF(fileName, 13.f, &fontConfig, ranges.Data)) + fontLoaded = true; } + + if (!fontLoaded) + printf("Failed to load font: %s, using embedded font.\n", fileName); + io.Fonts->Build(); } - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not load game data", - message.c_str(), window); - return 1; + ImGuiSDL::Initialize(renderer, 0, 0); + ImGui::StyleColorsDark(); + + // ImGui_ImplSDL2_Init is private, we are not actually using ImGui OpenGl backend + ImGui_ImplSDL2_InitForOpenGL(window, nullptr); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad; + + // Data search order: WD, executable path, user pref path, platform specific paths. + std::vector searchPaths + { + { + "", + basePath, + prefPath + } + }; + searchPaths.insert(searchPaths.end(), std::begin(PlatformDataPaths), std::end(PlatformDataPaths)); + pb::SelectDatFile(searchPaths); + + // Second step: run updates that depend on .DAT file selection + options::InitSecondary(); + + if (!Sound::Init(Options.SoundChannels, Options.Sounds, Options.SoundVolume)) + Options.Sounds = false; + + if (!pb::quickFlag && !midi::music_init(Options.MusicVolume)) + Options.Music = false; + + if (pb::init()) + { + std::string message = "The .dat file is missing.\n" + "Make sure that the game data is present in any of the following locations:\n"; + for (auto path : searchPaths) + { + if (path) + { + message = message + (path[0] ? path : "working directory") + "\n"; + } + } + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not load game data", + message.c_str(), window); + return 1; + } + + fullscrn::init(); + + pb::reset_table(); + pb::firsttime_setup(); + + if (strstr(lpCmdLine, "-fullscreen")) + { + Options.FullScreen = true; + } + + if (!Options.FullScreen) + { + auto resInfo = &fullscrn::resolution_array[fullscrn::GetResolution()]; + SDL_SetWindowSize(MainWindow, resInfo->TableWidth, resInfo->TableHeight); + } + SDL_ShowWindow(window); + fullscrn::set_screen_mode(Options.FullScreen); + + if (strstr(lpCmdLine, "-demo")) + pb::toggle_demo(); + else + pb::replay_level(false); + + MainLoop(); + + options::uninit(); + midi::music_shutdown(); + pb::uninit(); + Sound::Close(); + + ImGuiSDL::Deinitialize(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); } + while (restart); - fullscrn::init(); + SDL_free(basePath); + SDL_free(prefPath); + delete gfr_display; + gfr_display = nullptr; + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); - pb::reset_table(); - pb::firsttime_setup(); - - if (strstr(lpCmdLine, "-fullscreen")) - { - Options.FullScreen = true; - } - - SDL_ShowWindow(window); - fullscrn::set_screen_mode(Options.FullScreen); - - if (strstr(lpCmdLine, "-demo")) - pb::toggle_demo(); - else - pb::replay_level(false); + return return_value; +} +void winmain::MainLoop() +{ + bQuit = false; unsigned updateCounter = 0, frameCounter = 0; - auto frameStart = Clock::now(); double UpdateToFrameCounter = 0; DurationMs sleepRemainder(0), frameDuration(TargetFrameTime); auto prevTime = frameStart; + while (true) { if (DispFrameRate) @@ -223,7 +253,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) auto elapsedSec = DurationMs(curTime - prevTime).count() * 0.001; snprintf(buf, sizeof buf, "Updates/sec = %02.02f Frames/sec = %02.02f ", updateCounter / elapsedSec, frameCounter / elapsedSec); - SDL_SetWindowTitle(window, buf); + SDL_SetWindowTitle(MainWindow, buf); FpsDetails = buf; frameCounter = updateCounter = 0; prevTime = curTime; @@ -239,7 +269,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) { int x, y, w, h; SDL_GetMouseState(&x, &y); - SDL_GetWindowSize(window, &w, &h); + SDL_GetWindowSize(MainWindow, &w, &h); float dx = static_cast(last_mouse_x - x) / static_cast(w); float dy = static_cast(y - last_mouse_y) / static_cast(h); pb::ballset(dx, dy); @@ -256,7 +286,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) // Mouse warp does not work over remote desktop or in some VMs x = abs(x - xMod); y = abs(y - yMod); - SDL_WarpMouseInWindow(window, x, y); + SDL_WarpMouseInWindow(MainWindow, x, y); } last_mouse_x = x; @@ -297,16 +327,16 @@ int winmain::WinMain(LPCSTR lpCmdLine) ImGui::NewFrame(); RenderUi(); - SDL_RenderClear(renderer); + SDL_RenderClear(Renderer); // Alternative clear hack, clear might fail on some systems // Todo: remove original clear, if save for all platforms - SDL_RenderFillRect(renderer, nullptr); + SDL_RenderFillRect(Renderer, nullptr); render::PresentVScreen(); ImGui::Render(); ImGuiSDL::Render(ImGui::GetDrawData()); - SDL_RenderPresent(renderer); + SDL_RenderPresent(Renderer); frameCounter++; UpdateToFrameCounter -= UpdateToFrameRatio; } @@ -366,23 +396,6 @@ int winmain::WinMain(LPCSTR lpCmdLine) { printf("SDL Error: ^ Previous Error Repeated %u Times\n", PrevSdlErrorCount); } - - SDL_free(basePath); - SDL_free(prefPath); - delete gfr_display; - gfr_display = nullptr; - options::uninit(); - midi::music_shutdown(); - pb::uninit(); - Sound::Close(); - ImGuiSDL::Deinitialize(); - ImGui_ImplSDL2_Shutdown(); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - ImGui::DestroyContext(); - SDL_Quit(); - - return return_value; } void winmain::RenderUi() @@ -512,7 +525,7 @@ void winmain::RenderUi() if (ImGui::MenuItem(item.DisplayName, nullptr, currentLanguage->Language == item.Language)) { translations::SetCurrentLanguage(item.ShortName); - winmain::Restart(); + Restart(); } } ImGui::EndMenu(); @@ -623,7 +636,7 @@ void winmain::RenderUi() if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Table_Resolution))) { char buffer[20]{}; - Msg resolutionStringId = Msg::Menu1_UseMaxResolution_640x480; + auto resolutionStringId = Msg::Menu1_UseMaxResolution_640x480; switch (fullscrn::GetMaxResolution()) { diff --git a/SpaceCadetPinball/winmain.h b/SpaceCadetPinball/winmain.h index 5550a78..7ef7fa9 100644 --- a/SpaceCadetPinball/winmain.h +++ b/SpaceCadetPinball/winmain.h @@ -83,7 +83,6 @@ public: static void new_game(); static void pause(bool toggle = true); static void Restart(); - static bool RestartRequested() { return restart; } static void UpdateFrameRate(); private: static int return_value, DispFrameRate; @@ -105,4 +104,5 @@ private: static void RenderUi(); static void RenderFrameTimeDialog(); static void HybridSleep(DurationMs seconds); + static void MainLoop(); };