Fix crash related to async content loading (#7508)

macos_ci_fix
Petr Mikheev 1 year ago
parent d2f16774d9
commit 2b601eea6d

@ -2,8 +2,8 @@
#include <cerrno> #include <cerrno>
#include <chrono> #include <chrono>
#include <future>
#include <system_error> #include <system_error>
#include <thread>
#include <osgDB/WriteFile> #include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> #include <osgViewer/ViewerEventHandlers>
@ -38,6 +38,7 @@
#include <components/l10n/manager.hpp> #include <components/l10n/manager.hpp>
#include <components/loadinglistener/asynclistener.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/frameratelimiter.hpp> #include <components/misc/frameratelimiter.hpp>
@ -742,8 +743,10 @@ void OMW::Engine::prepareEngine()
mWorld = std::make_unique<MWWorld::World>( mWorld = std::make_unique<MWWorld::World>(
mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath()); mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath());
std::thread loadDataThread( Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
[&] { mWorld->loadData(mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder.get()); }); Loading::AsyncListener asyncListener(*listener);
auto dataLoading = std::async(std::launch::async,
[&] { mWorld->loadData(mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder.get(), &asyncListener); });
if (!mSkipMenu) if (!mSkipMenu)
{ {
@ -752,9 +755,12 @@ void OMW::Engine::prepareEngine()
mWindowManager->playVideo(logo, true); mWindowManager->playVideo(logo, true);
} }
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn(); listener->loadingOn();
loadDataThread.join(); {
using namespace std::chrono_literals;
while (dataLoading.wait_for(50ms) != std::future_status::ready)
asyncListener.update();
}
listener->loadingOff(); listener->loadingOff();
mWorld->init(mViewer, rootNode, mWorkQueue.get(), *mUnrefQueue); mWorld->init(mViewer, rootNode, mWorkQueue.get(), *mUnrefQueue);

@ -76,10 +76,6 @@ namespace MWGui
void LoadingScreen::setLabel(const std::string& label, bool important) void LoadingScreen::setLabel(const std::string& label, bool important)
{ {
std::lock_guard<std::mutex> guard(mMutex);
if (!isVisible())
return;
mImportantLabel = important; mImportantLabel = important;
mLoadingText->setCaptionWithReplacing(label); mLoadingText->setCaptionWithReplacing(label);
@ -145,8 +141,6 @@ namespace MWGui
void LoadingScreen::loadingOn(bool visible) void LoadingScreen::loadingOn(bool visible)
{ {
std::lock_guard<std::mutex> guard(mMutex);
// Early-out if already on // Early-out if already on
if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible()) if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible())
return; return;
@ -187,8 +181,6 @@ namespace MWGui
void LoadingScreen::loadingOff() void LoadingScreen::loadingOff()
{ {
std::lock_guard<std::mutex> guard(mMutex);
if (--mNestedLoadingCount > 0) if (--mNestedLoadingCount > 0)
return; return;
mLoadingBox->setVisible(true); // restore mLoadingBox->setVisible(true); // restore
@ -247,10 +239,6 @@ namespace MWGui
void LoadingScreen::setProgress(size_t value) void LoadingScreen::setProgress(size_t value)
{ {
std::lock_guard<std::mutex> guard(mMutex);
if (!isVisible())
return;
// skip expensive update if there isn't enough visible progress // skip expensive update if there isn't enough visible progress
if (mProgressBar->getWidth() <= 0 if (mProgressBar->getWidth() <= 0
|| value - mProgress < mProgressBar->getScrollRange() / mProgressBar->getWidth()) || value - mProgress < mProgressBar->getScrollRange() / mProgressBar->getWidth())
@ -265,10 +253,6 @@ namespace MWGui
void LoadingScreen::increaseProgress(size_t increase) void LoadingScreen::increaseProgress(size_t increase)
{ {
std::lock_guard<std::mutex> guard(mMutex);
if (!isVisible())
return;
mProgressBar->setScrollPosition(0); mProgressBar->setScrollPosition(0);
size_t value = mProgress + increase; size_t value = mProgress + increase;
value = std::min(value, mProgressBar->getScrollRange() - 1); value = std::min(value, mProgressBar->getScrollRange() - 1);

@ -2,7 +2,6 @@
#define MWGUI_LOADINGSCREEN_H #define MWGUI_LOADINGSCREEN_H
#include <memory> #include <memory>
#include <mutex>
#include <osg/Timer> #include <osg/Timer>
#include <osg/ref_ptr> #include <osg/ref_ptr>
@ -70,8 +69,6 @@ namespace MWGui
bool mVisible; bool mVisible;
int mNestedLoadingCount; int mNestedLoadingCount;
std::mutex mMutex;
size_t mProgress; size_t mProgress;
bool mShowWallpaper; bool mShowWallpaper;

@ -270,13 +270,12 @@ namespace MWWorld
} }
void World::loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles, void World::loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles,
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder) const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
{ {
mContentFiles = contentFiles; mContentFiles = contentFiles;
if (encoder) if (encoder)
mReaders.setStatelessEncoder(encoder->getStatelessEncoder()); mReaders.setStatelessEncoder(encoder->getStatelessEncoder());
mESMVersions.resize(mContentFiles.size(), -1); mESMVersions.resize(mContentFiles.size(), -1);
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
loadContentFiles(fileCollections, contentFiles, encoder, listener); loadContentFiles(fileCollections, contentFiles, encoder, listener);
loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder, listener); loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder, listener);

@ -199,7 +199,8 @@ namespace MWWorld
const std::filesystem::path& userDataPath); const std::filesystem::path& userDataPath);
void loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles, void loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles,
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder); const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder,
Loading::Listener* listener);
// Must be called after `loadData`. // Must be called after `loadData`.
void init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue, void init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,

@ -292,7 +292,7 @@ add_component_dir (terrain
) )
add_component_dir (loadinglistener add_component_dir (loadinglistener
loadinglistener loadinglistener asynclistener
) )
add_component_dir (myguiplatform add_component_dir (myguiplatform

@ -0,0 +1,67 @@
#ifndef COMPONENTS_LOADINGLISTENER_ASYNCLISTENER_H
#define COMPONENTS_LOADINGLISTENER_ASYNCLISTENER_H
#include <mutex>
#include <optional>
#include <string>
#include "loadinglistener.hpp"
namespace Loading
{
class AsyncListener : public Listener
{
public:
AsyncListener(Listener& baseListener)
: mBaseListener(baseListener)
{
}
void setLabel(const std::string& label, bool important) override
{
std::lock_guard<std::mutex> guard(mMutex);
mLabelUpdate = label;
mImportantLabel = important;
}
void setProgressRange(size_t range) override
{
std::lock_guard<std::mutex> guard(mMutex);
mRangeUpdate = range;
}
void setProgress(size_t value) override
{
std::lock_guard<std::mutex> guard(mMutex);
mProgressUpdate = value;
}
void increaseProgress(size_t increase) override
{ /* not implemented */
}
void update()
{
std::lock_guard<std::mutex> guard(mMutex);
if (mLabelUpdate)
mBaseListener.setLabel(*mLabelUpdate, mImportantLabel);
if (mRangeUpdate)
mBaseListener.setProgressRange(*mRangeUpdate);
if (mProgressUpdate)
mBaseListener.setProgress(*mProgressUpdate);
mLabelUpdate = std::nullopt;
mRangeUpdate = std::nullopt;
mProgressUpdate = std::nullopt;
}
private:
Listener& mBaseListener;
std::mutex mMutex;
std::optional<std::string> mLabelUpdate;
bool mImportantLabel = false;
std::optional<size_t> mRangeUpdate;
std::optional<size_t> mProgressUpdate;
};
}
#endif // COMPONENTS_LOADINGLISTENER_ASYNCLISTENER_H
Loading…
Cancel
Save