mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-23 02:41:58 -05:00
Merge pull request #22301 from AaronVanGeffen/more-progress-bars
Add progress bars to loading saved games and scenarios
This commit is contained in:
commit
1d361a07e3
15 changed files with 153 additions and 50 deletions
|
@ -3720,6 +3720,9 @@ STR_6645 :Makes some UI elements bigger so they are easier to click or tap.
|
||||||
STR_6646 :Author: {STRING}
|
STR_6646 :Author: {STRING}
|
||||||
STR_6647 :Authors: {STRING}
|
STR_6647 :Authors: {STRING}
|
||||||
STR_6648 :Loading plugin engine…
|
STR_6648 :Loading plugin engine…
|
||||||
|
STR_6649 :Loading scenario…
|
||||||
|
STR_6650 :Loading saved game…
|
||||||
|
STR_6651 :{STRING} ({COMMA32}%)
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# Scenarios #
|
# Scenarios #
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
- Feature: [#20832] The ride music tab now shows a track listing for the current music style.
|
- Feature: [#20832] The ride music tab now shows a track listing for the current music style.
|
||||||
- Feature: [#22172] [Plugin] Expose ride satisfaction ratings to the plugin API.
|
- Feature: [#22172] [Plugin] Expose ride satisfaction ratings to the plugin API.
|
||||||
- Feature: [#22213] [Plugin] Allow plugins to focus on textboxes in custom windows.
|
- Feature: [#22213] [Plugin] Allow plugins to focus on textboxes in custom windows.
|
||||||
|
- Feature: [#22301] Loading save games or scenarios now indicates loading progress.
|
||||||
- Feature: [OpenMusic#54] Added Progressive ride music style (feat. Approaching Nirvana).
|
- Feature: [OpenMusic#54] Added Progressive ride music style (feat. Approaching Nirvana).
|
||||||
- Change: [#22230] The plugin/script engine is now initialised off the main thread.
|
- Change: [#22230] The plugin/script engine is now initialised off the main thread.
|
||||||
- Change: [#22251] Hide author info in the scenery window unless debug tools are active.
|
- Change: [#22251] Hide author info in the scenery window unless debug tools are active.
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <openrct2/entity/EntityRegistry.h>
|
#include <openrct2/entity/EntityRegistry.h>
|
||||||
#include <openrct2/interface/Viewport.h>
|
#include <openrct2/interface/Viewport.h>
|
||||||
#include <openrct2/interface/Window.h>
|
#include <openrct2/interface/Window.h>
|
||||||
|
#include <openrct2/localisation/StringIds.h>
|
||||||
#include <openrct2/management/NewsItem.h>
|
#include <openrct2/management/NewsItem.h>
|
||||||
#include <openrct2/object/ObjectManager.h>
|
#include <openrct2/object/ObjectManager.h>
|
||||||
#include <openrct2/scenario/ScenarioRepository.h>
|
#include <openrct2/scenario/ScenarioRepository.h>
|
||||||
|
@ -48,6 +49,7 @@ namespace OpenRCT2::Title
|
||||||
std::unique_ptr<TitleSequence> _sequence;
|
std::unique_ptr<TitleSequence> _sequence;
|
||||||
int32_t _position = 0;
|
int32_t _position = 0;
|
||||||
int32_t _waitCounter = 0;
|
int32_t _waitCounter = 0;
|
||||||
|
bool _initialLoadCommand = true;
|
||||||
|
|
||||||
int32_t _previousWindowWidth = 0;
|
int32_t _previousWindowWidth = 0;
|
||||||
int32_t _previousWindowHeight = 0;
|
int32_t _previousWindowHeight = 0;
|
||||||
|
@ -207,6 +209,7 @@ namespace OpenRCT2::Title
|
||||||
{
|
{
|
||||||
_position = 0;
|
_position = 0;
|
||||||
_waitCounter = 0;
|
_waitCounter = 0;
|
||||||
|
_initialLoadCommand = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Seek(int32_t targetPosition) override
|
void Seek(int32_t targetPosition) override
|
||||||
|
@ -280,6 +283,20 @@ namespace OpenRCT2::Title
|
||||||
return _position != entryPosition;
|
return _position != entryPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReportProgress(uint8_t progress)
|
||||||
|
{
|
||||||
|
if (!_initialLoadCommand)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (progress == 0)
|
||||||
|
GetContext()->OpenProgress(STR_LOADING_TITLE_SEQUENCE);
|
||||||
|
|
||||||
|
GetContext()->SetProgress(progress, 100, STR_STRING_M_PERCENT, true);
|
||||||
|
|
||||||
|
if (progress == 100)
|
||||||
|
GetContext()->CloseProgress();
|
||||||
|
}
|
||||||
|
|
||||||
bool LoadParkFromFile(const u8string& path)
|
bool LoadParkFromFile(const u8string& path)
|
||||||
{
|
{
|
||||||
LOG_VERBOSE("TitleSequencePlayer::LoadParkFromFile(%s)", path.c_str());
|
LOG_VERBOSE("TitleSequencePlayer::LoadParkFromFile(%s)", path.c_str());
|
||||||
|
@ -294,24 +311,39 @@ namespace OpenRCT2::Title
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Inhibit viewport rendering while we're loading
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
|
||||||
|
|
||||||
|
ReportProgress(0);
|
||||||
auto parkImporter = ParkImporter::Create(path);
|
auto parkImporter = ParkImporter::Create(path);
|
||||||
|
|
||||||
auto result = parkImporter->Load(path);
|
auto result = parkImporter->Load(path);
|
||||||
|
ReportProgress(10);
|
||||||
|
|
||||||
auto& objectManager = GetContext()->GetObjectManager();
|
auto& objectManager = GetContext()->GetObjectManager();
|
||||||
objectManager.LoadObjects(result.RequiredObjects);
|
objectManager.LoadObjects(result.RequiredObjects, true);
|
||||||
|
ReportProgress(90);
|
||||||
|
|
||||||
// TODO: Have a separate GameState and exchange once loaded.
|
// TODO: Have a separate GameState and exchange once loaded.
|
||||||
auto& gameState = GetGameState();
|
auto& gameState = GetGameState();
|
||||||
parkImporter->Import(gameState);
|
parkImporter->Import(gameState);
|
||||||
|
ReportProgress(100);
|
||||||
|
|
||||||
MapAnimationAutoCreate();
|
MapAnimationAutoCreate();
|
||||||
}
|
}
|
||||||
PrepareParkForPlayback();
|
PrepareParkForPlayback();
|
||||||
|
_initialLoadCommand = false;
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
catch (const std::exception&)
|
catch (const std::exception&)
|
||||||
{
|
{
|
||||||
Console::Error::WriteLine("Unable to load park: %s", path.c_str());
|
Console::Error::WriteLine("Unable to load park: %s", path.c_str());
|
||||||
|
GetContext()->CloseProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset viewport rendering inhibition
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
|
||||||
|
|
||||||
gLoadKeepWindowsOpen = false;
|
gLoadKeepWindowsOpen = false;
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -334,26 +366,40 @@ namespace OpenRCT2::Title
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Inhibit viewport rendering while we're loading
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
|
||||||
|
|
||||||
|
ReportProgress(0);
|
||||||
bool isScenario = ParkImporter::ExtensionIsScenario(hintPath);
|
bool isScenario = ParkImporter::ExtensionIsScenario(hintPath);
|
||||||
auto parkImporter = ParkImporter::Create(hintPath);
|
auto parkImporter = ParkImporter::Create(hintPath);
|
||||||
|
|
||||||
auto result = parkImporter->LoadFromStream(stream, isScenario);
|
auto result = parkImporter->LoadFromStream(stream, isScenario);
|
||||||
|
ReportProgress(30);
|
||||||
|
|
||||||
auto& objectManager = GetContext()->GetObjectManager();
|
auto& objectManager = GetContext()->GetObjectManager();
|
||||||
objectManager.LoadObjects(result.RequiredObjects);
|
objectManager.LoadObjects(result.RequiredObjects, true);
|
||||||
|
ReportProgress(70);
|
||||||
|
|
||||||
// TODO: Have a separate GameState and exchange once loaded.
|
// TODO: Have a separate GameState and exchange once loaded.
|
||||||
auto& gameState = GetGameState();
|
auto& gameState = GetGameState();
|
||||||
|
|
||||||
parkImporter->Import(gameState);
|
parkImporter->Import(gameState);
|
||||||
|
ReportProgress(100);
|
||||||
|
|
||||||
MapAnimationAutoCreate();
|
MapAnimationAutoCreate();
|
||||||
}
|
}
|
||||||
PrepareParkForPlayback();
|
PrepareParkForPlayback();
|
||||||
|
_initialLoadCommand = false;
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
catch (const std::exception&)
|
catch (const std::exception&)
|
||||||
{
|
{
|
||||||
Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str());
|
Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str());
|
||||||
|
GetContext()->CloseProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset viewport rendering inhibition
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
|
||||||
|
|
||||||
gLoadKeepWindowsOpen = false;
|
gLoadKeepWindowsOpen = false;
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,6 @@ static Widget _mainWidgets[] = {
|
||||||
|
|
||||||
void OnDraw(DrawPixelInfo& dpi) override
|
void OnDraw(DrawPixelInfo& dpi) override
|
||||||
{
|
{
|
||||||
// Skip viewport render during preloader
|
|
||||||
if (GetContext()->GetActiveScene() == GetContext()->GetPreloaderScene())
|
|
||||||
return;
|
|
||||||
|
|
||||||
ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } });
|
ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -656,13 +656,24 @@ namespace OpenRCT2
|
||||||
ContextOpenIntent(&intent);
|
ContextOpenIntent(&intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetProgress(uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE) override
|
void SetProgress(
|
||||||
|
uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE, bool forceDraw = false) override
|
||||||
{
|
{
|
||||||
auto intent = Intent(INTENT_ACTION_PROGRESS_SET);
|
auto intent = Intent(INTENT_ACTION_PROGRESS_SET);
|
||||||
intent.PutExtra(INTENT_EXTRA_PROGRESS_OFFSET, currentProgress);
|
intent.PutExtra(INTENT_EXTRA_PROGRESS_OFFSET, currentProgress);
|
||||||
intent.PutExtra(INTENT_EXTRA_PROGRESS_TOTAL, totalCount);
|
intent.PutExtra(INTENT_EXTRA_PROGRESS_TOTAL, totalCount);
|
||||||
intent.PutExtra(INTENT_EXTRA_STRING_ID, format);
|
intent.PutExtra(INTENT_EXTRA_STRING_ID, format);
|
||||||
ContextOpenIntent(&intent);
|
ContextOpenIntent(&intent);
|
||||||
|
|
||||||
|
// Ideally, we'd force a redraw at all times at this point. OpenGL has to be directed
|
||||||
|
// from the main thread, though, so this cannot be invoked when off main thread.
|
||||||
|
// It's fine (and indeed useful!) for synchronous calls, so we keep it as an option.
|
||||||
|
if (!gOpenRCT2Headless && forceDraw)
|
||||||
|
{
|
||||||
|
_uiContext->ProcessMessages();
|
||||||
|
WindowInvalidateByClass(WindowClass::ProgressWindow);
|
||||||
|
Draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseProgress() override
|
void CloseProgress() override
|
||||||
|
@ -755,18 +766,30 @@ namespace OpenRCT2
|
||||||
parkImporter = ParkImporter::CreateS6(*_objectRepository);
|
parkImporter = ParkImporter::CreateS6(*_objectRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inhibit viewport rendering while we're loading
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
|
||||||
|
|
||||||
|
OpenProgress(asScenario ? STR_LOADING_SCENARIO : STR_LOADING_SAVED_GAME);
|
||||||
|
SetProgress(0, 100, STR_STRING_M_PERCENT, true);
|
||||||
|
|
||||||
auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
|
auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
|
||||||
|
SetProgress(10, 100, STR_STRING_M_PERCENT, true);
|
||||||
|
|
||||||
// From this point onwards the currently loaded park will be corrupted if loading fails
|
// From this point onwards the currently loaded park will be corrupted if loading fails
|
||||||
// so reload the title screen if that happens.
|
// so reload the title screen if that happens.
|
||||||
loadTitleScreenFirstOnFail = true;
|
loadTitleScreenFirstOnFail = true;
|
||||||
|
|
||||||
GameUnloadScripts();
|
GameUnloadScripts();
|
||||||
_objectManager->LoadObjects(result.RequiredObjects);
|
_objectManager->LoadObjects(result.RequiredObjects, true);
|
||||||
|
SetProgress(90, 100, STR_STRING_M_PERCENT, true);
|
||||||
|
|
||||||
// TODO: Have a separate GameState and exchange once loaded.
|
// TODO: Have a separate GameState and exchange once loaded.
|
||||||
auto& gameState = ::GetGameState();
|
auto& gameState = ::GetGameState();
|
||||||
parkImporter->Import(gameState);
|
parkImporter->Import(gameState);
|
||||||
|
SetProgress(100, 100, STR_STRING_M_PERCENT, true);
|
||||||
|
|
||||||
|
// Reset viewport rendering inhibition
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
|
||||||
|
|
||||||
gScenarioSavePath = path;
|
gScenarioSavePath = path;
|
||||||
gCurrentLoadedPath = path;
|
gCurrentLoadedPath = path;
|
||||||
|
@ -841,6 +864,7 @@ namespace OpenRCT2
|
||||||
windowManager->ShowError(STR_PARK_USES_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter());
|
windowManager->ShowError(STR_PARK_USES_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloseProgress();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (const ObjectLoadException& e)
|
catch (const ObjectLoadException& e)
|
||||||
|
@ -916,6 +940,8 @@ namespace OpenRCT2
|
||||||
Console::Error::WriteLine(e.what());
|
Console::Error::WriteLine(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloseProgress();
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,9 @@ namespace OpenRCT2
|
||||||
virtual void DisposeDrawingEngine() = 0;
|
virtual void DisposeDrawingEngine() = 0;
|
||||||
|
|
||||||
virtual void OpenProgress(StringId captionStringId) = 0;
|
virtual void OpenProgress(StringId captionStringId) = 0;
|
||||||
virtual void SetProgress(uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE) = 0;
|
virtual void SetProgress(
|
||||||
|
uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE, bool forceDraw = false)
|
||||||
|
= 0;
|
||||||
virtual void CloseProgress() = 0;
|
virtual void CloseProgress() = 0;
|
||||||
|
|
||||||
virtual bool LoadParkFromFile(const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) = 0;
|
virtual bool LoadParkFromFile(const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) = 0;
|
||||||
|
|
|
@ -927,6 +927,9 @@ void ViewportRotateAll(int32_t direction)
|
||||||
*/
|
*/
|
||||||
void ViewportRender(DrawPixelInfo& dpi, const Viewport* viewport, const ScreenRect& screenRect)
|
void ViewportRender(DrawPixelInfo& dpi, const Viewport* viewport, const ScreenRect& screenRect)
|
||||||
{
|
{
|
||||||
|
if (viewport->flags & VIEWPORT_FLAG_RENDERING_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
auto [topLeft, bottomRight] = screenRect;
|
auto [topLeft, bottomRight] = screenRect;
|
||||||
|
|
||||||
if (bottomRight.x <= viewport->pos.x)
|
if (bottomRight.x <= viewport->pos.x)
|
||||||
|
@ -1018,6 +1021,9 @@ static void ViewportPaint(const Viewport* viewport, DrawPixelInfo& dpi, const Sc
|
||||||
PROFILED_FUNCTION();
|
PROFILED_FUNCTION();
|
||||||
|
|
||||||
const uint32_t viewFlags = viewport->flags;
|
const uint32_t viewFlags = viewport->flags;
|
||||||
|
if (viewFlags & VIEWPORT_FLAG_RENDERING_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
uint32_t width = screenRect.GetWidth();
|
uint32_t width = screenRect.GetWidth();
|
||||||
uint32_t height = screenRect.GetHeight();
|
uint32_t height = screenRect.GetHeight();
|
||||||
const uint32_t bitmask = viewport->zoom >= ZoomLevel{ 0 } ? 0xFFFFFFFF & (viewport->zoom.ApplyTo(0xFFFFFFFF)) : 0xFFFFFFFF;
|
const uint32_t bitmask = viewport->zoom >= ZoomLevel{ 0 } ? 0xFFFFFFFF & (viewport->zoom.ApplyTo(0xFFFFFFFF)) : 0xFFFFFFFF;
|
||||||
|
|
|
@ -66,6 +66,7 @@ enum : uint32_t
|
||||||
VIEWPORT_FLAG_INVISIBLE_SUPPORTS = (1u << 29),
|
VIEWPORT_FLAG_INVISIBLE_SUPPORTS = (1u << 29),
|
||||||
|
|
||||||
VIEWPORT_FLAG_INDEPEDENT_ROTATION = (1u << 30),
|
VIEWPORT_FLAG_INDEPEDENT_ROTATION = (1u << 30),
|
||||||
|
VIEWPORT_FLAG_RENDERING_INHIBITED = (1u << 31),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class VisibilityKind
|
enum class VisibilityKind
|
||||||
|
|
|
@ -101,6 +101,19 @@ void WindowVisitEach(std::function<void(WindowBase*)> func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowSetFlagForAllViewports(uint32_t viewportFlag, bool enabled)
|
||||||
|
{
|
||||||
|
WindowVisitEach([&](WindowBase* w) {
|
||||||
|
if (w->viewport != nullptr)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
w->viewport->flags |= viewportFlag;
|
||||||
|
else
|
||||||
|
w->viewport->flags &= ~viewportFlag;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* rct2: 0x006ED7B0
|
* rct2: 0x006ED7B0
|
||||||
|
|
|
@ -491,6 +491,8 @@ extern bool gDisableErrorWindowSound;
|
||||||
std::list<std::shared_ptr<WindowBase>>::iterator WindowGetIterator(const WindowBase* w);
|
std::list<std::shared_ptr<WindowBase>>::iterator WindowGetIterator(const WindowBase* w);
|
||||||
void WindowVisitEach(std::function<void(WindowBase*)> func);
|
void WindowVisitEach(std::function<void(WindowBase*)> func);
|
||||||
|
|
||||||
|
void WindowSetFlagForAllViewports(uint32_t viewportFlag, bool enabled);
|
||||||
|
|
||||||
void WindowDispatchUpdateAll();
|
void WindowDispatchUpdateAll();
|
||||||
void WindowUpdateAllViewports();
|
void WindowUpdateAllViewports();
|
||||||
void WindowUpdateAll();
|
void WindowUpdateAll();
|
||||||
|
|
|
@ -1685,6 +1685,9 @@ enum : StringId
|
||||||
STR_STRING_M_OF_N_KIB = 6643,
|
STR_STRING_M_OF_N_KIB = 6643,
|
||||||
|
|
||||||
STR_LOADING_PLUGIN_ENGINE = 6648,
|
STR_LOADING_PLUGIN_ENGINE = 6648,
|
||||||
|
STR_LOADING_SCENARIO = 6649,
|
||||||
|
STR_LOADING_SAVED_GAME = 6650,
|
||||||
|
STR_STRING_M_PERCENT = 6651,
|
||||||
|
|
||||||
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
|
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
|
||||||
/* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings
|
/* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "../ParkImporter.h"
|
#include "../ParkImporter.h"
|
||||||
#include "../audio/audio.h"
|
#include "../audio/audio.h"
|
||||||
#include "../core/Console.hpp"
|
#include "../core/Console.hpp"
|
||||||
|
#include "../core/JobPool.h"
|
||||||
#include "../core/Memory.hpp"
|
#include "../core/Memory.hpp"
|
||||||
#include "../localisation/StringIds.h"
|
#include "../localisation/StringIds.h"
|
||||||
#include "../ride/Ride.h"
|
#include "../ride/Ride.h"
|
||||||
|
@ -38,6 +39,8 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace OpenRCT2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an object that is to be loaded or is loaded and ready
|
* Represents an object that is to be loaded or is loaded and ready
|
||||||
* to be placed in an object list.
|
* to be placed in an object list.
|
||||||
|
@ -183,13 +186,13 @@ public:
|
||||||
return RepositoryItemToObject(ori, slot);
|
return RepositoryItemToObject(ori, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadObjects(const ObjectList& objectList) override
|
void LoadObjects(const ObjectList& objectList, const bool reportProgress) override
|
||||||
{
|
{
|
||||||
// Find all the required objects
|
// Find all the required objects
|
||||||
auto requiredObjects = GetRequiredObjects(objectList);
|
auto requiredObjects = GetRequiredObjects(objectList);
|
||||||
|
|
||||||
// Load the required objects
|
// Load the required objects
|
||||||
LoadObjects(requiredObjects);
|
LoadObjects(requiredObjects, reportProgress);
|
||||||
|
|
||||||
// Update indices.
|
// Update indices.
|
||||||
UpdateSceneryGroupIndexes();
|
UpdateSceneryGroupIndexes();
|
||||||
|
@ -556,31 +559,17 @@ private:
|
||||||
return requiredObjects;
|
return requiredObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename TFunc> static void ParallelFor(const std::vector<T>& items, TFunc func)
|
void ReportProgress(size_t numLoaded, size_t numRequired)
|
||||||
{
|
{
|
||||||
auto partitions = std::thread::hardware_concurrency();
|
constexpr auto kObjectLoadMinProgress = 10;
|
||||||
auto partitionSize = (items.size() + (partitions - 1)) / partitions;
|
constexpr auto kObjectLoadMaxProgress = 90;
|
||||||
std::vector<std::thread> threads;
|
constexpr auto kObjectLoadProgressRange = kObjectLoadMaxProgress - kObjectLoadMinProgress;
|
||||||
for (size_t n = 0; n < partitions; n++)
|
|
||||||
{
|
const auto currentProgress = kObjectLoadMinProgress + (numLoaded * kObjectLoadProgressRange / numRequired);
|
||||||
auto begin = n * partitionSize;
|
OpenRCT2::GetContext()->SetProgress(static_cast<uint32_t>(currentProgress), 100, STR_STRING_M_PERCENT, true);
|
||||||
auto end = std::min(items.size(), begin + partitionSize);
|
|
||||||
threads.emplace_back(
|
|
||||||
[func](size_t pbegin, size_t pend) {
|
|
||||||
for (size_t i = pbegin; i < pend; i++)
|
|
||||||
{
|
|
||||||
func(i);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
begin, end);
|
|
||||||
}
|
|
||||||
for (auto& t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadObjects(std::vector<ObjectToLoad>& requiredObjects)
|
void LoadObjects(std::vector<ObjectToLoad>& requiredObjects, bool reportProgress)
|
||||||
{
|
{
|
||||||
std::vector<Object*> objects;
|
std::vector<Object*> objects;
|
||||||
std::vector<Object*> newLoadedObjects;
|
std::vector<Object*> newLoadedObjects;
|
||||||
|
@ -607,11 +596,11 @@ private:
|
||||||
std::sort(objectsToLoad.begin(), objectsToLoad.end());
|
std::sort(objectsToLoad.begin(), objectsToLoad.end());
|
||||||
objectsToLoad.erase(std::unique(objectsToLoad.begin(), objectsToLoad.end()), objectsToLoad.end());
|
objectsToLoad.erase(std::unique(objectsToLoad.begin(), objectsToLoad.end()), objectsToLoad.end());
|
||||||
|
|
||||||
// Load the objects.
|
// Prepare for loading objects multi-threaded
|
||||||
|
auto numProcessed = 0;
|
||||||
|
auto numRequired = objectsToLoad.size();
|
||||||
std::mutex commonMutex;
|
std::mutex commonMutex;
|
||||||
ParallelFor(objectsToLoad, [&](size_t i) {
|
auto loadSingleObject = [&](const ObjectRepositoryItem* requiredObject) {
|
||||||
const auto* requiredObject = objectsToLoad[i];
|
|
||||||
|
|
||||||
// Object requires to be loaded, if the object successfully loads it will register it
|
// Object requires to be loaded, if the object successfully loads it will register it
|
||||||
// as a loaded object otherwise placed into the badObjects list.
|
// as a loaded object otherwise placed into the badObjects list.
|
||||||
auto newObject = _objectRepository.LoadObject(requiredObject);
|
auto newObject = _objectRepository.LoadObject(requiredObject);
|
||||||
|
@ -628,7 +617,24 @@ private:
|
||||||
// Connect the ori to the registered object
|
// Connect the ori to the registered object
|
||||||
_objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject));
|
_objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
numProcessed++;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto completionFn = [&]() {
|
||||||
|
if (reportProgress && (numProcessed % 100) == 0)
|
||||||
|
ReportProgress(numProcessed, numRequired);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dispatch loading the objects
|
||||||
|
JobPool jobs{};
|
||||||
|
for (auto* object : objectsToLoad)
|
||||||
|
{
|
||||||
|
jobs.AddTask([object, &loadSingleObject]() { loadSingleObject(object); }, completionFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all jobs are fully completed
|
||||||
|
jobs.Join();
|
||||||
|
|
||||||
// Assign the loaded objects to the required objects
|
// Assign the loaded objects to the required objects
|
||||||
for (auto& requiredObject : requiredObjects)
|
for (auto& requiredObject : requiredObjects)
|
||||||
|
|
|
@ -36,7 +36,7 @@ struct IObjectManager
|
||||||
virtual Object* LoadObject(const RCTObjectEntry* entry) = 0;
|
virtual Object* LoadObject(const RCTObjectEntry* entry) = 0;
|
||||||
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) = 0;
|
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) = 0;
|
||||||
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) = 0;
|
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) = 0;
|
||||||
virtual void LoadObjects(const ObjectList& entries) = 0;
|
virtual void LoadObjects(const ObjectList& entries, const bool reportProgress = false) = 0;
|
||||||
virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) = 0;
|
virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) = 0;
|
||||||
virtual void UnloadAllTransient() = 0;
|
virtual void UnloadAllTransient() = 0;
|
||||||
virtual void UnloadAll() = 0;
|
virtual void UnloadAll() = 0;
|
||||||
|
|
|
@ -41,6 +41,7 @@ void PreloaderScene::Load()
|
||||||
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
|
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
|
||||||
ViewportInitAll();
|
ViewportInitAll();
|
||||||
ContextOpenWindow(WindowClass::MainWindow);
|
ContextOpenWindow(WindowClass::MainWindow);
|
||||||
|
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
|
||||||
WindowResizeGui(ContextGetWidth(), ContextGetHeight());
|
WindowResizeGui(ContextGetWidth(), ContextGetHeight());
|
||||||
|
|
||||||
// Reset screen
|
// Reset screen
|
||||||
|
|
|
@ -109,19 +109,10 @@ void TitleScene::Load()
|
||||||
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
|
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
|
||||||
ViewportInitAll();
|
ViewportInitAll();
|
||||||
ContextOpenWindow(WindowClass::MainWindow);
|
ContextOpenWindow(WindowClass::MainWindow);
|
||||||
CreateWindows();
|
|
||||||
|
|
||||||
GetContext().OpenProgress(STR_LOADING_TITLE_SEQUENCE);
|
|
||||||
|
|
||||||
TitleInitialise();
|
TitleInitialise();
|
||||||
OpenRCT2::Audio::PlayTitleMusic();
|
OpenRCT2::Audio::PlayTitleMusic();
|
||||||
|
|
||||||
if (gOpenRCT2ShowChangelog)
|
|
||||||
{
|
|
||||||
gOpenRCT2ShowChangelog = false;
|
|
||||||
ContextOpenWindow(WindowClass::Changelog);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sequencePlayer != nullptr)
|
if (_sequencePlayer != nullptr)
|
||||||
{
|
{
|
||||||
// Force the title sequence to load / update so we
|
// Force the title sequence to load / update so we
|
||||||
|
@ -131,7 +122,13 @@ void TitleScene::Load()
|
||||||
_sequencePlayer->Update();
|
_sequencePlayer->Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
GetContext().CloseProgress();
|
CreateWindows();
|
||||||
|
|
||||||
|
if (gOpenRCT2ShowChangelog)
|
||||||
|
{
|
||||||
|
gOpenRCT2ShowChangelog = false;
|
||||||
|
ContextOpenWindow(WindowClass::Changelog);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_VERBOSE("TitleScene::Load() finished");
|
LOG_VERBOSE("TitleScene::Load() finished");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue