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;
}
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;
system = system.next_sibling("system")) {
std::string name;
@ -521,6 +530,13 @@ bool SystemData::loadConfig()
sortName = system.child("systemsortname").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 = [&] {
for (auto system : sSystemVector) {
if (system->mName == name) {
@ -581,7 +597,7 @@ bool SystemData::loadConfig()
}
// 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
// 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
// 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.
std::string saveStateDir = Utils::FileSystem::expandHomePath(
"~/Library/Saved Application State/org.es-de.EmulationStation.savedState");
std::string saveStateDir {Utils::FileSystem::expandHomePath(
"~/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
// 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
// step as an extra precaution.
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);
}
}
else {
Utils::FileSystem::createDirectory(saveStateDir);
@ -683,8 +682,6 @@ int main(int argc, char* argv[])
window->pushGui(ViewController::getInstance());
bool splashScreen {Settings::getInstance()->getBool("SplashScreen")};
InputManager::getInstance().parseEvent(event);
if (event.type == SDL_QUIT)
return 1;
@ -707,15 +704,18 @@ int main(int argc, char* argv[])
}
#endif
if (splashScreen) {
std::string progressText {"Loading system config..."};
window->renderLoadingScreen(progressText);
}
AudioManager::getInstance();
MameNames::getInstance();
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()};
if (splashScreen && vSync)
SDL_GL_SetSwapInterval(1);
if (loadSystemsStatus) {
// 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.
if (!loadSystemsStatus) {
if (forceInputConfig) {

View file

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

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition
// 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.
//
@ -25,6 +25,8 @@
Window::Window() noexcept
: mRenderer {Renderer::getInstance()}
, mSplashTextPositions {0.0f, 0.0f, 0.0f, 0.0f}
, mBackgroundOverlayOpacity {1.0f}
, mScreensaver {nullptr}
, mMediaViewer {nullptr}
, mLaunchScreen {nullptr}
@ -47,23 +49,19 @@ Window::Window() noexcept
, mInvalidateCacheTimer {0}
, mVideoPlayerCount {0}
, mTopScale {0.5f}
, mRenderedHelpPrompts {false}
, mChangedThemeSet {false}
{
}
Window::~Window()
{
delete mBackgroundOverlay;
delete mSplash;
// Delete all our GUIs.
while (peekGui())
delete peekGui();
if (mInfoPopup)
delete mInfoPopup;
delete mHelp;
}
Window* Window::getInstance()
@ -112,10 +110,10 @@ bool Window::init()
ResourceManager::getInstance().reloadAll();
mHelp = new HelpComponent;
mSplash = new ImageComponent;
mHelp = std::make_unique<HelpComponent>();
mSplash = std::make_unique<ImageComponent>(false, false);
mBackgroundOverlay = new ImageComponent;
mBackgroundOverlay = std::make_unique<ImageComponent>(false, false);
mBackgroundOverlayOpacity = 0.0f;
// 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,
(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->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()};
mRenderer->setMatrix(trans);
mRenderer->drawRect(0.0f, 0.0f, mRenderer->getScreenWidth(), mRenderer->getScreenHeight(),
0x000000FF, 0x000000FF);
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);
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();
}
@ -707,14 +775,6 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
mHelp->setPrompts(addPrompts);
}
void Window::reloadHelpPrompts()
{
if (mHelp) {
delete mHelp;
mHelp = new HelpComponent;
}
}
void Window::stopInfoPopup()
{
if (mInfoPopup)

View file

@ -3,7 +3,7 @@
// EmulationStation Desktop Edition
// 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.
//
@ -92,13 +92,18 @@ public:
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.
void renderListScrollOverlay(const float opacity, const std::string& text);
void renderHelpPromptsEarly(); // Used to render HelpPrompts before a fade.
void setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpStyle& style);
void reloadHelpPrompts();
// GuiInfoPopup notifications.
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.
bool isProcessing();
struct ProgressBarRectangle {
float barWidth;
float barHeight;
float barPosX;
float barPosY;
unsigned int color;
};
Renderer* mRenderer;
HelpComponent* mHelp;
ImageComponent* mBackgroundOverlay;
ImageComponent* mSplash;
std::unique_ptr<HelpComponent> mHelp;
std::unique_ptr<ImageComponent> mBackgroundOverlay;
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;
std::vector<GuiComponent*> mGuiStack;
std::vector<std::shared_ptr<Font>> mDefaultFonts;

View file

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