Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Asset/assetmanager.h')
-rw-r--r--Source/Asset/assetmanager.h408
1 files changed, 408 insertions, 0 deletions
diff --git a/Source/Asset/assetmanager.h b/Source/Asset/assetmanager.h
new file mode 100644
index 00000000..b5390ecd
--- /dev/null
+++ b/Source/Asset/assetmanager.h
@@ -0,0 +1,408 @@
+//-----------------------------------------------------------------------------
+// Name: assetmanager.h
+// Developer: Wolfire Games LLC
+// Description:
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#pragma once
+
+#include <Asset/assetref.h>
+#include <Asset/assetmanager.h>
+#include <Asset/assetloaderbase.h>
+#include <Asset/assettypes.h>
+#include <Asset/assetmanagerthreadhandler.h>
+#include <Asset/assetloaderrors.h>
+
+#include <Internal/common.h>
+#include <Internal/datemodified.h>
+#include <Internal/error.h>
+
+#include <Logging/logdata.h>
+#include <Utility/timing.h>
+#include <Threading/sdl_wrapper.h>
+#include <Utility/simple_vector.h>
+#include <XML/Parsers/assetloadwarningparser.h>
+
+#include <list>
+#include <string>
+#include <limits>
+
+#define HOLD_LOAD_MASK_PRELOAD (1UL >> 0)
+
+enum AssetLoadingState {
+ ASSET_LOADING_UNLOADED,
+ ASSET_LOADING_LOADING,
+ ASSET_LOADING_LOADED
+};
+
+struct AssetInstanceCounter {
+ uint32_t id;
+ uint32_t hold_load_mask;
+ uint32_t load_flags;
+ uint16_t count;
+ int64_t modified;
+ uint32_t deallocate_attempts;
+ AssetType type;
+ const char* asset_type_name;
+ Asset* asset;
+ AssetLoadingState load_state;
+ char rel_name[kPathSize];
+ bool load_warning;
+};
+
+class AssetLoadedCallbackInterface {
+public:
+ virtual void AssetLoadedCallback( uint32_t loadid, AssetRefBase* ref ) = 0;
+ virtual void AssetFailedLoadCallback( uint32_t loadid, AssetRefBase* ref ) = 0;
+};
+
+struct LoadingInstance {
+ uint32_t load_id;
+ uint32_t asset_id;
+ AssetLoaderBase* loader;
+ AssetLoadedCallbackInterface* caller;
+ bool in_queue;
+ uint32_t next_step_id;
+ int load_step_result;
+ AssetRefBase* asset_ref;
+};
+
+#ifdef MEASURE_LOADS
+struct AssetLoadTime {
+ AssetLoadTime(uint64_t time, uint32_t time_ms, const char* asset_type_name, AssetType asset_type );
+
+ uint64_t time_ms;
+ uint64_t time;
+ const char* asset_type_name;
+ AssetType asset_type;
+};
+#endif
+
+size_t GetNextAssetListSize( size_t current_size );
+
+/*
+ * Manager for all assets, keeps track of all assets.
+ */
+class AssetManager {
+private:
+#ifdef MEASURE_LOADS
+ std::vector<AssetLoadTime> asset_load_times;
+#endif
+ bool load_warning_activated;
+ char load_warning_mode_name[32];
+ char load_warning_level_name[256];
+ AssetLoadWarningParser load_warning_instances;
+
+ uint32_t asset_id_counter;
+
+ AssetInstanceCounter* asset_list;
+ size_t asset_list_count;
+ size_t asset_list_size;
+
+ void ReallocAssetInstanceCounter( size_t new_size );
+
+ uint32_t load_id_counter;
+
+ SimpleVector<LoadingInstance> loading_instances;
+
+ AssetManagerThreadHandler io_thread_manager;
+ AssetManagerThreadHandler loading_thread_manager;
+
+ static const int MAX_ASSET_TYPE_COUNT = 32;
+ int asset_type_count[MAX_ASSET_TYPE_COUNT];
+
+ int asset_warning_count;
+ int asset_hold_count;
+
+ template <typename TAsset>
+ uint32_t AllocateAsset( const std::string& rel_path, uint32_t load_flags ) {
+ for( uint32_t id = 0; id < asset_list_count; id++ ) {
+ if(asset_list[id].type == TAsset::GetType() && asset_list[id].load_flags == load_flags && strcmp(asset_list[id].rel_name, rel_path.c_str()) == 0) {
+ return id;
+ }
+ }
+
+ if( asset_list_count == asset_list_size ) {
+ ReallocAssetInstanceCounter(GetNextAssetListSize(asset_list_size));
+ } else if( asset_list_count > asset_list_size ) {
+ LOGF << "Asset list count is larger than asset list size, this should never be possible" << std::endl;
+ }
+
+ bool has_warning = false;
+ if( load_warning_activated && TAsset::AssetWarning() ) {
+ has_warning = true;
+ //LOGW << "Loading asset: \"" << rel_path << "\" " << TAsset::GetType() << " " << TAsset::GetTypeName() << " while in load warning mode. State: " << load_warning_mode_name << std::endl;
+ load_warning_instances.AddAssetWarning(AssetLoadWarningParser::AssetWarning(rel_path, load_flags, TAsset::GetTypeName(), load_warning_level_name));
+ }
+
+ uint32_t asset_id = asset_id_counter++;
+ TAsset *asset = new TAsset(this,asset_id);
+
+ asset_list[asset_list_count].asset = asset;
+ asset_list[asset_list_count].hold_load_mask = 0x0;
+ asset_list[asset_list_count].load_flags = load_flags;
+ asset_list[asset_list_count].type = TAsset::GetType();
+ asset_list[asset_list_count].asset_type_name = TAsset::GetTypeName();
+ asset_list[asset_list_count].id = asset_id;
+ asset_list[asset_list_count].count = 0;
+ asset_list[asset_list_count].load_state = ASSET_LOADING_UNLOADED;
+ asset_list[asset_list_count].load_warning = has_warning;
+ strncpy(asset_list[asset_list_count].rel_name, rel_path.c_str(), kPathSize);
+
+ asset_type_count[(int)TAsset::GetType()]++;
+
+ if( asset_list[asset_list_count].load_warning ) {
+ asset_warning_count++;
+ }
+
+ return (uint32_t) asset_list_count++;
+ }
+
+ uint32_t GetAsset( uint32_t asset_id );
+
+ void DeallocateAsset( uint32_t asset_id, uint32_t starting_id );
+
+public:
+ //Increment reference count to asset, indicating there are more valid references in existance.
+ void IncrementAsset( uint32_t asset_id );
+
+ //Decrement reference count to asset, marking it for deletion during update if it reaches zero.
+ void DecrementAsset( uint32_t asset_id );
+
+ void Initialize();
+
+ bool UnloadUnreferenced( int limit, unsigned int required_attempts );
+
+ template<typename TAsset>
+ void Reload() {
+ LOGI << "Reloading " << TAsset::GetTypeName() << std::endl;
+ int64_t new_modified;
+ for( size_t i = 0; i < asset_list_count; i++ ) {
+ if( asset_list[i].type == TAsset::GetType() ) {
+ TAsset* asset = static_cast<TAsset*>(asset_list[i].asset);
+ Path file_path = FindFilePath(asset->path_, kDataPaths | kModPaths | kAbsPath, false);
+ new_modified = GetDateModifiedInt64(file_path.GetFullPath());
+ if(asset_list[i].modified != new_modified) {
+ asset_list[i].modified = new_modified;
+ asset->Reload();
+ }
+ }
+ }
+ }
+
+ void Dispose();
+ AssetManager();
+ ~AssetManager();
+
+ /*
+ * Request that an asset is to be loaded
+ *
+ * returns the globally unique id of the asset loaded.
+ * The hold load mask is set to the asset loaded, if this mask is set
+ * to anything else than zero the asset manager will not unload the asset until the mask
+ * has been release by calling ReleaseHoldLoadMask(uint32_t) including all the bits set with the original call (or more)
+ * If the asset exists since before and the mask was set to something, the masks will be bitwise OR'd together.
+ */
+
+ template<typename TAsset>
+ uint32_t LoadAssetAsynchronized( const std::string& instr, uint32_t load_flags, AssetLoadedCallbackInterface* callback, uint32_t hold_load_mask ) {
+ std::string str = SanitizePath(instr);
+
+ uint32_t asset_index = AllocateAsset<TAsset>(str, load_flags);
+ uint32_t load_index = loading_instances.Allocate();
+
+ loading_instances[load_index].load_id = load_id_counter++;
+ loading_instances[load_index].asset_id = asset_list[asset_index].id;
+
+ if(asset_list[asset_index].hold_load_mask == 0x0 && hold_load_mask != 0x0 ) {
+ asset_hold_count++;
+ }
+
+ asset_list[asset_index].hold_load_mask |= hold_load_mask;
+
+ if( asset_list[asset_index].load_state == ASSET_LOADING_LOADED
+ || asset_list[asset_index].load_state == ASSET_LOADING_LOADING ) {
+ loading_instances[load_index].in_queue = false;
+ loading_instances[load_index].next_step_id = std::numeric_limits<uint32_t>::max(); //If we loaded, we did all the steps.
+ loading_instances[load_index].loader = NULL;
+ loading_instances[load_index].caller = callback;
+ loading_instances[load_index].asset_ref = new AssetRef<TAsset>(asset_list[asset_index].asset);
+ loading_instances[load_index].load_step_result = kLoadOk;
+
+ } else if( asset_list[asset_index].load_state == ASSET_LOADING_UNLOADED ) {
+ asset_list[asset_index].load_state = ASSET_LOADING_LOADING;
+ loading_instances[load_index].in_queue = false;
+ loading_instances[load_index].next_step_id = 0;
+ loading_instances[load_index].loader = asset_list[asset_index].asset->NewLoader();
+ loading_instances[load_index].caller = callback;
+ loading_instances[load_index].asset_ref = new AssetRef<TAsset>(asset_list[asset_index].asset);
+ loading_instances[load_index].load_step_result = kLoadOk;
+
+ loading_instances[load_index].loader->Initialize(str,load_flags,asset_list[asset_index].asset);
+ } else {
+ LOGE << "unknown load state" << std::endl;
+ }
+
+ return loading_instances[load_index].load_id;
+ }
+
+ template<typename TAsset>
+ uint32_t LoadAssetAsynchronized( const std::string& str, AssetLoadedCallbackInterface* callback ) {
+ return LoadAssetAsynchronized<TAsset>(str, callback, 0x0, 0x0);
+ }
+
+ //Load flags are specifically used by texture loading.
+ template<typename TAsset>
+ AssetRef<TAsset> LoadSync( const std::string& instr, uint32_t load_flags, uint32_t hold_load_mask ) {
+ std::string str = SanitizePath(instr);
+
+ uint32_t internal_id = AllocateAsset<TAsset>(str, load_flags);
+ bool failed_load = false;
+
+ if(asset_list[internal_id].hold_load_mask == 0x0 && hold_load_mask != 0x0 ) {
+ asset_hold_count++;
+ }
+
+ asset_list[internal_id].hold_load_mask |= hold_load_mask;
+
+ TAsset * asset = static_cast<TAsset*>(asset_list[internal_id].asset);
+
+ if( asset_list[internal_id].load_state == ASSET_LOADING_LOADING ) {
+ char buffer[kPathSize];
+ strcpy(buffer, asset_list[internal_id].rel_name);
+ LOGI << "Trying to synchronously load an asset being asynchronously loaded"
+ ", will wait until it's finished." << std::endl;
+ while( asset_list[internal_id].load_state != ASSET_LOADING_LOADED ) {
+ Update();
+ }
+
+ // This was discovered when a SyncedAnimationGroup (A) required another SyncedAnimationGroup (B) which in turn required A
+ if(strcmp(buffer, asset_list[internal_id].rel_name) != 0) {
+ LOGW << buffer << " internal_id changed while loading it" << std::endl;
+ LOGW << "There is a possible circular include, make sure " << buffer << " doesn't require something that in turn requires " << buffer << std::endl;
+ failed_load = true;
+ }
+ }
+
+ if( !failed_load && asset_list[internal_id].load_state == ASSET_LOADING_UNLOADED ) {
+ asset_list[internal_id].load_state = ASSET_LOADING_LOADING;
+#ifdef MEASURE_LOADS
+ uint64_t start_load = getCPUTSC();
+ uint32_t start_load_ms = SDL_TS_GetTicks();
+#endif
+ AssetLoaderBase* loader = asset->NewLoader();
+ loader->Initialize(str, load_flags, asset);
+
+ ErrorResponse ret = _retry;
+ while( ret == _retry ) {
+ ret = _continue;
+ for( int i = 0; i < loader->GetAssetLoadStepCount(); i++ ) {
+ int err = loader->DoLoadStep(i);
+ if( err != kLoadOk ) {
+ {
+ LOGE << "Error loading asset type: "
+ << loader->GetTypeName()
+ << " from path: "
+ << asset_list[internal_id].rel_name
+ << ". Reason: "
+ << GetLoadErrorString(err)
+ << ", specifically: \""
+ << loader->GetLoadErrorString()
+ << loader->GetLoadErrorStringExtended()
+ << "\""
+ << std::endl;
+ }
+
+ ret = DisplayFormatError( _ok_cancel_retry,
+ true,
+ "Asset Load Error",
+ "Error loading asset type: %s from path: %s.\n\nReason: %s, specifically: \"%s\", \nSee log output for more details (if available)",
+ loader->GetTypeName(),
+ asset_list[internal_id].rel_name,
+ GetLoadErrorString(err),
+ loader->GetLoadErrorString()
+
+ );
+
+ if( ret == _continue ) {
+ failed_load = true;
+ }
+ break;
+ }
+ }
+ }
+
+ delete loader;
+#ifdef MEASURE_LOADS
+ asset_load_times.push_back(AssetLoadTime(getCPUTSC()-start_load, SDL_TS_GetTicks()-start_load_ms, TAsset::GetTypeName(), TAsset::GetType() ));
+#endif
+ asset_list[internal_id].modified = GetDateModifiedInt64(str.c_str());
+ asset_list[internal_id].load_state = ASSET_LOADING_LOADED;
+ } else if( asset_list[internal_id].load_state == ASSET_LOADING_LOADED ) {
+
+ } else {
+ LOGE << "Unknown load state for asset" << std::endl;
+ }
+
+ if( failed_load ) {
+ return AssetRef<TAsset>();
+ } else {
+ return AssetRef<TAsset>(asset);
+ }
+ }
+
+ template<typename TAsset>
+ AssetRef<TAsset> LoadSync( const std::string& str ) {
+ return LoadSync<TAsset>( str, 0x0, 0x0 );
+ }
+
+ void LoadSync( AssetType type, const std::string& str, uint32_t load_flags, uint32_t hold_load_mask );
+
+ /*
+ * Main thread update step
+ */
+ void Update();
+
+ void ReleaseAssetHoldLoad( uint32_t hold_load_mask );
+
+ //Set if we want the asset-loading warning outputs.
+ //This is a tool for determining if and what assets
+ //are being loaded after the loading screen and during gameplay, which shouldn't
+ //happen.
+ void SetLoadWarningMode(bool val, const char* mode_name, const char* level_name);
+ //Clear and dump load warnings to write directory .xml
+ void DumpLoadWarningData(const char* destination);
+
+ size_t GetLoadedAssetCount();
+ const char* GetAssetName(uint32_t index);
+ AssetType GetAssetType(uint32_t index);
+ uint32_t GetAssetHoldFlags(uint32_t index);
+ uint16_t GetAssetRefCount(uint32_t index);
+ bool GetAssetLoadWarning(uint32_t index);
+
+ int GetAssetTypeCount( AssetType asset );
+
+ int GetAssetWarningCount();
+ int GetAssetHoldCount();
+#ifdef MEASURE_LOADS
+ void DumpTimingData();
+#endif
+};
+