diff --git a/src/celengine/dsodb.cpp b/src/celengine/dsodb.cpp index c17eb5e02e..ec5ab64e83 100644 --- a/src/celengine/dsodb.cpp +++ b/src/celengine/dsodb.cpp @@ -230,7 +230,7 @@ bool DSODatabase::load(std::istream& in, const fs::path& resourcePath) const Hash* objParams = objParamsValue.getHash(); if (objParams == nullptr) { - GetLogger()->error("Error parsing deep sky catalog entry {}\n", objName.c_str()); + GetLogger()->error("Error parsing deep sky catalog entry {}\n", objName); return false; } diff --git a/src/celengine/meshmanager.h b/src/celengine/meshmanager.h index 37a6bd3627..180594ed9f 100644 --- a/src/celengine/meshmanager.h +++ b/src/celengine/meshmanager.h @@ -97,6 +97,6 @@ inline bool operator<(const GeometryInfo::ResourceKey& k0, std::tie(k1.resolvedPath, k1.center.x(), k1.center.y(), k1.center.z(), k1.scale, k1.isNormalized); } -using GeometryManager = ResourceManager; +using GeometryManager = ResourceManager; extern GeometryManager* GetGeometryManager(); diff --git a/src/celestia/celestiacore.cpp b/src/celestia/celestiacore.cpp index 2a46bb4140..47b408d799 100644 --- a/src/celestia/celestiacore.cpp +++ b/src/celestia/celestiacore.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -2307,6 +2308,11 @@ void CelestiaCore::updateFOV(float newFOV, std::optional focus, } } +template +bool ready(const std::future &future) +{ + return future.wait_for(std::chrono::seconds(1)) == std::future_status::ready; +} bool CelestiaCore::initSimulation(const fs::path& configFileName, const vector& extrasDirs, @@ -2391,23 +2397,46 @@ bool CelestiaCore::initSimulation(const fs::path& configFileName, StarDetails::SetStarTextures(config->starTextures); - std::unique_ptr starCatalog = loadStars(*config, progressNotifier); - if (starCatalog == nullptr) - { - fatalError(_("Cannot read star database."), false); - return false; - } - universe->setStarCatalog(std::move(starCatalog)); + progressNotifier->update(_("Deep Space Objects and Star catalogs")); - /***** Load the deep sky catalogs *****/ + auto *cfg = config.get(); - std::unique_ptr dsoCatalog = loadDSO(*config, progressNotifier); - if (dsoCatalog == nullptr) + std::future> starsLoader = std::async(std::launch::async, [cfg]() { + return loadStars(*cfg, nullptr); + }); + + std::future> dsoLoader = std::async(std::launch::async, [cfg]() { + return loadDSO(*cfg, nullptr); + }); + + for (bool starsReady = false, dsoReady = false; !starsReady || !dsoReady;) { - fatalError(_("Cannot read DSO database."), false); - return false; + if (!starsReady && ready(starsLoader)) + { + starsReady = true; + + std::unique_ptr starCatalog = starsLoader.get(); + if (starCatalog == nullptr) + { + fatalError(_("Cannot read star database."), false); + return false; + } + universe->setStarCatalog(std::move(starCatalog)); + } + + if (!dsoReady && ready(dsoLoader)) + { + dsoReady = true; + + std::unique_ptr dsoCatalog = dsoLoader.get(); + if (dsoCatalog == nullptr) + { + fatalError(_("Cannot read DSO database."), false); + return false; + } + universe->setDSOCatalog(std::move(dsoCatalog)); + } } - universe->setDSOCatalog(std::move(dsoCatalog)); /***** Load the solar system catalogs *****/ @@ -2669,7 +2698,7 @@ void CelestiaCore::fatalError(const string& msg, bool visual) if (visual) flash(msg); else - GetLogger()->error(msg.c_str()); + GetLogger()->error(msg); } else { @@ -3270,14 +3299,14 @@ void CelestiaCore::setAudioNoPause(int channel, bool nopause) void CelestiaCore::pauseAudioIfNeeded() { - for (auto const &[_, value] : audioSessions) + for (const auto &[_, value] : audioSessions) if (!value->nopause()) value->stop(); } void CelestiaCore::resumeAudioIfNeeded() { - for (auto const &[_, value] : audioSessions) + for (const auto &[_, value] : audioSessions) if (!value->nopause()) value->play(); } diff --git a/src/celutil/logger.cpp b/src/celutil/logger.cpp index 8dd5a848e5..9882da3fb7 100644 --- a/src/celutil/logger.cpp +++ b/src/celutil/logger.cpp @@ -9,48 +9,74 @@ // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. +#include #include +#include #ifdef _MSC_VER -#include +# include #endif -#include #include "logger.h" +#include + namespace celestia::util { -Logger* Logger::g_logger = nullptr; +namespace +{ +std::unique_ptr globalLogger = nullptr; +} -Logger* GetLogger() +Logger * +GetLogger() { - return Logger::g_logger; + return globalLogger.get(); } -Logger* CreateLogger(Level level) +Logger * +CreateLogger(Level level) { return CreateLogger(level, std::clog, std::cerr); } -Logger* CreateLogger(Level level, Logger::Stream &log, Logger::Stream &err) +Logger * +CreateLogger(Level level, Logger::Stream &log, Logger::Stream &err) { - if (Logger::g_logger == nullptr) - Logger::g_logger = new Logger(level, log, err); - return Logger::g_logger; + if (globalLogger == nullptr) + { + globalLogger = std::make_unique(level, log, err); + std::atexit(DestroyLogger); + } + return globalLogger.get(); } -void DestroyLogger() +void +DestroyLogger() { - delete Logger::g_logger; + globalLogger = nullptr; } Logger::Logger() : - m_log(std::clog), - m_err(std::cerr) + Logger(Level::Info, std::clog, std::cerr) +{ +} + +Logger::Logger(Level level, Stream &log, Stream &err) : + m_log(log), + m_err(err), + m_level(level) +{ +} + +void +Logger::setLevel(Level level) { + m_level = level; } -void Logger::vlog(Level level, fmt::string_view format, fmt::format_args args) const +void +Logger::vlog(Level level, std::string_view format, fmt::format_args args) const { #ifdef _MSC_VER if (level == Level::Debug && IsDebuggerPresent()) @@ -61,7 +87,7 @@ void Logger::vlog(Level level, fmt::string_view format, fmt::format_args args) c #endif auto &stream = (level <= Level::Warning || level == Level::Debug) ? m_err : m_log; - fmt::vprint(stream, format, args); + fmt::print(stream, fmt::vformat(format, args)); } } // end namespace celestia::util diff --git a/src/celutil/logger.h b/src/celutil/logger.h index ea5ba85ccf..e07e11153a 100644 --- a/src/celutil/logger.h +++ b/src/celutil/logger.h @@ -1,6 +1,6 @@ // logger.h // -// Copyright (C) 2021, Celestia Development Team +// Copyright (C) 2021-present, Celestia Development Team // // Logging functions. // @@ -11,20 +11,17 @@ #pragma once +#include +#include #include #include +#include -#include - -#include - -template <> -struct fmt::formatter : formatter +template<> struct fmt::formatter : formatter { - template - auto format(const fs::path &path, FormatContext &ctx) + template auto format(const fs::path &path, FormatContext &ctx) { - return formatter::format(path.string(), ctx); + return formatter::format(path.u8string(), ctx); } }; @@ -42,91 +39,111 @@ enum class Level class Logger { - public: +public: using Stream = std::basic_ostream; + /** Create a default logger with Info verbocity writing to std::clog and std::cerr */ Logger(); - Logger(Level level, Stream &log, Stream &err) : - m_log(log), - m_err(err), - m_level(level) - {} - void setLevel(Level level) - { - m_level = level; - } + /** + * Create a logger with custom verbocity and output streams + * @param level verbosibility level + * @param log stream used to output normal messages + * @param err stream used to output errors, warnings and debug messages + */ + Logger(Level level, Stream &log, Stream &err); - template inline void - debug(const char *format, const Args&... args) const; + /** + * Set verbocity level + * + * @param level verbosibility level + */ + void setLevel(Level level); - template inline void - info(const char *format, const Args&... args) const; + template + inline void debug(std::string_view format, const Args &...args) const; - template inline void - verbose(const char *format, const Args&... args) const; + template + inline void info(std::string_view format, const Args &...args) const; - template inline void - warn(const char *format, const Args&... args) const; + template + inline void verbose(std::string_view format, const Args &...args) const; - template inline void - error(const char *format, const Args&... args) const; + template + inline void warn(std::string_view format, const Args &...args) const; - template void - log(Level, const char *format, const Args&... args) const; + template + inline void error(std::string_view format, const Args &...args) const; - static Logger* g_logger; + template + void log(Level, std::string_view format, const Args &...args) const; - private: - void vlog(Level level, fmt::string_view format, fmt::format_args args) const; +private: + void vlog(Level level, std::string_view format, fmt::format_args args) const; Stream &m_log; Stream &m_err; - Level m_level { Level::Info }; + Level m_level; }; -template void -Logger::log(Level level, const char *format, const Args&... args) const +template void +Logger::log(Level level, std::string_view format, const Args &...args) const { if (level <= m_level) - vlog(level, fmt::string_view(format), fmt::make_format_args(args...)); + vlog(level, format, fmt::make_format_args(args...)); } -template void -Logger::debug(const char *format, const Args&... args) const +template void +Logger::debug(std::string_view format, const Args &...args) const { log(Level::Debug, format, args...); } -template inline void -Logger::info(const char *format, const Args&... args) const +template inline void +Logger::info(std::string_view format, const Args &...args) const { log(Level::Info, format, args...); } -template inline void -Logger::verbose(const char *format, const Args&... args) const +template inline void +Logger::verbose(std::string_view format, const Args &...args) const { log(Level::Verbose, format, args...); } -template inline void -Logger::warn(const char *format, const Args&... args) const +template inline void +Logger::warn(std::string_view format, const Args &...args) const { log(Level::Warning, format, args...); } -template inline void -Logger::error(const char *format, const Args&... args) const +template inline void +Logger::error(std::string_view format, const Args &...args) const { log(Level::Error, format, args...); } -Logger* GetLogger(); -Logger* CreateLogger(Level level = Level::Info); -Logger* CreateLogger(Level level, - Logger::Stream &log, - Logger::Stream &err); +/** Return a pointer to the default global Logger instance */ +Logger *GetLogger(); + +/** + * Initiliaze the default global Logger instance writing to std::clog and std::cerr + * + * @param level verbosibility level + * @return pointer to the default global Logger instance + */ +Logger *CreateLogger(Level level = Level::Info); + +/** + * Initiliaze the default global Logger instance writing to custom streams + * + * @param level verbosibility level + * @param log stream used to output normal messages + * @param err stream used to output errors, warnings and debug messages +*/ +Logger *CreateLogger(Level level, Logger::Stream &log, Logger::Stream &err); + +/** Destroy the default global Logger instance */ void DestroyLogger(); } // end namespace celestia::util diff --git a/src/celutil/resmanager.h b/src/celutil/resmanager.h index 1e43ae9331..ac8657fa9b 100644 --- a/src/celutil/resmanager.h +++ b/src/celutil/resmanager.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include #include @@ -18,16 +19,19 @@ #include -enum class ResourceState { +enum class ResourceState +{ NotLoaded = 0, Loaded = 1, LoadingFailed = 2, + LoadingAsync = 3, }; -template class ResourceManager +template class ResourceManager { - public: + static constexpr bool async = A; +public: explicit ResourceManager(const fs::path& _baseDir) : baseDir(_baseDir) {}; ~ResourceManager() = default; @@ -63,13 +67,17 @@ template class ResourceManager { loadResource(resources[h]); } + else if (resources[h].state == ResourceState::LoadingAsync) + { + handleAsyncLoad(resources[h]); + } return resources[h].state == ResourceState::Loaded ? resources[h].resource.get() : nullptr; } - private: +private: using KeyType = typename T::ResourceKey; struct InfoType @@ -77,6 +85,7 @@ template class ResourceManager T info; ResourceState state{ ResourceState::NotLoaded }; std::shared_ptr resource{ nullptr }; + std::future future; explicit InfoType(T _info) : info(std::move(_info)) {} InfoType(const InfoType&) = delete; @@ -113,7 +122,39 @@ template class ResourceManager info.resource = std::move(resource); info.state = ResourceState::Loaded; } - else if (info.load(resolvedKey)) + else + { + if constexpr (async) + { + info.future = std::async(std::launch::async, [&info, key = std::move(resolvedKey)]() + { + return info.load(key); + }); + info.state = ResourceState::LoadingAsync; + } + else + { + bool good = info.load(resolvedKey); + finish(good, info, resolvedKey); + } + } + } + + void handleAsyncLoad(InfoType& info) + { + if constexpr (async) + { + if (info.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + bool good = info.future.get(); + finish(good, info, info.resolve(baseDir)); + } + } + } + + void finish(bool good, InfoType& info, const KeyType &resolvedKey) + { + if (good) { info.state = ResourceState::Loaded; if (auto [iter, inserted] = loadedResources.try_emplace(std::move(resolvedKey), info.resource); !inserted)