Optimized table restart, external font loading.

Fixed memory leak related to restart.
Added window to table size adjustment on init, imperfect.
This commit is contained in:
Muzychenko Andrey 2022-11-17 15:38:59 +03:00
parent 6486589c4a
commit 80947888a0
7 changed files with 141 additions and 145 deletions

View file

@ -5,25 +5,13 @@
#include "winmain.h" #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[]) int main(int argc, char* argv[])
{ {
std::string cmdLine; std::string cmdLine;
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
cmdLine += argv[i]; cmdLine += argv[i];
return MainActual(cmdLine.c_str()); return winmain::WinMain(cmdLine.c_str());
} }
#if _WIN32 #if _WIN32
@ -32,7 +20,7 @@ int main(int argc, char* argv[])
// Windows subsystem main // Windows subsystem main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 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 // fopen to _wfopen adapter, for UTF-8 paths

View file

@ -43,8 +43,8 @@ void options::InitPrimary()
// Settings are loaded from disk on the first frame // Settings are loaded from disk on the first frame
if (!imContext->SettingsLoaded) if (!imContext->SettingsLoaded)
{ {
ImGui::NewFrame(); ImGui::LoadIniSettingsFromDisk(imContext->IO.IniFilename);
ImGui::EndFrame(); imContext->SettingsLoaded = true;
} }
Options.Key = Options.KeyDft = Options.Key = Options.KeyDft =

View file

@ -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() void pb::pause_continue()
{ {
winmain::single_step ^= true; winmain::single_step ^= true;

View file

@ -64,7 +64,6 @@ public:
static void ballset(float dx, float dy); static void ballset(float dx, float dy);
static void frame(float dtMilliSec); static void frame(float dtMilliSec);
static void timed_frame(float timeNow, float timeDelta, bool drawBalls); static void timed_frame(float timeNow, float timeDelta, bool drawBalls);
static void window_size(int* width, int* height);
static void pause_continue(); static void pause_continue();
static void loose_focus(); static void loose_focus();
static void InputUp(GameInput input); static void InputUp(GameInput input);

View file

@ -132,6 +132,8 @@ void render::uninit()
delete sprite_list[0]; delete sprite_list[0];
while (!ball_list.empty()) while (!ball_list.empty())
delete ball_list[0]; delete ball_list[0];
for (auto& ballBmp : ball_bitmap)
delete ballBmp;
DebugOverlay::UnInit(); DebugOverlay::UnInit();
} }

View file

@ -47,9 +47,6 @@ WelfordState winmain::SleepState{};
int winmain::WinMain(LPCSTR lpCmdLine) int winmain::WinMain(LPCSTR lpCmdLine)
{ {
restart = false;
bQuit = false;
std::set_new_handler(memalloc_failure); std::set_new_handler(memalloc_failure);
// SDL init // SDL init
@ -101,117 +98,150 @@ int winmain::WinMain(LPCSTR lpCmdLine)
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); 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 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<ImWchar> 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(); auto basePath = SDL_GetBasePath();
std::vector<const char*> 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())
{ {
"", ImVector<ImWchar> ranges;
basePath, translations::GetGlyphRange(&ranges);
prefPath ImFontConfig fontConfig{};
}
};
searchPaths.insert(searchPaths.end(), std::begin(PlatformDataPaths), std::end(PlatformDataPaths));
pb::SelectDatFile(searchPaths);
// Second step: run updates depending on FullTiltMode // ToDo: further tweak font options, maybe try imgui_freetype
options::InitSecondary(); fontConfig.OversampleV = 2;
fontConfig.OversampleH = 4;
if (!Sound::Init(Options.SoundChannels, Options.Sounds, Options.SoundVolume)) // ToDo: improve font file test, checking if file exists is not enough
Options.Sounds = false; auto fontLoaded = false;
auto fileName = Options.FontFileName.c_str();
if (!pb::quickFlag && !midi::music_init(Options.MusicVolume)) auto fileHandle = fopenu(fileName, "rb");
Options.Music = false; if (fileHandle)
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"; 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", ImGuiSDL::Initialize(renderer, 0, 0);
message.c_str(), window); ImGui::StyleColorsDark();
return 1;
// 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<const char*> 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(); return return_value;
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);
void winmain::MainLoop()
{
bQuit = false;
unsigned updateCounter = 0, frameCounter = 0; unsigned updateCounter = 0, frameCounter = 0;
auto frameStart = Clock::now(); auto frameStart = Clock::now();
double UpdateToFrameCounter = 0; double UpdateToFrameCounter = 0;
DurationMs sleepRemainder(0), frameDuration(TargetFrameTime); DurationMs sleepRemainder(0), frameDuration(TargetFrameTime);
auto prevTime = frameStart; auto prevTime = frameStart;
while (true) while (true)
{ {
if (DispFrameRate) if (DispFrameRate)
@ -223,7 +253,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
auto elapsedSec = DurationMs(curTime - prevTime).count() * 0.001; auto elapsedSec = DurationMs(curTime - prevTime).count() * 0.001;
snprintf(buf, sizeof buf, "Updates/sec = %02.02f Frames/sec = %02.02f ", snprintf(buf, sizeof buf, "Updates/sec = %02.02f Frames/sec = %02.02f ",
updateCounter / elapsedSec, frameCounter / elapsedSec); updateCounter / elapsedSec, frameCounter / elapsedSec);
SDL_SetWindowTitle(window, buf); SDL_SetWindowTitle(MainWindow, buf);
FpsDetails = buf; FpsDetails = buf;
frameCounter = updateCounter = 0; frameCounter = updateCounter = 0;
prevTime = curTime; prevTime = curTime;
@ -239,7 +269,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
{ {
int x, y, w, h; int x, y, w, h;
SDL_GetMouseState(&x, &y); SDL_GetMouseState(&x, &y);
SDL_GetWindowSize(window, &w, &h); SDL_GetWindowSize(MainWindow, &w, &h);
float dx = static_cast<float>(last_mouse_x - x) / static_cast<float>(w); float dx = static_cast<float>(last_mouse_x - x) / static_cast<float>(w);
float dy = static_cast<float>(y - last_mouse_y) / static_cast<float>(h); float dy = static_cast<float>(y - last_mouse_y) / static_cast<float>(h);
pb::ballset(dx, dy); 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 // Mouse warp does not work over remote desktop or in some VMs
x = abs(x - xMod); x = abs(x - xMod);
y = abs(y - yMod); y = abs(y - yMod);
SDL_WarpMouseInWindow(window, x, y); SDL_WarpMouseInWindow(MainWindow, x, y);
} }
last_mouse_x = x; last_mouse_x = x;
@ -297,16 +327,16 @@ int winmain::WinMain(LPCSTR lpCmdLine)
ImGui::NewFrame(); ImGui::NewFrame();
RenderUi(); RenderUi();
SDL_RenderClear(renderer); SDL_RenderClear(Renderer);
// Alternative clear hack, clear might fail on some systems // Alternative clear hack, clear might fail on some systems
// Todo: remove original clear, if save for all platforms // Todo: remove original clear, if save for all platforms
SDL_RenderFillRect(renderer, nullptr); SDL_RenderFillRect(Renderer, nullptr);
render::PresentVScreen(); render::PresentVScreen();
ImGui::Render(); ImGui::Render();
ImGuiSDL::Render(ImGui::GetDrawData()); ImGuiSDL::Render(ImGui::GetDrawData());
SDL_RenderPresent(renderer); SDL_RenderPresent(Renderer);
frameCounter++; frameCounter++;
UpdateToFrameCounter -= UpdateToFrameRatio; UpdateToFrameCounter -= UpdateToFrameRatio;
} }
@ -366,23 +396,6 @@ int winmain::WinMain(LPCSTR lpCmdLine)
{ {
printf("SDL Error: ^ Previous Error Repeated %u Times\n", PrevSdlErrorCount); 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() void winmain::RenderUi()
@ -512,7 +525,7 @@ void winmain::RenderUi()
if (ImGui::MenuItem(item.DisplayName, nullptr, currentLanguage->Language == item.Language)) if (ImGui::MenuItem(item.DisplayName, nullptr, currentLanguage->Language == item.Language))
{ {
translations::SetCurrentLanguage(item.ShortName); translations::SetCurrentLanguage(item.ShortName);
winmain::Restart(); Restart();
} }
} }
ImGui::EndMenu(); ImGui::EndMenu();
@ -623,7 +636,7 @@ void winmain::RenderUi()
if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Table_Resolution))) if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Table_Resolution)))
{ {
char buffer[20]{}; char buffer[20]{};
Msg resolutionStringId = Msg::Menu1_UseMaxResolution_640x480; auto resolutionStringId = Msg::Menu1_UseMaxResolution_640x480;
switch (fullscrn::GetMaxResolution()) switch (fullscrn::GetMaxResolution())
{ {

View file

@ -83,7 +83,6 @@ public:
static void new_game(); static void new_game();
static void pause(bool toggle = true); static void pause(bool toggle = true);
static void Restart(); static void Restart();
static bool RestartRequested() { return restart; }
static void UpdateFrameRate(); static void UpdateFrameRate();
private: private:
static int return_value, DispFrameRate; static int return_value, DispFrameRate;
@ -105,4 +104,5 @@ private:
static void RenderUi(); static void RenderUi();
static void RenderFrameTimeDialog(); static void RenderFrameTimeDialog();
static void HybridSleep(DurationMs seconds); static void HybridSleep(DurationMs seconds);
static void MainLoop();
}; };