Added a startup progress bar to the splash screen.

This commit is contained in:
Leon Styhre 2023-01-22 21:03:08 +01:00
parent d15927c156
commit e44c18bc1b
6 changed files with 150 additions and 55 deletions

View file

@ -508,6 +508,15 @@ bool SystemData::loadConfig()
return true; return true;
} }
const bool splashScreen {Settings::getInstance()->getBool("SplashScreen")};
float systemCount {0.0f};
float loadedSystems {0.0f};
for (pugi::xml_node system {systemList.child("system")}; system;
system = system.next_sibling("system")) {
++systemCount;
}
for (pugi::xml_node system {systemList.child("system")}; system; for (pugi::xml_node system {systemList.child("system")}; system;
system = system.next_sibling("system")) { system = system.next_sibling("system")) {
std::string name; std::string name;
@ -521,6 +530,13 @@ bool SystemData::loadConfig()
sortName = system.child("systemsortname").text().get(); sortName = system.child("systemsortname").text().get();
path = system.child("path").text().get(); path = system.child("path").text().get();
if (splashScreen) {
++loadedSystems;
const float progress {glm::mix(0.0f, 0.5f, loadedSystems / systemCount)};
Window::getInstance()->renderSplashScreen(Window::SplashScreenState::SCANNING,
progress);
}
auto nameFindFunc = [&] { auto nameFindFunc = [&] {
for (auto system : sSystemVector) { for (auto system : sSystemVector) {
if (system->mName == name) { if (system->mName == name) {
@ -581,7 +597,7 @@ bool SystemData::loadConfig()
} }
// Convert extensions list from a string into a vector of strings. // Convert extensions list from a string into a vector of strings.
std::vector<std::string> extensions = readList(system.child("extension").text().get()); std::vector<std::string> extensions {readList(system.child("extension").text().get())};
// Load all launch command tags for the system and if there are multiple tags, then // Load all launch command tags for the system and if there are multiple tags, then
// the label attribute needs to be set on all entries as it's a requirement for the // the label attribute needs to be set on all entries as it's a requirement for the

View file

@ -531,16 +531,15 @@ int main(int argc, char* argv[])
// This is a workaround to disable the incredibly annoying save state functionality in // This is a workaround to disable the incredibly annoying save state functionality in
// macOS which forces a restore of the previous window state. The problem is that this // macOS which forces a restore of the previous window state. The problem is that this
// removes the splash screen on startup and it may have other adverse effects as well. // removes the splash screen on startup and it may have other adverse effects as well.
std::string saveStateDir = Utils::FileSystem::expandHomePath( std::string saveStateDir {Utils::FileSystem::expandHomePath(
"~/Library/Saved Application State/org.es-de.EmulationStation.savedState"); "~/Library/Saved Application State/org.es-de.EmulationStation.savedState")};
// Deletion of the state files should normally not be required as there shouldn't be any // Deletion of the state files should normally not be required as there shouldn't be any
// files to begin with. But maybe the files can still be created for unknown reasons // files to begin with. But maybe the files can still be created for unknown reasons
// as macOS really really loves to restore windows. Let's therefore include this deletion // as macOS really really loves to restore windows. Let's therefore include this deletion
// step as an extra precaution. // step as an extra precaution.
if (Utils::FileSystem::exists(saveStateDir)) { if (Utils::FileSystem::exists(saveStateDir)) {
for (std::string stateFile : Utils::FileSystem::getDirContent(saveStateDir)) { for (std::string stateFile : Utils::FileSystem::getDirContent(saveStateDir))
Utils::FileSystem::removeFile(stateFile); Utils::FileSystem::removeFile(stateFile);
}
} }
else { else {
Utils::FileSystem::createDirectory(saveStateDir); Utils::FileSystem::createDirectory(saveStateDir);
@ -683,8 +682,6 @@ int main(int argc, char* argv[])
window->pushGui(ViewController::getInstance()); window->pushGui(ViewController::getInstance());
bool splashScreen {Settings::getInstance()->getBool("SplashScreen")};
InputManager::getInstance().parseEvent(event); InputManager::getInstance().parseEvent(event);
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
return 1; return 1;
@ -707,15 +704,18 @@ int main(int argc, char* argv[])
} }
#endif #endif
if (splashScreen) {
std::string progressText {"Loading system config..."};
window->renderLoadingScreen(progressText);
}
AudioManager::getInstance(); AudioManager::getInstance();
MameNames::getInstance(); MameNames::getInstance();
ThemeData::populateThemeSets(); ThemeData::populateThemeSets();
// We need to temporarily disable VSync as the splash screen may otherwise slow down
// application startup significantly due to excessive swapBuffers() calls.
const bool splashScreen {Settings::getInstance()->getBool("SplashScreen")};
const bool vSync {Settings::getInstance()->getBool("VSync")};
if (splashScreen && vSync)
SDL_GL_SetSwapInterval(0);
loadSystemsReturnCode loadSystemsStatus {loadSystemConfigFile()}; loadSystemsReturnCode loadSystemsStatus {loadSystemConfigFile()};
if (splashScreen && vSync)
SDL_GL_SetSwapInterval(1);
if (loadSystemsStatus) { if (loadSystemsStatus) {
// If there was an issue parsing the es_systems.xml file, display an error message. // If there was an issue parsing the es_systems.xml file, display an error message.
@ -752,9 +752,6 @@ int main(int argc, char* argv[])
<< "\""; << "\"";
} }
if (splashScreen)
window->renderLoadingScreen("Done");
// Open the input configuration GUI if the flag to force this was passed from the command line. // Open the input configuration GUI if the flag to force this was passed from the command line.
if (!loadSystemsStatus) { if (!loadSystemsStatus) {
if (forceInputConfig) { if (forceInputConfig) {

View file

@ -241,7 +241,7 @@ void ViewController::goToStart(bool playTransition)
void ViewController::ReloadAndGoToStart() void ViewController::ReloadAndGoToStart()
{ {
mWindow->renderLoadingScreen("Loading..."); mWindow->renderSplashScreen(Window::SplashScreenState::RELOADING, 0.0f);
reloadAll(); reloadAll();
if (mState.viewing == GAMELIST) { if (mState.viewing == GAMELIST) {
goToSystemView(SystemData::sSystemVector.front(), false); goToSystemView(SystemData::sSystemVector.front(), false);
@ -1108,13 +1108,15 @@ void ViewController::preload()
if (!SystemData::sSystemVector.empty()) if (!SystemData::sSystemVector.empty())
getSystemListView(); getSystemListView();
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); float loadedSystems {0.0f};
++it) {
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); ++it) {
if (Settings::getInstance()->getBool("SplashScreen")) { if (Settings::getInstance()->getBool("SplashScreen")) {
mWindow->renderLoadingScreen( ++loadedSystems;
"Loading '" + (*it)->getFullName() + "' (" + const float progress {
std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it) + 1) + "/" + glm::mix(0.5f, 1.0f, loadedSystems / static_cast<float>(systemCount))};
std::to_string(systemCount) + ")"); mWindow->renderSplashScreen(Window::SplashScreenState::POPULATING, progress);
} }
(*it)->getIndex()->resetFilters(); (*it)->getIndex()->resetFilters();
getGamelistView(*it)->preloadGamelist(); getGamelistView(*it)->preloadGamelist();

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// Window.cpp // Window.cpp
// //
// Window management, screensaver management, and help prompts. // Window management, screensaver management, help prompts and splash screen.
// The input stack starts here as well, as this is the first instance called by InputManager. // The input stack starts here as well, as this is the first instance called by InputManager.
// //
@ -25,6 +25,8 @@
Window::Window() noexcept Window::Window() noexcept
: mRenderer {Renderer::getInstance()} : mRenderer {Renderer::getInstance()}
, mSplashTextPositions {0.0f, 0.0f, 0.0f, 0.0f}
, mBackgroundOverlayOpacity {1.0f}
, mScreensaver {nullptr} , mScreensaver {nullptr}
, mMediaViewer {nullptr} , mMediaViewer {nullptr}
, mLaunchScreen {nullptr} , mLaunchScreen {nullptr}
@ -47,23 +49,19 @@ Window::Window() noexcept
, mInvalidateCacheTimer {0} , mInvalidateCacheTimer {0}
, mVideoPlayerCount {0} , mVideoPlayerCount {0}
, mTopScale {0.5f} , mTopScale {0.5f}
, mRenderedHelpPrompts {false}
, mChangedThemeSet {false} , mChangedThemeSet {false}
{ {
} }
Window::~Window() Window::~Window()
{ {
delete mBackgroundOverlay;
delete mSplash;
// Delete all our GUIs. // Delete all our GUIs.
while (peekGui()) while (peekGui())
delete peekGui(); delete peekGui();
if (mInfoPopup) if (mInfoPopup)
delete mInfoPopup; delete mInfoPopup;
delete mHelp;
} }
Window* Window::getInstance() Window* Window::getInstance()
@ -112,10 +110,10 @@ bool Window::init()
ResourceManager::getInstance().reloadAll(); ResourceManager::getInstance().reloadAll();
mHelp = new HelpComponent; mHelp = std::make_unique<HelpComponent>();
mSplash = new ImageComponent; mSplash = std::make_unique<ImageComponent>(false, false);
mBackgroundOverlay = new ImageComponent; mBackgroundOverlay = std::make_unique<ImageComponent>(false, false);
mBackgroundOverlayOpacity = 0.0f; mBackgroundOverlayOpacity = 0.0f;
// Keep a reference to the default fonts, so they don't keep getting destroyed/recreated. // Keep a reference to the default fonts, so they don't keep getting destroyed/recreated.
@ -135,6 +133,49 @@ bool Window::init()
mSplash->setPosition((mRenderer->getScreenWidth() - mSplash->getSize().x) / 2.0f, mSplash->setPosition((mRenderer->getScreenWidth() - mSplash->getSize().x) / 2.0f,
(mRenderer->getScreenHeight() - mSplash->getSize().y) / 2.0f * 0.6f); (mRenderer->getScreenHeight() - mSplash->getSize().y) / 2.0f * 0.6f);
mSplashTextScanning = std::unique_ptr<TextCache>(mDefaultFonts.at(1)->buildTextCache(
"Scanning game files...", 0.0f, 0.0f, DEFAULT_TEXTCOLOR));
mSplashTextPopulating = std::unique_ptr<TextCache>(mDefaultFonts.at(1)->buildTextCache(
"Populating systems...", 0.0f, 0.0f, DEFAULT_TEXTCOLOR));
mSplashTextReloading = std::unique_ptr<TextCache>(
mDefaultFonts.at(1)->buildTextCache("Reloading...", 0.0f, 0.0f, DEFAULT_TEXTCOLOR));
mSplashTextPositions.x =
(mRenderer->getScreenWidth() - mSplashTextScanning->metrics.size.x) / 2.0f;
mSplashTextPositions.z =
(mRenderer->getScreenWidth() - mSplashTextPopulating->metrics.size.x) / 2.0f;
mSplashTextPositions.w =
(mRenderer->getScreenWidth() - mSplashTextReloading->metrics.size.x) / 2.0f;
mSplashTextPositions.y = mRenderer->getScreenHeight() * 0.745f;
ProgressBarRectangle progressBarRect;
if (mRenderer->getScreenWidth() > mRenderer->getScreenHeight())
progressBarRect.barWidth = mRenderer->getScreenHeight() * 0.53f;
else
progressBarRect.barWidth = mRenderer->getScreenWidth() * 0.53f;
progressBarRect.barHeight = mDefaultFonts.at(1)->getLetterHeight() * 1.3f;
progressBarRect.barPosX =
(mRenderer->getScreenWidth() / 2.0f) - (progressBarRect.barWidth / 2.0f);
progressBarRect.barPosY = mSplashTextPositions.y + (progressBarRect.barHeight * 1.65f);
progressBarRect.color = DEFAULT_TEXTCOLOR;
mProgressBarRectangles.emplace_back(progressBarRect);
const float borderThickness {std::ceil(2.0f * mRenderer->getScreenHeightModifier())};
progressBarRect.barWidth -= borderThickness * 2.0f;
progressBarRect.barHeight -= borderThickness * 2.0f;
progressBarRect.barPosX += borderThickness;
progressBarRect.barPosY += borderThickness;
progressBarRect.color = 0x000000FF;
mProgressBarRectangles.emplace_back(progressBarRect);
progressBarRect.barWidth -= borderThickness * 2.0f;
progressBarRect.barHeight -= borderThickness * 2.0f;
progressBarRect.barPosX += borderThickness;
progressBarRect.barPosY += borderThickness;
progressBarRect.color = 0xA1001DFF;
mProgressBarRectangles.emplace_back(progressBarRect);
mBackgroundOverlay->setImage(":/graphics/frame.png"); mBackgroundOverlay->setImage(":/graphics/frame.png");
mBackgroundOverlay->setResize(mRenderer->getScreenWidth(), mRenderer->getScreenHeight()); mBackgroundOverlay->setResize(mRenderer->getScreenWidth(), mRenderer->getScreenHeight());
@ -605,23 +646,50 @@ void Window::render()
} }
} }
void Window::renderLoadingScreen(std::string text) void Window::renderSplashScreen(SplashScreenState state, float progress)
{ {
glm::mat4 trans {mRenderer->getIdentity()}; glm::mat4 trans {mRenderer->getIdentity()};
mRenderer->setMatrix(trans); mRenderer->setMatrix(trans);
mRenderer->drawRect(0.0f, 0.0f, mRenderer->getScreenWidth(), mRenderer->getScreenHeight(), mRenderer->drawRect(0.0f, 0.0f, mRenderer->getScreenWidth(), mRenderer->getScreenHeight(),
0x000000FF, 0x000000FF); 0x000000FF, 0x000000FF);
mSplash->render(trans); mSplash->render(trans);
auto& font = mDefaultFonts.at(1);
TextCache* cache {font->buildTextCache(text, 0.0f, 0.0f, 0x656565FF)};
float x {std::round((mRenderer->getScreenWidth() - cache->metrics.size.x) / 2.0f)};
float y {std::round(mRenderer->getScreenHeight() * 0.835f)};
trans = glm::translate(trans, glm::round(glm::vec3 {x, y, 0.0f}));
mRenderer->setMatrix(trans); mRenderer->setMatrix(trans);
font->renderTextCache(cache);
delete cache; if (state != SplashScreenState::RELOADING) {
// We need to render three rectangles: border, black center and actual progress bar.
for (size_t i {0}; i < mProgressBarRectangles.size(); ++i) {
const float rectWidth {i == mProgressBarRectangles.size() - 1 ? progress : 1.0f};
mRenderer->drawRect(
mProgressBarRectangles.at(i).barPosX, mProgressBarRectangles.at(i).barPosY,
mProgressBarRectangles.at(i).barWidth * rectWidth,
mProgressBarRectangles.at(i).barHeight, mProgressBarRectangles.at(i).color,
mProgressBarRectangles.at(i).color);
}
}
float textPosX {0.0f};
float textPosY {mSplashTextPositions.y};
if (state == SplashScreenState::SCANNING) {
textPosX = mSplashTextPositions.x;
}
else if (state == SplashScreenState::POPULATING) {
textPosX = mSplashTextPositions.z;
}
else if (state == SplashScreenState::RELOADING) {
textPosX = mSplashTextPositions.w;
textPosY += mDefaultFonts.at(1)->getLetterHeight();
}
trans = glm::translate(trans, glm::round(glm::vec3 {textPosX, textPosY, 0.0f}));
mRenderer->setMatrix(trans);
if (state == SplashScreenState::SCANNING)
mDefaultFonts.at(1)->renderTextCache(mSplashTextScanning.get());
else if (state == SplashScreenState::POPULATING)
mDefaultFonts.at(1)->renderTextCache(mSplashTextPopulating.get());
else if (state == SplashScreenState::RELOADING)
mDefaultFonts.at(1)->renderTextCache(mSplashTextReloading.get());
mRenderer->swapBuffers(); mRenderer->swapBuffers();
} }
@ -707,14 +775,6 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
mHelp->setPrompts(addPrompts); mHelp->setPrompts(addPrompts);
} }
void Window::reloadHelpPrompts()
{
if (mHelp) {
delete mHelp;
mHelp = new HelpComponent;
}
}
void Window::stopInfoPopup() void Window::stopInfoPopup()
{ {
if (mInfoPopup) if (mInfoPopup)

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// Window.h // Window.h
// //
// Window management, screensaver management, and help prompts. // Window management, screensaver management, help prompts and splash screen.
// The input stack starts here as well, as this is the first instance called by InputManager. // The input stack starts here as well, as this is the first instance called by InputManager.
// //
@ -92,13 +92,18 @@ public:
void normalizeNextUpdate() { mNormalizeNextUpdate = true; } void normalizeNextUpdate() { mNormalizeNextUpdate = true; }
void renderLoadingScreen(std::string text); enum class SplashScreenState {
SCANNING,
POPULATING,
RELOADING
};
void renderSplashScreen(SplashScreenState state, float progress);
// The list scroll overlay is triggered from IList when the highest scrolling tier is reached. // The list scroll overlay is triggered from IList when the highest scrolling tier is reached.
void renderListScrollOverlay(const float opacity, const std::string& text); void renderListScrollOverlay(const float opacity, const std::string& text);
void renderHelpPromptsEarly(); // Used to render HelpPrompts before a fade. void renderHelpPromptsEarly(); // Used to render HelpPrompts before a fade.
void setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpStyle& style); void setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpStyle& style);
void reloadHelpPrompts();
// GuiInfoPopup notifications. // GuiInfoPopup notifications.
void queueInfoPopup(const std::string& message, const int& duration) void queueInfoPopup(const std::string& message, const int& duration)
@ -150,10 +155,24 @@ private:
// Returns true if at least one component on the stack is processing. // Returns true if at least one component on the stack is processing.
bool isProcessing(); bool isProcessing();
struct ProgressBarRectangle {
float barWidth;
float barHeight;
float barPosX;
float barPosY;
unsigned int color;
};
Renderer* mRenderer; Renderer* mRenderer;
HelpComponent* mHelp; std::unique_ptr<HelpComponent> mHelp;
ImageComponent* mBackgroundOverlay; std::unique_ptr<ImageComponent> mBackgroundOverlay;
ImageComponent* mSplash; std::unique_ptr<ImageComponent> mSplash;
std::unique_ptr<TextCache> mSplashTextScanning;
std::unique_ptr<TextCache> mSplashTextPopulating;
std::unique_ptr<TextCache> mSplashTextReloading;
glm::vec4 mSplashTextPositions;
std::vector<ProgressBarRectangle> mProgressBarRectangles;
float mBackgroundOverlayOpacity; float mBackgroundOverlayOpacity;
std::vector<GuiComponent*> mGuiStack; std::vector<GuiComponent*> mGuiStack;
std::vector<std::shared_ptr<Font>> mDefaultFonts; std::vector<std::shared_ptr<Font>> mDefaultFonts;

View file

@ -340,6 +340,7 @@ void RendererOpenGL::setSwapInterval()
LOG(LogInfo) << "Enabling VSync..."; LOG(LogInfo) << "Enabling VSync...";
} }
else { else {
Settings::getInstance()->setBool("VSync", false);
LOG(LogWarning) << "Could not enable VSync: " << SDL_GetError(); LOG(LogWarning) << "Could not enable VSync: " << SDL_GetError();
} }
} }