Merge branch 'main' into LoadPipelineFromGitHub
This commit is contained in:
@@ -1080,7 +1080,7 @@ void C3DEngine::WorldStreamUpdate()
|
||||
|
||||
if (GetCVars()->e_StreamSaveStartupResultsIntoXML)
|
||||
{
|
||||
const char* testResultsFile = "@cache@/TestResults/Streaming_Level_Start_Throughput.xml";
|
||||
const char* testResultsFile = "@usercache@/TestResults/Streaming_Level_Start_Throughput.xml";
|
||||
|
||||
AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
|
||||
if (resultsFile != AZ::IO::InvalidHandle)
|
||||
|
||||
@@ -952,7 +952,7 @@ void C3DEngine::ProcessStreamingLatencyTest(const CCamera& camIn, CCamera& camOu
|
||||
|
||||
if (GetCVars()->e_SQTestCount == 0)
|
||||
{
|
||||
const char* testResultsFile = "@cache@/TestResults/Streaming_Latency_Test.xml";
|
||||
const char* testResultsFile = "@usercache@/TestResults/Streaming_Latency_Test.xml";
|
||||
|
||||
AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
|
||||
if (resultsFile != AZ::IO::InvalidHandle)
|
||||
|
||||
@@ -461,7 +461,7 @@ void C3DEngine::UnloadLevel()
|
||||
m_ptexIconLowMemoryUsage = nullptr;
|
||||
m_ptexIconHighMemoryUsage = nullptr;
|
||||
m_ptexIconEditorConnectedToConsole = nullptr;
|
||||
|
||||
|
||||
if (m_pOpticsManager && !gEnv->IsEditor())
|
||||
{
|
||||
m_pOpticsManager->Reset();
|
||||
@@ -656,7 +656,7 @@ bool C3DEngine::LoadLevel(const char* szFolderName, const char* szMissionName)
|
||||
// set default render parameters.
|
||||
// for some reason this is not done later???
|
||||
m_pSkyLightManager->UpdateRenderParams();
|
||||
}
|
||||
}
|
||||
|
||||
// Load LevelData.xml File.
|
||||
XmlNodeRef xmlLevelData = GetSystem()->LoadXmlFromFile(GetLevelFilePath(LEVEL_DATA_FILE));
|
||||
@@ -687,17 +687,7 @@ bool C3DEngine::LoadLevel(const char* szFolderName, const char* szMissionName)
|
||||
// preload level cgfs
|
||||
if (GetCVars()->e_StatObjPreload && !gEnv->IsEditor())
|
||||
{
|
||||
if (GetCVars()->e_StatObjPreload == 2)
|
||||
{
|
||||
GetSystem()->OutputLoadingTimeStats();
|
||||
}
|
||||
|
||||
m_pObjManager->PreloadLevelObjects();
|
||||
|
||||
if (GetCVars()->e_StatObjPreload == 2)
|
||||
{
|
||||
GetSystem()->OutputLoadingTimeStats();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<struct IStatObj*>* pStatObjTable = NULL;
|
||||
|
||||
@@ -317,7 +317,8 @@ int CLightEntity::UpdateGSMLightSourceDynamicShadowFrustum(int nDynamicLodCount,
|
||||
|
||||
if (bDoGSM)
|
||||
{
|
||||
Vec3 vSunDir = Get3DEngine()->GetSunDir().GetNormalized(); // todo: remove GetNormalized() once GetSunDir() returns the normalized value
|
||||
//Vec3 vSunDir = Get3DEngine()->GetSunDir().GetNormalized(); // todo: remove GetNormalized() once GetSunDir() returns the normalized value
|
||||
Vec3 vSunDir = Vec3(1.0f, 0.0f, 0.0f);
|
||||
Vec3 vCameraDirWithoutDepth = vCameraDir - vCameraDir.Dot(vSunDir) * vSunDir;
|
||||
|
||||
Vec3 vFocusPos = passInfo.GetCamera().GetPosition() + vCameraDirWithoutDepth * fGSMBoxSize;
|
||||
|
||||
@@ -117,10 +117,6 @@ CMatInfo::CMatInfo()
|
||||
|
||||
ZeroStruct(m_streamZoneInfo);
|
||||
|
||||
#ifdef TRACE_MATERIAL_LEAKS
|
||||
m_sLoadingCallstack = GetSystem()->GetLoadingProfilerCallstack();
|
||||
#endif
|
||||
|
||||
// Used to know when a .dccmtl file has been changed,
|
||||
// requiring the source material to be updated
|
||||
m_dccMaterialHash = 0;
|
||||
@@ -341,7 +337,7 @@ ISurfaceType* CMatInfo::GetSurfaceType()
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CMatInfo::SetSubMtlCount(int numSubMtl)
|
||||
{
|
||||
AUTO_LOCK(GetSubMaterialResizeLock());
|
||||
AUTO_LOCK(GetSubMaterialResizeLock());
|
||||
if (numSubMtl > 0)
|
||||
{
|
||||
m_Flags |= MTL_FLAG_MULTI_SUBMTL;
|
||||
@@ -1112,15 +1108,6 @@ void CMatInfo::SetTexelDensityDebug([[maybe_unused]] int mode)
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* CMatInfo::GetLoadingCallstack()
|
||||
{
|
||||
#ifdef TRACE_MATERIAL_LEAKS
|
||||
return m_sLoadingCallstack.c_str();
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMatInfo::PrecacheMaterial(const float _fEntDistance, IRenderMesh* pRenderMesh, bool bFullUpdate, bool bDrawNear)
|
||||
{
|
||||
// FUNCTION_PROFILER_3DENGINE;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <IMaterial.h>
|
||||
|
||||
#if !defined(CONSOLE)
|
||||
# define TRACE_MATERIAL_LEAKS
|
||||
# define SUPPORT_MATERIAL_EDITING
|
||||
#endif
|
||||
|
||||
@@ -203,7 +202,7 @@ public:
|
||||
bool SetGetMaterialParamFloat(const char* sParamName, float& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
|
||||
bool SetGetMaterialParamVec3(const char* sParamName, Vec3& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
|
||||
bool SetGetMaterialParamVec4(const char* sParamName, Vec4& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
|
||||
|
||||
|
||||
void SetDirty(bool dirty = true) override;
|
||||
bool IsDirty() const override;
|
||||
|
||||
@@ -264,11 +263,6 @@ public:
|
||||
bool IsForwardRenderingRequired();
|
||||
bool IsNearestCubemapRequired();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Debug routines
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual const char* GetLoadingCallstack(); // trace leaking materials by callstack
|
||||
|
||||
void DisableTextureStreaming() override;
|
||||
virtual void RequestTexturesLoading(const float fMipFactor);
|
||||
|
||||
@@ -286,13 +280,6 @@ public:
|
||||
void SetDccMaterialHash(uint32 hash) override { m_dccMaterialHash = hash; }
|
||||
|
||||
virtual CryCriticalSection& GetSubMaterialResizeLock();
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// for debug purposes
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#ifdef TRACE_MATERIAL_LEAKS
|
||||
string m_sLoadingCallstack;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class CMatMan;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(CONSOLE)
|
||||
# define TRACE_CGF_LEAKS
|
||||
# define SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
|
||||
#endif
|
||||
|
||||
@@ -337,13 +336,6 @@ public:
|
||||
std::vector<uint16> m_chunkBoneIds;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// for debug purposes
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#ifdef TRACE_CGF_LEAKS
|
||||
string m_sLoadingCallstack;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Sub objects.
|
||||
@@ -419,7 +411,7 @@ public:
|
||||
virtual unsigned int GetBreakableByGame() { return m_bBreakableByGame; };
|
||||
|
||||
//Note: This function checks both the children and root data
|
||||
//It should really be 'has any deformable objects'
|
||||
//It should really be 'has any deformable objects'
|
||||
//Should eventually be refactored as part of an eventual statobj refactor.
|
||||
virtual bool IsDeformable() override;
|
||||
|
||||
|
||||
@@ -56,10 +56,6 @@ CStatObj::CStatObj()
|
||||
m_fLodDistance = 0.0f;
|
||||
|
||||
Init();
|
||||
|
||||
#ifdef TRACE_CGF_LEAKS
|
||||
m_sLoadingCallstack = GetSystem()->GetLoadingProfilerCallstack();
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1064,7 +1064,7 @@ namespace Audio
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Interface methods
|
||||
virtual bool Initialize(const SSystemInitParams* initParams) = 0;
|
||||
virtual bool Initialize() = 0;
|
||||
virtual void Release() = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ struct IMaterial
|
||||
virtual void SetDirty(bool dirty = true) = 0;
|
||||
virtual bool IsDirty() const = 0;
|
||||
|
||||
//! Returns true if the material is the parent of a group of materials
|
||||
//! Returns true if the material is the parent of a group of materials
|
||||
virtual bool IsMaterialGroup() const = 0;
|
||||
|
||||
//! Returns true if the material is a single material belongs to a material group
|
||||
@@ -411,11 +411,6 @@ struct IMaterial
|
||||
// - 2, fast sketch mode.
|
||||
virtual void SetSketchMode(int mode) = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Debug routines
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual const char* GetLoadingCallstack() = 0; // trace leaking materials by callstack
|
||||
|
||||
// Sets FT_DONT_STREAM flag for all textures used by the material
|
||||
// If a stream is already in process, this will stop the stream and flush the device texture
|
||||
virtual void DisableTextureStreaming() = 0;
|
||||
|
||||
@@ -119,8 +119,6 @@ namespace AZ
|
||||
|
||||
class IResourceCompilerHelper;
|
||||
|
||||
class CBootProfilerRecord;
|
||||
|
||||
namespace Serialization {
|
||||
struct IArchiveHost;
|
||||
}
|
||||
@@ -629,15 +627,7 @@ struct SSystemInitParams
|
||||
void* hWnd; //
|
||||
void* hWndForInputSystem; // the HWND for the input devices, distinct from the hWnd, which the rendering system overrides anyways
|
||||
|
||||
char remoteIP[256];
|
||||
int remotePort;
|
||||
bool remoteFileIO;
|
||||
bool remoteResourceCompiler;
|
||||
bool connectToRemote;
|
||||
bool waitForConnection; // if true, wait for the remote connection to be established before proceeding to system init.
|
||||
char assetsPlatform[64]; // what flavor of assets to load. Corresponds to those in rc.ini and asset processor ini
|
||||
char gameFolderName[256]; // just the name. Not the full path.
|
||||
char branchToken[12]; // information written by the assetprocessor which help determine whether the game/editor are running from the same branch or not
|
||||
|
||||
ILog* pLog; // You can specify your own ILog to be used by System.
|
||||
ILogCallback* pLogCallback; // You can specify your own ILogCallback to be added on log creation (used by Editor).
|
||||
@@ -648,45 +638,6 @@ struct SSystemInitParams
|
||||
IOutputPrintSink* pPrintSync; // Print Sync which can be used to catch all output from engine
|
||||
char szSystemCmdLine[2048]; // Command line.
|
||||
|
||||
|
||||
// set some paths before you create the system.
|
||||
|
||||
// rootPath - (REQUIRED) folder containing root. Must contain system.cfg or bootstrap.cfg basically.
|
||||
// the remainder are optional and if specified should contain prefixes that can be prepended to any file to get to that location:
|
||||
// READ ONLY!
|
||||
char rootPath[256];
|
||||
char rootPathCache[256];
|
||||
|
||||
// assetsPath - (REQUIRED) - where you assets live. The engine config parser will default this to @root@/gamename
|
||||
// READ ONLY!
|
||||
char assetsPath[256];
|
||||
char assetsPathCache[256];
|
||||
|
||||
// userPath - (OPTIONAL) User path contains a folder for preferences persistent storage. May be persisted to the cloud (by things like IOS)
|
||||
// If not specified, this is assumed @root@/User/
|
||||
// WRITABLE
|
||||
char userPath[256];
|
||||
|
||||
// cachePath - (OPTIONAL) a temporary store that can be erased at any time and does not need to be persisted
|
||||
// on the cloud or anything like that. if not specified, this will be @user@/Cache
|
||||
// WRITABLE
|
||||
char cachePath[256];
|
||||
|
||||
// logPath - (OPTIONAL) a log path folder.
|
||||
// If not specified, it will be @cache@/Logs
|
||||
// WRITABLE
|
||||
char logPath[256];
|
||||
|
||||
// the game should never use these values instead, the game should be using crypak or fileio with aliases:
|
||||
// @root@ To get to the folder where system.cfg lives
|
||||
// @assets@ to get to the folder containing game assets (textures and such) - by default, this is @root@/Gamename/
|
||||
// @devroot@ to get to source files that are checked into source control (PC EDITOR ONLY!)
|
||||
// @engroot@ to get to path to the engine root folder
|
||||
// @user@ to access user store
|
||||
// @cache@ to access temporary cache
|
||||
// @log@ to access log file and other forensic storage
|
||||
char szBinariesDir[256];
|
||||
|
||||
bool bEditor; // When running in Editor mode.
|
||||
bool bPreview; // When running in Preview mode (Minimal initialization).
|
||||
bool bTestMode; // When running in Automated testing mode.
|
||||
@@ -730,26 +681,7 @@ struct SSystemInitParams
|
||||
hWnd = NULL;
|
||||
hWndForInputSystem = NULL;
|
||||
|
||||
memset(rootPath, 0, sizeof(rootPath));
|
||||
memset(rootPathCache, 0, sizeof(rootPathCache));
|
||||
memset(userPath, 0, sizeof(userPath));
|
||||
memset(assetsPath, 0, sizeof(assetsPath));
|
||||
memset(assetsPathCache, 0, sizeof(assetsPathCache));
|
||||
memset(cachePath, 0, sizeof(cachePath));
|
||||
memset(logPath, 0, sizeof(logPath));
|
||||
memset(gameFolderName, 0, sizeof(gameFolderName));
|
||||
memset(branchToken, 0, sizeof(branchToken));
|
||||
|
||||
memset(remoteIP, 0, sizeof(remoteIP));
|
||||
azstrcpy(remoteIP, sizeof(remoteIP), "127.0.0.1");
|
||||
memset(assetsPlatform, 0, sizeof(assetsPlatform));
|
||||
azstrcpy(assetsPlatform, sizeof(assetsPlatform), "pc");
|
||||
|
||||
remotePort = 45643;
|
||||
remoteFileIO = false;
|
||||
remoteResourceCompiler = false;
|
||||
connectToRemote = false;
|
||||
waitForConnection = false;
|
||||
|
||||
pLog = NULL;
|
||||
pLogCallback = NULL;
|
||||
@@ -762,7 +694,6 @@ struct SSystemInitParams
|
||||
pValidator = NULL;
|
||||
pPrintSync = NULL;
|
||||
memset(szSystemCmdLine, 0, sizeof(szSystemCmdLine));
|
||||
memset(szBinariesDir, 0, sizeof(szBinariesDir));
|
||||
|
||||
bEditor = false;
|
||||
bPreview = false;
|
||||
@@ -798,17 +729,6 @@ struct SSystemInitParams
|
||||
|
||||
pSharedEnvironment = nullptr;
|
||||
}
|
||||
|
||||
bool UseAssetCache() const
|
||||
{
|
||||
#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_MAC) || defined(AZ_PLATFORM_LINUX)
|
||||
char checkPath[AZ_MAX_PATH_LEN] = { 0 };
|
||||
azsnprintf(checkPath, AZ_MAX_PATH_LEN, "%s/engine.json", rootPathCache);
|
||||
return AZ::IO::SystemFile::Exists(checkPath);
|
||||
#else
|
||||
return false;
|
||||
#endif // defined(AZ_PLATFORM_WINDOWS) || AZ_TRAIT_OS_PLATFORM_APPLE
|
||||
}
|
||||
};
|
||||
|
||||
// Notes:
|
||||
@@ -1228,11 +1148,6 @@ struct ISystem
|
||||
// Gets number of CPUs
|
||||
virtual int GetLogicalCPUCount() = 0;
|
||||
|
||||
//! Get the 'kind' of assets you need to load - this describes the flavor of assets you are going to load
|
||||
//! based on the platform you're on - so for example, android on ES3 will be 'es3' but android on opengl might load PC assets or others...
|
||||
//! This is defined in bootstrap.cfg and is read-only during runtime.
|
||||
virtual const char* GetAssetsPlatform() const = 0;
|
||||
|
||||
// Summary:
|
||||
// Return the rendering driver name. GL or Metal
|
||||
virtual const char* GetRenderingDriverName() const = 0;
|
||||
@@ -1406,43 +1321,6 @@ struct ISystem
|
||||
// True if system running in Test mode.
|
||||
virtual bool IsTestMode() const = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Loading time/memory profiling
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Summary:
|
||||
// Starts function loading stats profiling.
|
||||
virtual struct SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName) = 0;
|
||||
|
||||
// Summary:
|
||||
// Ends function loading stats profiling.
|
||||
virtual void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler) = 0;
|
||||
|
||||
// Summary:
|
||||
// Starts function profiling with bootprofiler (session must be started).
|
||||
virtual CBootProfilerRecord* StartBootSectionProfiler(const char* name, const char* args) = 0;
|
||||
|
||||
// Summary:
|
||||
// Ends function profiling with bootprofiler.
|
||||
virtual void StopBootSectionProfiler(CBootProfilerRecord* record) = 0;
|
||||
|
||||
|
||||
// Summary:
|
||||
// Starts frame session
|
||||
virtual void StartBootProfilerSessionFrames(const char* pName) = 0;
|
||||
|
||||
// Summary:
|
||||
// Stops frame session
|
||||
virtual void StopBootProfilerSessionFrames() = 0;
|
||||
|
||||
// Summary:
|
||||
// Prints loading stats into log.
|
||||
virtual void OutputLoadingTimeStats() = 0;
|
||||
|
||||
// Summary:
|
||||
// Starts function loading stats profiling.
|
||||
virtual const char* GetLoadingProfilerCallstack() = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// File version.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1581,10 +1459,10 @@ struct ISystem
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Summary:
|
||||
// Enable/Disable drawing the console
|
||||
// Enable/Disable drawing the console
|
||||
virtual void SetConsoleDrawEnabled(bool enabled) = 0;
|
||||
|
||||
// Enable/Disable drawing the UI
|
||||
// Enable/Disable drawing the UI
|
||||
virtual void SetUIDrawEnabled(bool enabled) = 0;
|
||||
|
||||
// Summary:
|
||||
@@ -1772,79 +1650,19 @@ struct DiskOperationInfo
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
struct CLoadingTimeProfiler
|
||||
{
|
||||
CLoadingTimeProfiler(ISystem* pSystem, const char* szFuncName)
|
||||
: m_pSystem(pSystem)
|
||||
{
|
||||
m_pSystem = pSystem;
|
||||
m_pTimeContainer = m_pSystem->StartLoadingSectionProfiling(this, szFuncName);
|
||||
}
|
||||
|
||||
~CLoadingTimeProfiler()
|
||||
{
|
||||
m_pSystem->EndLoadingSectionProfiling(this);
|
||||
}
|
||||
|
||||
struct SLoadingTimeContainer* m_pTimeContainer;
|
||||
double m_fConstructorTime;
|
||||
double m_fConstructorMemUsage;
|
||||
|
||||
DiskOperationInfo m_constructorInfo;
|
||||
|
||||
ISystem* m_pSystem;
|
||||
};
|
||||
|
||||
class CSYSBootProfileBlock
|
||||
{
|
||||
ISystem* m_pSystem;
|
||||
CBootProfilerRecord* m_pRecord;
|
||||
public:
|
||||
CSYSBootProfileBlock(ISystem* pSystem, const char* name, const char* args = NULL)
|
||||
: m_pSystem(pSystem)
|
||||
{
|
||||
if (m_pSystem)
|
||||
{
|
||||
m_pRecord = m_pSystem->StartBootSectionProfiler(name, args);
|
||||
}
|
||||
}
|
||||
|
||||
~CSYSBootProfileBlock()
|
||||
{
|
||||
if (m_pSystem)
|
||||
{
|
||||
m_pSystem->StopBootSectionProfiler(m_pRecord);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef AZ_PROFILE_TELEMETRY
|
||||
#if defined(ENABLE_LOADING_PROFILER) && AZ_PROFILE_TELEMETRY
|
||||
|
||||
#define LOADING_TIME_PROFILE_SECTION AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzCore)
|
||||
#define LOADING_TIME_PROFILE_SECTION_ARGS(...) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, __VA_ARGS__)
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName) AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzCore, sectionName)
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, sectionName, args)
|
||||
|
||||
#else
|
||||
|
||||
#define LOADING_TIME_PROFILE_SECTION CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv && gEnv->pSystem ? gEnv->pSystem : nullptr, __FUNCTION__);
|
||||
#define LOADING_TIME_PROFILE_SECTION_ARGS(args) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, __FUNCTION__, args);
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, sectionName);
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, sectionName, args);
|
||||
|
||||
#endif // AZ_PROFILE_TELEMETRY
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, ...) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, sectionName, __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define LOADING_TIME_PROFILE_SECTION
|
||||
#define LOADING_TIME_PROFILE_SECTION_ARGS(args)
|
||||
#define LOADING_TIME_PROFILE_SECTION_ARGS(...)
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName)
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args)
|
||||
#define LOADING_TIME_PROFILE_SESSION_SECTION(sessionName)
|
||||
#define LOADING_TIME_PROFILE_SESSION_START(sessionName)
|
||||
#define LOADING_TIME_PROFILE_SESSION_STOP(sessionName)
|
||||
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, ...)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1865,7 +1683,7 @@ extern SC_API SSystemGlobalEnvironment* gEnv;
|
||||
inline ISystem* GetISystem()
|
||||
{
|
||||
// Some unit tests temporarily install and then uninstall ISystem* mocks.
|
||||
// It is generally okay for runtime and tool systems which call this function to cache the returned pointer,
|
||||
// It is generally okay for runtime and tool systems which call this function to cache the returned pointer,
|
||||
// because their lifetime is usually shorter than the lifetime of the ISystem* implementation.
|
||||
// It is NOT safe for this function to cache it as a static itself, though, as the static it would cache
|
||||
// it inside may outlive the the actual instance implementing ISystem* when unit tests are torn down and then restarted.
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/IO/SystemFile.h> // for max path len
|
||||
#include <ISystem.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <ISystem.h>
|
||||
|
||||
namespace MaterialUtils
|
||||
{
|
||||
//! UnifyMaterialName - given a non-unified material name, remove the extension, unify the slashes
|
||||
@@ -75,14 +77,11 @@ namespace MaterialUtils
|
||||
static char cachedGameName[AZ_MAX_PATH_LEN] = { 0 };
|
||||
if (!removals[removalSize - 1])
|
||||
{
|
||||
if ((gEnv) && (gEnv->pConsole))
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
if (!projectName.empty())
|
||||
{
|
||||
ICVar* pGameNameCVar = gEnv->pConsole->GetCVar("sys_game_folder");
|
||||
if (pGameNameCVar)
|
||||
{
|
||||
azstrcpy(cachedGameName, AZ_MAX_PATH_LEN, pGameNameCVar->GetString());
|
||||
azstrcat(cachedGameName, AZ_MAX_PATH_LEN, "/");
|
||||
}
|
||||
azstrcpy(cachedGameName, AZ_MAX_PATH_LEN, projectName.c_str());
|
||||
azstrcat(cachedGameName, AZ_MAX_PATH_LEN, "/");
|
||||
}
|
||||
|
||||
if (cachedGameName[0] == 0)
|
||||
|
||||
@@ -55,8 +55,6 @@ public:
|
||||
int());
|
||||
MOCK_METHOD0(GetLogicalCPUCount,
|
||||
int());
|
||||
MOCK_CONST_METHOD0(GetAssetsPlatform,
|
||||
const char*());
|
||||
MOCK_CONST_METHOD0(GetRenderingDriverName,
|
||||
const char*());
|
||||
MOCK_METHOD1(DumpMemoryUsageStatistics,
|
||||
@@ -206,22 +204,6 @@ public:
|
||||
bool());
|
||||
MOCK_METHOD3(SetFrameProfiler,
|
||||
void(bool on, bool display, char* prefix));
|
||||
MOCK_METHOD2(StartLoadingSectionProfiling,
|
||||
struct SLoadingTimeContainer*(CLoadingTimeProfiler * pProfiler, const char* szFuncName));
|
||||
MOCK_METHOD1(EndLoadingSectionProfiling,
|
||||
void(CLoadingTimeProfiler * pProfiler));
|
||||
MOCK_METHOD2(StartBootSectionProfiler,
|
||||
CBootProfilerRecord * (const char* name, const char* args));
|
||||
MOCK_METHOD1(StopBootSectionProfiler,
|
||||
void(CBootProfilerRecord * record));
|
||||
MOCK_METHOD1(StartBootProfilerSessionFrames,
|
||||
void(const char* pName));
|
||||
MOCK_METHOD0(StopBootProfilerSessionFrames,
|
||||
void());
|
||||
MOCK_METHOD0(OutputLoadingTimeStats,
|
||||
void());
|
||||
MOCK_METHOD0(GetLoadingProfilerCallstack,
|
||||
const char*());
|
||||
MOCK_METHOD0(GetFileVersion,
|
||||
const SFileVersion&());
|
||||
MOCK_METHOD0(GetProductVersion,
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISystem.h"
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
|
||||
// any of the following tags can be present in the bootstrap.cfg
|
||||
// you can also prefix it with a platform.
|
||||
// so for example, you can specify remote_ip alone to specify it for all platforms
|
||||
// or you could specify android_remote_ip to change it for android only.
|
||||
// the instructions are executed in the order that they appear, so you can set the default
|
||||
// by using the non-platform-specific version, and then later on in the file you
|
||||
// can override specific platforms.
|
||||
|
||||
#define CONFIG_KEY_FOR_REMOTEIP "remote_ip"
|
||||
#define CONFIG_KEY_FOR_REMOTEPORT "remote_port"
|
||||
#define CONFIG_KEY_FOR_GAMEFOLDER "sys_game_folder"
|
||||
#define CONFIG_KEY_FOR_REMOTEFILEIO "remote_filesystem"
|
||||
#define CONFIG_KEY_FOR_CONNECTTOREMOTE "connect_to_remote"
|
||||
#define CONFIG_KEY_WAIT_FOR_CONNECT "wait_for_connect"
|
||||
#define DEFAULT_GAMEDLL "EmptyTemplate"
|
||||
#define DEFAULT_GAMEFOLDER "EmptyTemplate"
|
||||
#define DEFAULT_REMOTEIP "127.0.0.1"
|
||||
#define DEFAULT_REMOTEPORT 45643
|
||||
#define CONFIG_KEY_FOR_ASSETS "assets"
|
||||
#define CONFIG_KEY_FOR_BRANCHTOKEN "assetProcessor_branch_token"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CEngineConfig
|
||||
{
|
||||
public:
|
||||
string m_gameFolder; // folder only ("MyGame")
|
||||
string m_assetPlatform; // what platform folder assets are from if more than one is available or using VFS ("pc" / "es3")
|
||||
bool m_connectToRemote;
|
||||
bool m_remoteFileIO;
|
||||
bool m_waitForConnect;
|
||||
string m_remoteIP;
|
||||
int m_remotePort;
|
||||
|
||||
string m_rootFolder; // The engine root folder
|
||||
string m_branchToken;
|
||||
|
||||
CEngineConfig([[maybe_unused]] const char** sourcePaths = nullptr, [[maybe_unused]] size_t numSearchPaths = 0, [[maybe_unused]] size_t numLevelsUp = 3)
|
||||
: m_gameFolder(DEFAULT_GAMEFOLDER)
|
||||
, m_connectToRemote(false)
|
||||
, m_remoteFileIO(false)
|
||||
, m_remotePort(DEFAULT_REMOTEPORT)
|
||||
, m_waitForConnect(false)
|
||||
, m_remoteIP(DEFAULT_REMOTEIP)
|
||||
{
|
||||
m_assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
|
||||
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString gameFolder;
|
||||
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, CONFIG_KEY_FOR_GAMEFOLDER);
|
||||
if (settingsRegistry->Get(gameFolder, gameFolderKey))
|
||||
{
|
||||
m_gameFolder.assign(gameFolder.c_str(), gameFolder.size());
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString engineRoot;
|
||||
if (settingsRegistry->Get(engineRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
m_rootFolder.assign(engineRoot.c_str(), engineRoot.size());
|
||||
}
|
||||
}
|
||||
|
||||
OnLoadSettings();
|
||||
}
|
||||
|
||||
void CopyToStartupParams(SSystemInitParams& startupParams) const
|
||||
{
|
||||
startupParams.remoteFileIO = m_remoteFileIO;
|
||||
startupParams.remotePort = m_remotePort;
|
||||
startupParams.connectToRemote = m_connectToRemote;
|
||||
startupParams.waitForConnection = m_waitForConnect;
|
||||
|
||||
azstrncpy(startupParams.remoteIP, sizeof(startupParams.remoteIP), m_remoteIP.c_str(), m_remoteIP.length() + 1); // +1 for the null terminator
|
||||
azstrncpy(startupParams.assetsPlatform, sizeof(startupParams.assetsPlatform), m_assetPlatform.c_str(), m_assetPlatform.length() + 1); // +1 for the null terminator
|
||||
azstrncpy(startupParams.rootPath, sizeof(startupParams.rootPath), m_rootFolder.c_str(), m_rootFolder.length() + 1); // +1 for the null terminator
|
||||
azstrncpy(startupParams.gameFolderName, sizeof(startupParams.gameFolderName), m_gameFolder.c_str(), m_gameFolder.length() + 1); // +1 for the null terminator
|
||||
azstrncpy(startupParams.branchToken, sizeof(startupParams.branchToken), m_branchToken.c_str(), m_branchToken.length() + 1); // +1 for the null terminator
|
||||
|
||||
// compute assets path based on game folder name
|
||||
string gameFolderLower(m_gameFolder);
|
||||
gameFolderLower.MakeLower();
|
||||
azsnprintf(startupParams.assetsPath, sizeof(startupParams.assetsPath), "%s/%s", startupParams.rootPath, gameFolderLower.c_str());
|
||||
|
||||
// compute where the cache should be located
|
||||
azsnprintf(startupParams.rootPathCache, sizeof(startupParams.rootPathCache), "%s/Cache/%s/%s", m_rootFolder.c_str(), m_gameFolder.c_str(), m_assetPlatform.c_str());
|
||||
azsnprintf(startupParams.assetsPathCache, sizeof(startupParams.assetsPathCache), "%s/%s", startupParams.rootPathCache, gameFolderLower.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void OnLoadSettings()
|
||||
{
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry == nullptr)
|
||||
{
|
||||
AZ_Warning("ParseEngineConfig", false, "Attempting to load configuration data while SettingsRegistry does not exist");
|
||||
return;
|
||||
|
||||
}
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsKeyPrefix = AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey;
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsValueString;
|
||||
AZ::s64 settingsValueInt{};
|
||||
|
||||
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEFILEIO))
|
||||
{
|
||||
m_remoteFileIO = settingsValueInt != 0;
|
||||
}
|
||||
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_WAIT_FOR_CONNECT))
|
||||
{
|
||||
m_waitForConnect = settingsValueInt != 0;
|
||||
}
|
||||
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_CONNECTTOREMOTE))
|
||||
{
|
||||
m_connectToRemote = settingsValueInt != 0;
|
||||
}
|
||||
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEPORT))
|
||||
{
|
||||
m_remotePort = aznumeric_cast<AZ::u16>(settingsValueInt);
|
||||
}
|
||||
if (settingsValueString = {};
|
||||
AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueString, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEIP))
|
||||
{
|
||||
m_remoteIP.assign(settingsValueString.c_str(), settingsValueString.size());
|
||||
}
|
||||
if (settingsValueString = {};
|
||||
AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueString, settingsKeyPrefix, CONFIG_KEY_FOR_ASSETS))
|
||||
{
|
||||
m_assetPlatform.assign(settingsValueString.c_str(), settingsValueString.size());
|
||||
}
|
||||
if (settingsValueString = {};
|
||||
settingsRegistry->Get(settingsValueString, settingsKeyPrefix + "/" + CONFIG_KEY_FOR_BRANCHTOKEN))
|
||||
{
|
||||
m_branchToken.assign(settingsValueString.c_str(), settingsValueString.size());
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
@@ -293,7 +293,7 @@ typedef uint32 vtx_idx;
|
||||
|
||||
#if defined(ENABLE_PROFILING_CODE)
|
||||
# define USE_DISK_PROFILER
|
||||
//# define ENABLE_LOADING_PROFILER // Not guaranteed to have enough slots for all the threads in the system
|
||||
# define ENABLE_LOADING_PROFILER // requires AZ_PROFILE_TELEMETRY to also be defined
|
||||
#endif
|
||||
|
||||
#if defined(SOFTCODE_ENABLED)
|
||||
|
||||
@@ -110,7 +110,6 @@ set(FILES
|
||||
AzDXGIFormat.h
|
||||
SFunctor.h
|
||||
FunctorBaseFunction.h
|
||||
ParseEngineConfig.h
|
||||
CustomMemoryHeap.h
|
||||
FunctorBaseMember.h
|
||||
stridedptr.h
|
||||
|
||||
@@ -1,630 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
#include "BootProfiler.h"
|
||||
#include "ThreadInfo.h"
|
||||
#include <stack>
|
||||
#include <AzFramework/IO/FileOperations.h>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
StaticInstance<CBootProfiler, AZStd::no_destruct<CBootProfiler>> gProfilerInstance;
|
||||
enum
|
||||
{
|
||||
eMAX_THREADS_TO_PROFILE = 128,
|
||||
eNUM_RECORDS_PER_POOL = 2048, // so, eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord) == mem consumed by pool item
|
||||
// sizeof(CProfileBlockTimes)==152,
|
||||
// poolmem = 304Kb for 1 pool per thread
|
||||
};
|
||||
}
|
||||
|
||||
int CBootProfiler::CV_sys_bp_frames = 0;
|
||||
float CBootProfiler::CV_sys_bp_time_threshold = 0;
|
||||
|
||||
class CProfileBlockTimes
|
||||
{
|
||||
protected:
|
||||
LARGE_INTEGER m_startTimeStamp;
|
||||
LARGE_INTEGER m_stopTimeStamp;
|
||||
LARGE_INTEGER m_freq;
|
||||
CProfileBlockTimes()
|
||||
{
|
||||
memset(&m_startTimeStamp, 0, sizeof(m_startTimeStamp));
|
||||
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
|
||||
memset(&m_freq, 0, sizeof(m_freq));
|
||||
}
|
||||
};
|
||||
|
||||
class CBootProfilerRecord
|
||||
{
|
||||
public:
|
||||
const char* m_label;
|
||||
LARGE_INTEGER m_startTimeStamp;
|
||||
LARGE_INTEGER m_stopTimeStamp;
|
||||
LARGE_INTEGER m_freq;
|
||||
|
||||
CBootProfilerRecord* m_pParent;
|
||||
typedef AZStd::vector<CBootProfilerRecord*> ChildVector;
|
||||
ChildVector m_Childs;
|
||||
|
||||
CryFixedStringT<256> m_args;
|
||||
|
||||
ILINE CBootProfilerRecord(const char* label, LARGE_INTEGER timestamp, LARGE_INTEGER freq, const char* args)
|
||||
: m_label(label)
|
||||
, m_startTimeStamp(timestamp)
|
||||
, m_freq(freq)
|
||||
, m_pParent(NULL)
|
||||
{
|
||||
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
|
||||
if (args)
|
||||
{
|
||||
m_args = args;
|
||||
}
|
||||
}
|
||||
|
||||
ILINE ~CBootProfilerRecord()
|
||||
{
|
||||
// childs are allocated via pool as well, the destructors of each child
|
||||
// is called explicitly, for the purpose of freeing memory occupied by
|
||||
// m_Child vector. Otherwise there will be a memory leak.
|
||||
ChildVector::iterator it = m_Childs.begin();
|
||||
while (it != m_Childs.end())
|
||||
{
|
||||
(*it)->~CBootProfilerRecord();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Print(AZ::IO::HandleType fileHandle, char* buf, size_t buf_size, size_t depth, LARGE_INTEGER stopTime, const char* threadName, const float timeThreshold)
|
||||
{
|
||||
if (m_stopTimeStamp.QuadPart == 0)
|
||||
{
|
||||
m_stopTimeStamp = stopTime;
|
||||
}
|
||||
|
||||
const float time = (float)(m_stopTimeStamp.QuadPart - m_startTimeStamp.QuadPart) * 1000.f / (float)m_freq.QuadPart;
|
||||
|
||||
if (timeThreshold > 0.0f && time < timeThreshold)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string tabs; //tabs(depth++, '\t')
|
||||
tabs.insert(0, depth++, '\t');
|
||||
|
||||
{
|
||||
string label = m_label;
|
||||
label.replace("&", "&");
|
||||
label.replace("<", "<");
|
||||
label.replace(">", ">");
|
||||
label.replace("\"", """);
|
||||
label.replace("'", "'");
|
||||
|
||||
if (m_args.size() > 0)
|
||||
{
|
||||
m_args.replace("&", "&");
|
||||
m_args.replace("<", "<");
|
||||
m_args.replace(">", ">");
|
||||
m_args.replace("\"", """);
|
||||
m_args.replace("'", "'");
|
||||
m_args.replace("%", "%");
|
||||
}
|
||||
|
||||
sprintf_s(buf, buf_size, "%s<block name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" args=\"%s\"> \n",
|
||||
tabs.c_str(), label.c_str(), time, m_startTimeStamp.QuadPart, m_stopTimeStamp.QuadPart, m_args.c_str());
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
}
|
||||
|
||||
const size_t childsSize = m_Childs.size();
|
||||
for (size_t i = 0; i < childsSize; ++i)
|
||||
{
|
||||
CBootProfilerRecord* record = m_Childs[i];
|
||||
assert(record);
|
||||
record->Print(fileHandle, buf, buf_size, depth, stopTime, threadName, timeThreshold);
|
||||
}
|
||||
|
||||
sprintf_s(buf, buf_size, "%s</block>\n", tabs.c_str());
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CProfileInfo
|
||||
{
|
||||
friend class CBootProfilerSession;
|
||||
private:
|
||||
CBootProfilerRecord* m_pRoot;
|
||||
CBootProfilerRecord* m_pCurrent;
|
||||
public:
|
||||
CProfileInfo()
|
||||
: m_pRoot(NULL)
|
||||
, m_pCurrent(NULL) {}
|
||||
};
|
||||
|
||||
|
||||
class CBootProfilerThreadsInterface
|
||||
{
|
||||
protected:
|
||||
CBootProfilerThreadsInterface()
|
||||
{
|
||||
memset(m_threadInfo, 0, sizeof(m_threadInfo));
|
||||
m_threadCounter = 0;
|
||||
}
|
||||
|
||||
unsigned int GetThreadIndexByID(unsigned int threadID);
|
||||
const char* GetThreadNameByIndex(unsigned int threadIndex);
|
||||
|
||||
int m_threadCounter;
|
||||
private:
|
||||
unsigned int m_threadInfo[eMAX_THREADS_TO_PROFILE]; //threadIDs
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ILINE unsigned int CBootProfilerThreadsInterface::GetThreadIndexByID(unsigned int threadID)
|
||||
{
|
||||
for (int i = 0; i < eMAX_THREADS_TO_PROFILE; ++i)
|
||||
{
|
||||
if (m_threadInfo[i] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (m_threadInfo[i] == threadID)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int counter = CryInterlockedIncrement(&m_threadCounter) - 1; //count to index
|
||||
m_threadInfo[counter] = threadID;
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
ILINE const char* CBootProfilerThreadsInterface::GetThreadNameByIndex(unsigned int threadIndex)
|
||||
{
|
||||
assert(threadIndex < m_threadCounter);
|
||||
|
||||
const char* threadName = CryThreadGetName(m_threadInfo[threadIndex]);
|
||||
return threadName;
|
||||
}
|
||||
|
||||
class CRecordPool
|
||||
{
|
||||
public:
|
||||
CRecordPool()
|
||||
: m_baseAddr(NULL)
|
||||
, m_allocCounter(0)
|
||||
, m_next(NULL)
|
||||
{
|
||||
m_baseAddr = (CBootProfilerRecord*)CryModuleMemalign(eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord), 16);
|
||||
}
|
||||
~CRecordPool()
|
||||
{
|
||||
CryModuleMemalignFree(m_baseAddr);
|
||||
delete m_next;
|
||||
}
|
||||
|
||||
ILINE CBootProfilerRecord* allocateRecord()
|
||||
{
|
||||
if (m_allocCounter < eNUM_RECORDS_PER_POOL)
|
||||
{
|
||||
CBootProfilerRecord* newRecord = m_baseAddr + m_allocCounter;
|
||||
++m_allocCounter;
|
||||
return newRecord;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ILINE void setNextPool(CRecordPool* pool) { m_next = pool; }
|
||||
|
||||
private:
|
||||
CBootProfilerRecord* m_baseAddr;
|
||||
uint32 m_allocCounter;
|
||||
|
||||
CRecordPool* m_next;
|
||||
};
|
||||
|
||||
class CBootProfilerSession
|
||||
: public CBootProfilerThreadsInterface
|
||||
, protected CProfileBlockTimes
|
||||
{
|
||||
public:
|
||||
CBootProfilerSession();
|
||||
~CBootProfilerSession();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
CBootProfilerRecord* StartBlock(const char* name, const char* args);
|
||||
void StopBlock(CBootProfilerRecord* record);
|
||||
|
||||
void CollectResults(const char* filename, const float timeThreshold);
|
||||
|
||||
private:
|
||||
string m_name;
|
||||
|
||||
CProfileInfo m_threadsProfileInfo[eMAX_THREADS_TO_PROFILE];
|
||||
CRecordPool* m_threadsRecordsPool[eMAX_THREADS_TO_PROFILE]; //head
|
||||
CRecordPool* m_threadsCurrentPools[eMAX_THREADS_TO_PROFILE]; //current
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CBootProfilerSession::CBootProfilerSession()
|
||||
{
|
||||
memset(m_threadsProfileInfo, 0, sizeof(m_threadsProfileInfo));
|
||||
|
||||
memset(m_threadsRecordsPool, 0, sizeof(m_threadsRecordsPool));
|
||||
memset(m_threadsCurrentPools, 0, sizeof(m_threadsCurrentPools));
|
||||
}
|
||||
|
||||
CBootProfilerSession::~CBootProfilerSession()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_threadCounter; ++i)
|
||||
{
|
||||
CProfileInfo& profile = m_threadsProfileInfo[i];
|
||||
|
||||
// Since m_pRoot is allocated using memory pool (line 296),
|
||||
// its destructor is called explicitly to free the memory of
|
||||
// m_Childs and each of its child.
|
||||
|
||||
if (profile.m_pRoot)
|
||||
{
|
||||
profile.m_pRoot->~CBootProfilerRecord();
|
||||
}
|
||||
delete m_threadsRecordsPool[i];
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfilerSession::Start()
|
||||
{
|
||||
LARGE_INTEGER time, freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&time);
|
||||
m_startTimeStamp = time;
|
||||
m_freq = freq;
|
||||
}
|
||||
|
||||
void CBootProfilerSession::Stop()
|
||||
{
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
m_stopTimeStamp = time;
|
||||
}
|
||||
|
||||
CBootProfilerRecord* CBootProfilerSession::StartBlock(const char* name, const char* args)
|
||||
{
|
||||
const unsigned int curThread = CryGetCurrentThreadId();
|
||||
const unsigned int threadIndex = GetThreadIndexByID(curThread);
|
||||
|
||||
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
|
||||
|
||||
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
|
||||
|
||||
CRecordPool* pool = m_threadsCurrentPools[threadIndex];
|
||||
|
||||
if (!profile.m_pRoot)
|
||||
{
|
||||
if (!pool)
|
||||
{
|
||||
pool = new CRecordPool;
|
||||
m_threadsRecordsPool[threadIndex] = pool;
|
||||
m_threadsCurrentPools[threadIndex] = pool;
|
||||
}
|
||||
|
||||
CBootProfilerRecord* rec = pool->allocateRecord();
|
||||
profile.m_pRoot = profile.m_pCurrent = new(rec)CBootProfilerRecord("root", m_startTimeStamp, m_freq, args);
|
||||
}
|
||||
|
||||
assert(pool);
|
||||
|
||||
LARGE_INTEGER time, freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&time);
|
||||
|
||||
CBootProfilerRecord* pParent = profile.m_pCurrent;
|
||||
assert(pParent);
|
||||
assert(profile.m_pRoot);
|
||||
|
||||
CBootProfilerRecord* rec = pool->allocateRecord();
|
||||
if (!rec)
|
||||
{
|
||||
//pool is full, create a new one
|
||||
pool = new CRecordPool;
|
||||
m_threadsCurrentPools[threadIndex]->setNextPool(pool);
|
||||
m_threadsCurrentPools[threadIndex] = pool;
|
||||
|
||||
rec = pool->allocateRecord();
|
||||
}
|
||||
|
||||
profile.m_pCurrent = new(rec)CBootProfilerRecord(name, time, freq, args);
|
||||
profile.m_pCurrent->m_pParent = pParent;
|
||||
pParent->m_Childs.push_back(profile.m_pCurrent);
|
||||
|
||||
return profile.m_pCurrent;
|
||||
}
|
||||
|
||||
void CBootProfilerSession::StopBlock(CBootProfilerRecord* record)
|
||||
{
|
||||
if (record)
|
||||
{
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
record->m_stopTimeStamp = time;
|
||||
|
||||
unsigned int curThread = CryGetCurrentThreadId();
|
||||
unsigned int threadIndex = GetThreadIndexByID(curThread);
|
||||
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
|
||||
|
||||
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
|
||||
profile.m_pCurrent = record->m_pParent;
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfilerSession::CollectResults(const char* filename, const float timeThreshold)
|
||||
{
|
||||
if (!gEnv || !gEnv->pCryPak)
|
||||
{
|
||||
AZ_Warning("BootProfiler", false, "CryPak not set - skipping CollectResults");
|
||||
return;
|
||||
}
|
||||
static const char* szTestResults = "@cache@\\TestResults";
|
||||
string filePath = string(szTestResults) + "\\" + "bp_" + filename + ".xml";
|
||||
char path[AZ::IO::IArchive::MaxPath] = "";
|
||||
gEnv->pCryPak->AdjustFileName(filePath.c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
gEnv->pCryPak->MakeDir(szTestResults);
|
||||
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
gEnv->pFileIO->Open(path, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, fileHandle);
|
||||
if (fileHandle == AZ::IO::InvalidHandle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
const unsigned int buf_size = sizeof(buf);
|
||||
|
||||
sprintf_s(buf, buf_size, "<root>\n");
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
|
||||
const size_t numThreads = m_threadCounter;
|
||||
for (size_t i = 0; i < numThreads; ++i)
|
||||
{
|
||||
CBootProfilerRecord* pRoot = m_threadsProfileInfo[i].m_pRoot;
|
||||
if (pRoot)
|
||||
{
|
||||
pRoot->m_stopTimeStamp = m_stopTimeStamp;
|
||||
|
||||
const char* threadName = GetThreadNameByIndex(i);
|
||||
if (!threadName)
|
||||
{
|
||||
threadName = "UNKNOWN";
|
||||
}
|
||||
|
||||
|
||||
const float time = (float)(pRoot->m_stopTimeStamp.QuadPart - pRoot->m_startTimeStamp.QuadPart) * 1000.f / (float)pRoot->m_freq.QuadPart;
|
||||
|
||||
sprintf_s(buf, buf_size, "\t<thread name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" > \n", threadName, time,
|
||||
pRoot->m_startTimeStamp.QuadPart, pRoot->m_stopTimeStamp.QuadPart);
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
|
||||
for (size_t recordIdx = 0; recordIdx < pRoot->m_Childs.size(); ++recordIdx)
|
||||
{
|
||||
CBootProfilerRecord* record = pRoot->m_Childs[recordIdx];
|
||||
assert(record);
|
||||
record->Print(fileHandle, buf, buf_size, 2, m_stopTimeStamp, threadName, timeThreshold);
|
||||
}
|
||||
|
||||
sprintf_s(buf, buf_size, "\t</thread>\n");
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
}
|
||||
}
|
||||
|
||||
sprintf_s(buf, buf_size, "</root>\n");
|
||||
AZ::IO::Print(fileHandle, buf);
|
||||
gEnv->pFileIO->Close(fileHandle);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CBootProfiler& CBootProfiler::GetInstance()
|
||||
{
|
||||
return gProfilerInstance;
|
||||
}
|
||||
|
||||
CBootProfiler::CBootProfiler()
|
||||
: m_pCurrentSession(NULL)
|
||||
, m_pFrameRecord(NULL)
|
||||
, m_levelLoadAdditionalFrames(0)
|
||||
{
|
||||
}
|
||||
|
||||
CBootProfiler::~CBootProfiler()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
for (TSessionMap::iterator it = m_sessions.begin(); it != m_sessions.end(); ++it)
|
||||
{
|
||||
CBootProfilerSession* session = it->second;
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
// start session
|
||||
void CBootProfiler::StartSession(const char* sessionName)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
|
||||
TSessionMap::const_iterator it = m_sessions.find(sessionName);
|
||||
if (it == m_sessions.end())
|
||||
{
|
||||
m_pCurrentSession = new CBootProfilerSession();
|
||||
m_sessions[sessionName] = m_pCurrentSession;
|
||||
m_pCurrentSession->Start();
|
||||
}
|
||||
}
|
||||
|
||||
// stop session
|
||||
void CBootProfiler::StopSession(const char* sessionName)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
if (m_pCurrentSession)
|
||||
{
|
||||
TSessionMap::iterator it = m_sessions.find(sessionName);
|
||||
if (it != m_sessions.end())
|
||||
{
|
||||
if (m_pCurrentSession == it->second)
|
||||
{
|
||||
CBootProfilerSession* session = m_pCurrentSession;
|
||||
m_pCurrentSession = NULL;
|
||||
|
||||
session->Stop();
|
||||
session->CollectResults(sessionName, CV_sys_bp_time_threshold);
|
||||
|
||||
delete session;
|
||||
}
|
||||
m_sessions.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBootProfilerRecord* CBootProfiler::StartBlock(const char* name, const char* args)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
if (m_pCurrentSession)
|
||||
{
|
||||
return m_pCurrentSession->StartBlock(name, args);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CBootProfiler::StopBlock(CBootProfilerRecord* record)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
if (m_pCurrentSession)
|
||||
{
|
||||
m_pCurrentSession->StopBlock(record);
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfiler::StartFrame(const char* name)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
if (CV_sys_bp_frames)
|
||||
{
|
||||
StartSession("frames");
|
||||
m_pFrameRecord = StartBlock(name, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfiler::StopFrame()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
|
||||
if (m_pCurrentSession && CV_sys_bp_frames)
|
||||
{
|
||||
StopBlock(m_pFrameRecord);
|
||||
m_pFrameRecord = NULL;
|
||||
|
||||
--CV_sys_bp_frames;
|
||||
if (0 == CV_sys_bp_frames)
|
||||
{
|
||||
StopSession("frames");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pCurrentSession && m_levelLoadAdditionalFrames)
|
||||
{
|
||||
--m_levelLoadAdditionalFrames;
|
||||
if (0 == m_levelLoadAdditionalFrames)
|
||||
{
|
||||
StopSession("level");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfiler::Init(ISystem* pSystem)
|
||||
{
|
||||
//REGISTER_CVAR(sys_BootProfiler, 1, VF_DEV_ONLY,
|
||||
// "Collect and output session statistics into TestResults/bp_(session_name).xml \n"
|
||||
// "0 = Disabled\n"
|
||||
// "1 = Enabled\n");
|
||||
|
||||
pSystem->GetISystemEventDispatcher()->RegisterListener(this);
|
||||
StartSession("boot");
|
||||
}
|
||||
|
||||
void CBootProfiler::RegisterCVars()
|
||||
{
|
||||
REGISTER_CVAR2("sys_bp_frames", &CV_sys_bp_frames, 0, VF_DEV_ONLY, "Starts frame profiling for specified number of frames using BootProfiler");
|
||||
REGISTER_CVAR2("sys_bp_time_threshold", &CV_sys_bp_time_threshold, 0.1f, VF_DEV_ONLY, "If greater than 0 don't write blocks that took less time (default 0.1 ms)");
|
||||
}
|
||||
|
||||
void CBootProfiler::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case ESYSTEM_EVENT_GAME_POST_INIT_DONE:
|
||||
{
|
||||
StopSession("boot");
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_START:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
|
||||
{
|
||||
StartSession("level");
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_END:
|
||||
{
|
||||
StopSession("level");
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
|
||||
{
|
||||
//level loading can be stopped here, or m_levelLoadAdditionalFrames can be used to prolong dump for this amount of frames
|
||||
//StopSession("level");
|
||||
m_levelLoadAdditionalFrames = 20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBootProfiler::SetFrameCount(int frameCount)
|
||||
{
|
||||
CV_sys_bp_frames = frameCount;
|
||||
}
|
||||
#endif
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
|
||||
#pragma once
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
|
||||
class CBootProfilerRecord;
|
||||
class CBootProfilerSession;
|
||||
|
||||
class CBootProfiler
|
||||
: public ISystemEventListener
|
||||
{
|
||||
friend class CBootProfileBLock;
|
||||
public:
|
||||
CBootProfiler();
|
||||
~CBootProfiler();
|
||||
|
||||
static CBootProfiler& GetInstance();
|
||||
|
||||
void Init(ISystem* pSystem);
|
||||
void RegisterCVars();
|
||||
|
||||
void StartSession(const char* sessionName);
|
||||
void StopSession(const char* sessionName);
|
||||
|
||||
CBootProfilerRecord* StartBlock(const char* name, const char* args);
|
||||
void StopBlock(CBootProfilerRecord* record);
|
||||
|
||||
void StartFrame(const char* name);
|
||||
void StopFrame();
|
||||
protected:
|
||||
// === ISystemEventListener
|
||||
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
|
||||
void SetFrameCount(int frameCount);
|
||||
|
||||
private:
|
||||
CBootProfilerSession* m_pCurrentSession;
|
||||
typedef AZStd::unordered_map<AZStd::string, CBootProfilerSession*> TSessionMap;
|
||||
TSessionMap m_sessions;
|
||||
|
||||
static int CV_sys_bp_frames;
|
||||
static float CV_sys_bp_time_threshold;
|
||||
CBootProfilerRecord* m_pFrameRecord;
|
||||
AZStd::recursive_mutex m_recordMutex;
|
||||
int m_levelLoadAdditionalFrames;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
|
||||
@@ -277,17 +277,6 @@ int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
|
||||
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
|
||||
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
|
||||
|
||||
if (CSystem* pSystem = (CSystem*)GetSystem())
|
||||
{
|
||||
if (const char* pLoadingProfilerCallstack = pSystem->GetLoadingProfilerCallstack())
|
||||
{
|
||||
if (pLoadingProfilerCallstack[0])
|
||||
{
|
||||
WriteLineToLog("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
IMemoryManager::SProcessMemInfo memInfo;
|
||||
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
|
||||
@@ -593,7 +582,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
|
||||
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
|
||||
{
|
||||
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
|
||||
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include "System.h"
|
||||
#include <AzFramework/IO/FileOperations.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
//#if !defined(LINUX)
|
||||
|
||||
#include <ISystem.h>
|
||||
@@ -186,21 +186,17 @@ AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
|
||||
}
|
||||
}
|
||||
|
||||
if (gEnv->pConsole)
|
||||
{
|
||||
if (ICVar* pCVarGameDir = gEnv->pConsole->GetCVar("sys_game_folder"))
|
||||
{
|
||||
sprintf(s, "GameDir: %s\n", pCVarGameDir->GetString());
|
||||
azstrcat(str, length, s);
|
||||
}
|
||||
}
|
||||
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
|
||||
azstrcat(str, length, "ProjectDir: ");
|
||||
azstrcat(str, length, projectPath.c_str());
|
||||
azstrcat(str, length, "\n");
|
||||
|
||||
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
|
||||
GetModuleFileNameA(NULL, s, sizeof(s));
|
||||
|
||||
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
|
||||
AZStd::string exeName;
|
||||
if (AzFramework::StringFunc::Path::GetFullFileName(s, exeName))
|
||||
if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
|
||||
{
|
||||
azstrcat(str, length, "Executable: ");
|
||||
azstrcat(str, length, exeName.c_str());
|
||||
|
||||
@@ -1,647 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
#include "System.h"
|
||||
#include "LoadingProfiler.h"
|
||||
|
||||
#define LOADING_TIME_CONTAINER_MAX_TEXT_SIZE 1024
|
||||
#define MAX_LOADING_TIME_PROFILER_STACK_DEPTH 16
|
||||
|
||||
//#define SAVE_SAVELEVELSTATS_IN_ROOT
|
||||
|
||||
struct SLoadingTimeContainer
|
||||
: public _i_reference_target_t
|
||||
{
|
||||
SLoadingTimeContainer() {}
|
||||
|
||||
SLoadingTimeContainer(SLoadingTimeContainer* pParent, const char* pPureFuncName, const int nRootIndex)
|
||||
{
|
||||
m_dSelfMemUsage = m_dTotalMemUsage = m_dSelfTime = m_dTotalTime = 0;
|
||||
m_nCounter = 1;
|
||||
m_pFuncName = pPureFuncName;
|
||||
m_pParent = pParent;
|
||||
m_nRootIndex = nRootIndex;
|
||||
}
|
||||
|
||||
static int Cmp_SLoadingTimeContainer_Time(const void* v1, const void* v2)
|
||||
{
|
||||
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
|
||||
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
|
||||
|
||||
if (pChunk1->m_dSelfTime > pChunk2->m_dSelfTime)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (pChunk1->m_dSelfTime < pChunk2->m_dSelfTime)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Cmp_SLoadingTimeContainer_MemUsage(const void* v1, const void* v2)
|
||||
{
|
||||
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
|
||||
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
|
||||
|
||||
if (pChunk1->m_dSelfMemUsage > pChunk2->m_dSelfMemUsage)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (pChunk1->m_dSelfMemUsage < pChunk2->m_dSelfMemUsage)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double GetUsedMemory(ISystem* pSysytem)
|
||||
{
|
||||
static IMemoryManager::SProcessMemInfo processMemInfo;
|
||||
pSysytem->GetIMemoryManager()->GetProcessMemInfo(processMemInfo);
|
||||
return double(processMemInfo.PagefileUsage) / double(1024 * 1024);
|
||||
}
|
||||
|
||||
|
||||
void Clear()
|
||||
{
|
||||
for (size_t i = 0, end = m_pChilds.size(); i < end; ++i)
|
||||
{
|
||||
delete m_pChilds[i];
|
||||
}
|
||||
}
|
||||
|
||||
~SLoadingTimeContainer()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
double m_dSelfTime, m_dTotalTime;
|
||||
double m_dSelfMemUsage, m_dTotalMemUsage;
|
||||
uint32 m_nCounter;
|
||||
|
||||
const char* m_pFuncName;
|
||||
SLoadingTimeContainer* m_pParent;
|
||||
int m_nRootIndex;
|
||||
std::vector<SLoadingTimeContainer*> m_pChilds;
|
||||
|
||||
DiskOperationInfo m_selfInfo;
|
||||
DiskOperationInfo m_totalInfo;
|
||||
bool m_bUsed;
|
||||
};
|
||||
|
||||
bool operator== (const SLoadingTimeContainer& a, const SLoadingTimeContainer& b)
|
||||
{
|
||||
return b.m_pFuncName == a.m_pFuncName;
|
||||
}
|
||||
|
||||
bool operator== (const SLoadingTimeContainer& a, const char* b)
|
||||
{
|
||||
return b == a.m_pFuncName;
|
||||
}
|
||||
|
||||
|
||||
SLoadingTimeContainer* CLoadingProfilerSystem::m_pCurrentLoadingTimeContainer = 0;
|
||||
SLoadingTimeContainer* CLoadingProfilerSystem::m_pRoot[2] = {0, 0};
|
||||
int CLoadingProfilerSystem::m_iActiveRoot = 0;
|
||||
ICVar* CLoadingProfilerSystem::m_pEnableProfile = 0;
|
||||
int CLoadingProfilerSystem::nLoadingProfileMode = 1;
|
||||
int CLoadingProfilerSystem::nLoadingProfilerNotTrackedAllocations = -1;
|
||||
CryCriticalSection CLoadingProfilerSystem::csLock;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CLoadingProfilerSystem::OutputLoadingTimeStats(ILog* pLog, int nMode)
|
||||
{
|
||||
nLoadingProfileMode = nMode;
|
||||
|
||||
PodArray<SLoadingTimeContainer> arrNoStack;
|
||||
CreateNoStackList(arrNoStack);
|
||||
|
||||
|
||||
if (nLoadingProfileMode > 0)
|
||||
{ // loading mem stats per func
|
||||
pLog->Log("------ Level loading memory allocations (MB) by function ------------");
|
||||
pLog->Log(" ||Self | Total | Calls | Function (%d MB lost)||", nLoadingProfilerNotTrackedAllocations);
|
||||
pLog->Log("---------------------------------------------------------------------");
|
||||
|
||||
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_MemUsage);
|
||||
|
||||
for (int i = 0; i < arrNoStack.Count(); i++)
|
||||
{
|
||||
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
|
||||
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
|
||||
pTimeContainer->m_dSelfMemUsage, pTimeContainer->m_dTotalMemUsage, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
|
||||
}
|
||||
|
||||
pLog->Log("---------------------------------------------------------------------");
|
||||
}
|
||||
|
||||
if (nLoadingProfileMode > 0)
|
||||
{ // loading time stats per func
|
||||
pLog->Log("----------- Level loading time (sec) by function --------------------");
|
||||
pLog->Log(" ||Self | Total | Calls | Function||");
|
||||
pLog->Log("---------------------------------------------------------------------");
|
||||
|
||||
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
|
||||
|
||||
for (int i = 0; i < arrNoStack.Count(); i++)
|
||||
{
|
||||
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
|
||||
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
|
||||
pTimeContainer->m_dSelfTime, pTimeContainer->m_dTotalTime, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
|
||||
}
|
||||
|
||||
if (nLoadingProfileMode == 1)
|
||||
{
|
||||
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
|
||||
}
|
||||
else
|
||||
{
|
||||
pLog->Log("---------------------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
if (nLoadingProfileMode > 0)
|
||||
{ // file info
|
||||
pLog->Log("----------------------------- Level file information by function --------------------------------");
|
||||
pLog->Log("|| Self | Total |Bandwith| Calls | Function||");
|
||||
pLog->Log("|| Seeks |FileOpen|FileRead| Seeks |FileOpen|FileRead| Kb/s | | ||");
|
||||
|
||||
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
|
||||
|
||||
for (int i = 0; i < arrNoStack.Count(); i++)
|
||||
{
|
||||
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
|
||||
double bandwidth = pTimeContainer->m_dSelfTime > 0 ? (pTimeContainer->m_selfInfo.m_dOperationSize / pTimeContainer->m_dSelfTime / 1024.0) : 0.;
|
||||
pLog->Log("|%6d | %6d | %6d |%6d | %6d | %6d | %6.1f | %6d | %s|",
|
||||
pTimeContainer->m_selfInfo.m_nSeeksCount, pTimeContainer->m_selfInfo.m_nFileOpenCount, pTimeContainer->m_selfInfo.m_nFileReadCount,
|
||||
pTimeContainer->m_totalInfo.m_nSeeksCount, pTimeContainer->m_totalInfo.m_nFileOpenCount, pTimeContainer->m_totalInfo.m_nFileReadCount,
|
||||
bandwidth, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
|
||||
}
|
||||
|
||||
if (nLoadingProfileMode == 1)
|
||||
{
|
||||
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
|
||||
}
|
||||
else
|
||||
{
|
||||
pLog->Log("---------------------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CSystemEventListner_LoadingProfiler
|
||||
: public ISystemEventListener
|
||||
{
|
||||
private:
|
||||
CLoadingTimeProfiler* m_pPrecacheProfiler;
|
||||
ESystemEvent lastEvent;
|
||||
public:
|
||||
CSystemEventListner_LoadingProfiler()
|
||||
: m_pPrecacheProfiler(NULL) {}
|
||||
|
||||
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
|
||||
{
|
||||
CLoadingProfilerSystem::Clean();
|
||||
if (m_pPrecacheProfiler == NULL)
|
||||
{
|
||||
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "ModeSwitch");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
|
||||
{
|
||||
SAFE_DELETE(m_pPrecacheProfiler);
|
||||
CLoadingProfilerSystem::SaveTimeContainersToFile(gEnv->bMultiplayer == true ? "mode_switch_mp.lmbrlp" : "mode_switch_sp.lmbrlp", 0.0, true);
|
||||
}
|
||||
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
|
||||
{
|
||||
CLoadingProfilerSystem::Clean();
|
||||
if (m_pPrecacheProfiler == NULL)
|
||||
{
|
||||
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "LevelLoading");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_END:
|
||||
{
|
||||
delete m_pPrecacheProfiler;
|
||||
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "Precache");
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
|
||||
{
|
||||
if (lastEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME)
|
||||
{
|
||||
SAFE_DELETE(m_pPrecacheProfiler);
|
||||
string levelName = "no_level";
|
||||
ICVar* sv_map = gEnv->pConsole->GetCVar("sv_map");
|
||||
if (sv_map)
|
||||
{
|
||||
levelName = sv_map->GetString();
|
||||
}
|
||||
|
||||
string levelNameFullProfile = levelName + "_LP.lmbrlp";
|
||||
string levelNameThreshold = levelName + "_LP_OneSec.lmbrlp";
|
||||
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameFullProfile.c_str(), 0.0, false);
|
||||
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameThreshold.c_str(), 1.0, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
|
||||
{
|
||||
// Ensure that the precache profiler is dead
|
||||
SAFE_DELETE(m_pPrecacheProfiler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (event != ESYSTEM_EVENT_RANDOM_SEED)
|
||||
{
|
||||
lastEvent = event;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CSystemEventListner_LoadingProfiler g_system_event_listener_loadingProfiler;
|
||||
|
||||
void CLoadingProfilerSystem::Init()
|
||||
{
|
||||
gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_loadingProfiler);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CLoadingProfilerSystem::ShutDown()
|
||||
{
|
||||
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetISystemEventDispatcher())
|
||||
{
|
||||
gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(&g_system_event_listener_loadingProfiler);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SLoadingTimeContainer* CLoadingProfilerSystem::StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName)
|
||||
{
|
||||
if (!nLoadingProfileMode || !gEnv->pConsole)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DWORD threadID = GetCurrentThreadId();
|
||||
|
||||
static DWORD dwMainThreadId = GetCurrentThreadId();
|
||||
if (threadID != dwMainThreadId)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!m_pEnableProfile)
|
||||
{
|
||||
if (gEnv->pConsole)
|
||||
{
|
||||
m_pEnableProfile = gEnv->pConsole->GetCVar("sys_ProfileLevelLoading");
|
||||
if (!m_pEnableProfile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pEnableProfile->GetIVal() <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//if (m_pCurrentLoadingTimeContainer == m_pRoot && strstr(szFuncName,"Open"))
|
||||
//{
|
||||
// pProfiler->m_constructorInfo.m_nFileOpenCount +=1;
|
||||
//}
|
||||
|
||||
CryAutoCriticalSection lock(csLock);
|
||||
|
||||
if (true /*pProfiler && pProfiler->m_pSystem*/)
|
||||
{
|
||||
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
|
||||
pProfiler->m_fConstructorTime = pTimer->GetAsyncTime().GetSeconds();
|
||||
pProfiler->m_fConstructorMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
|
||||
|
||||
DiskOperationInfo info;
|
||||
pProfiler->m_constructorInfo = info;
|
||||
|
||||
if (nLoadingProfilerNotTrackedAllocations < 0)
|
||||
{
|
||||
nLoadingProfilerNotTrackedAllocations = (int)pProfiler->m_fConstructorMemUsage;
|
||||
}
|
||||
}
|
||||
|
||||
SLoadingTimeContainer* pParent = m_pCurrentLoadingTimeContainer;
|
||||
if (!pParent)
|
||||
{
|
||||
pParent = m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = new SLoadingTimeContainer(0, "Root", m_iActiveRoot);
|
||||
}
|
||||
|
||||
for (size_t i = 0, end = m_pCurrentLoadingTimeContainer->m_pChilds.size(); i < end; ++i)
|
||||
{
|
||||
if (m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pFuncName == szFuncName)
|
||||
{
|
||||
assert(m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pParent == m_pCurrentLoadingTimeContainer);
|
||||
assert(!m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed);
|
||||
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed = true;
|
||||
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_nCounter++;
|
||||
m_pCurrentLoadingTimeContainer = m_pCurrentLoadingTimeContainer->m_pChilds[i];
|
||||
return m_pCurrentLoadingTimeContainer;
|
||||
}
|
||||
}
|
||||
|
||||
m_pCurrentLoadingTimeContainer = new SLoadingTimeContainer(pParent, szFuncName, pParent->m_nRootIndex);
|
||||
m_pCurrentLoadingTimeContainer->m_bUsed = true;
|
||||
{
|
||||
// Need to iterate from the end than
|
||||
pParent->m_pChilds.push_back(m_pCurrentLoadingTimeContainer);
|
||||
}
|
||||
|
||||
return m_pCurrentLoadingTimeContainer;
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler)
|
||||
{
|
||||
if (!nLoadingProfileMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static DWORD dwMainThreadId = GetCurrentThreadId();
|
||||
|
||||
if (GetCurrentThreadId() != dwMainThreadId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pProfiler->m_pTimeContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CryAutoCriticalSection lock(csLock);
|
||||
|
||||
if (true /*pProfiler && pProfiler->m_pSystem*/)
|
||||
{
|
||||
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
|
||||
double fSelfTime = pTimer->GetAsyncTime().GetSeconds() - pProfiler->m_fConstructorTime;
|
||||
double fMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
|
||||
double fSelfMemUsage = fMemUsage - pProfiler->m_fConstructorMemUsage;
|
||||
|
||||
|
||||
if (fSelfTime < 0.0)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
pProfiler->m_pTimeContainer->m_dSelfTime += fSelfTime;
|
||||
pProfiler->m_pTimeContainer->m_dTotalTime += fSelfTime;
|
||||
pProfiler->m_pTimeContainer->m_dSelfMemUsage += fSelfMemUsage;
|
||||
pProfiler->m_pTimeContainer->m_dTotalMemUsage += fSelfMemUsage;
|
||||
|
||||
DiskOperationInfo info;
|
||||
info -= pProfiler->m_constructorInfo;
|
||||
pProfiler->m_pTimeContainer->m_totalInfo += info;
|
||||
pProfiler->m_pTimeContainer->m_selfInfo += info;
|
||||
pProfiler->m_pTimeContainer->m_bUsed = false;
|
||||
|
||||
SLoadingTimeContainer* pParent = pProfiler->m_pTimeContainer->m_pParent;
|
||||
pParent->m_selfInfo -= info;
|
||||
pParent->m_dSelfTime -= fSelfTime;
|
||||
pParent->m_dSelfMemUsage -= fSelfMemUsage;
|
||||
if (pProfiler->m_pTimeContainer->m_pParent && pProfiler->m_pTimeContainer->m_pParent->m_nRootIndex == m_iActiveRoot)
|
||||
{
|
||||
m_pCurrentLoadingTimeContainer = pProfiler->m_pTimeContainer->m_pParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* CLoadingProfilerSystem::GetLoadingProfilerCallstack()
|
||||
{
|
||||
CryAutoCriticalSection lock(csLock);
|
||||
|
||||
static char szStack[1024];
|
||||
|
||||
szStack[0] = 0;
|
||||
|
||||
SLoadingTimeContainer* pC = m_pCurrentLoadingTimeContainer;
|
||||
|
||||
PodArray<SLoadingTimeContainer*> arrItems;
|
||||
|
||||
while (pC)
|
||||
{
|
||||
arrItems.Add(pC);
|
||||
pC = pC->m_pParent;
|
||||
}
|
||||
|
||||
for (int i = arrItems.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
cry_strcat(szStack, " > ");
|
||||
cry_strcat(szStack, arrItems[i]->m_pFuncName);
|
||||
}
|
||||
|
||||
return &szStack[0];
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers)
|
||||
{
|
||||
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
|
||||
|
||||
PodArray<SLoadingTimeContainer> arrNoStack;
|
||||
CreateNoStackList(arrNoStack);
|
||||
//qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
|
||||
|
||||
uint32 count = arrNoStack.Size();
|
||||
profilers.resize(count);
|
||||
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
profilers[i].name = arrNoStack[i].m_pFuncName;
|
||||
profilers[i].selfTime = arrNoStack[i].m_dSelfTime;
|
||||
profilers[i].callsTotal = arrNoStack[i].m_nCounter;
|
||||
profilers[i].totalTime = arrNoStack[i].m_dTotalTime;
|
||||
profilers[i].memorySize = arrNoStack[i].m_dTotalMemUsage;
|
||||
profilers[i].selfInfo = arrNoStack[i].m_selfInfo;
|
||||
profilers[i].totalInfo = arrNoStack[i].m_totalInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLoadingProfilerSystem::AddTimeContainerFunction(PodArray<SLoadingTimeContainer>& arrNoStack, SLoadingTimeContainer* node)
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SLoadingTimeContainer* it = std::find(arrNoStack.begin(), arrNoStack.end(), node->m_pFuncName);
|
||||
|
||||
if (it == arrNoStack.end())
|
||||
{
|
||||
arrNoStack.push_back(*node);
|
||||
}
|
||||
else
|
||||
{
|
||||
it->m_dSelfMemUsage += node->m_dSelfMemUsage;
|
||||
it->m_dSelfTime += node->m_dSelfTime;
|
||||
it->m_dTotalMemUsage += node->m_dTotalMemUsage;
|
||||
it->m_dTotalTime += node->m_dTotalTime;
|
||||
it->m_nCounter += node->m_nCounter;
|
||||
it->m_selfInfo += node->m_selfInfo;
|
||||
it->m_totalInfo += node->m_totalInfo;
|
||||
}
|
||||
|
||||
for (size_t i = 0, end = node->m_pChilds.size(); i < end; ++i)
|
||||
{
|
||||
AddTimeContainerFunction(arrNoStack, node->m_pChilds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::CreateNoStackList(PodArray<SLoadingTimeContainer>& arrNoStack)
|
||||
{
|
||||
AddTimeContainerFunction(arrNoStack, m_pRoot[m_iActiveRoot]);
|
||||
}
|
||||
|
||||
#define g_szTestResults "@cache@\\TestResults"
|
||||
|
||||
void CLoadingProfilerSystem::SaveTimeContainersToFile(const char* name, double fMinTotalTime, bool bClean)
|
||||
{
|
||||
if (m_pRoot[m_iActiveRoot])
|
||||
{
|
||||
const char* levelName = name;
|
||||
//Ignore any folders in the input name
|
||||
const char* folder = strrchr(name, '/');
|
||||
if (folder != NULL)
|
||||
{
|
||||
levelName = folder + 1;
|
||||
}
|
||||
char path[AZ::IO::IArchive::MaxPath];
|
||||
path[sizeof(path) - 1] = 0;
|
||||
|
||||
gEnv->pCryPak->AdjustFileName(string(string(g_szTestResults) + "\\" + levelName).c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
gEnv->pCryPak->MakeDir(g_szTestResults);
|
||||
|
||||
AZ::IO::HandleType handle = AZ::IO::InvalidHandle;
|
||||
|
||||
AZ::IO::Result f = AZ::IO::FileIOBase::GetInstance()->Open(path,AZ::IO::OpenMode::ModeWrite, handle);
|
||||
|
||||
if (handle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
|
||||
WriteTimeContainerToFile(m_pRoot[m_iActiveRoot], handle, 0, fMinTotalTime);
|
||||
AZ::IO::FileIOBase::GetInstance()->Close(handle);
|
||||
}
|
||||
|
||||
if (bClean)
|
||||
{
|
||||
Clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->m_dTotalTime < fMinTotalTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CryFixedStringT<MAX_LOADING_TIME_PROFILER_STACK_DEPTH> sDepth;
|
||||
for (unsigned int i = 0; i < depth; i++)
|
||||
{
|
||||
sDepth += "\t";
|
||||
}
|
||||
|
||||
CryFixedStringT<128> str(p->m_pFuncName);
|
||||
str.replace(':', '_');
|
||||
|
||||
char data[4096];
|
||||
AZ::u64 bytesWritten;
|
||||
|
||||
azsnprintf(data, sizeof(data), "%s<%s selfTime='%f' selfMemory='%f' totalTime='%f' totalMemory='%f' count='%i' totalSeeks='%i' totalReads='%i' totalOpens='%i' totalDiskSize='%f' selfSeeks='%i' selfReads='%i' selfOpens='%i' selfDiskSize='%f'>\n",
|
||||
sDepth.c_str(), str.c_str(), p->m_dSelfTime, p->m_dSelfMemUsage, p->m_dTotalTime, p->m_dTotalMemUsage, p->m_nCounter,
|
||||
p->m_totalInfo.m_nSeeksCount, p->m_totalInfo.m_nFileReadCount, p->m_totalInfo.m_nFileOpenCount, p->m_totalInfo.m_dOperationSize,
|
||||
p->m_selfInfo.m_nSeeksCount, p->m_selfInfo.m_nFileReadCount, p->m_selfInfo.m_nFileOpenCount, p->m_selfInfo.m_dOperationSize);
|
||||
|
||||
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
|
||||
|
||||
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
|
||||
{
|
||||
WriteTimeContainerToFile(p->m_pChilds[i], handle, depth + 1, fMinTotalTime);
|
||||
}
|
||||
|
||||
azsnprintf(data, sizeof(data), "%s</%s>\n", sDepth.c_str(), str.c_str());
|
||||
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
|
||||
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::UpdateSelfStatistics(SLoadingTimeContainer* p)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p->m_dSelfMemUsage = 0;
|
||||
p->m_dSelfTime = 0;
|
||||
p->m_nCounter = 1;
|
||||
p->m_selfInfo.m_dOperationSize = 0;
|
||||
p->m_selfInfo.m_nFileOpenCount = 0;
|
||||
p->m_selfInfo.m_nFileReadCount = 0;
|
||||
p->m_selfInfo.m_nSeeksCount = 0;
|
||||
|
||||
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
|
||||
{
|
||||
p->m_dTotalMemUsage += p->m_pChilds[i]->m_dTotalMemUsage;
|
||||
p->m_dTotalTime += p->m_pChilds[i]->m_dTotalTime;
|
||||
p->m_totalInfo += p->m_pChilds[i]->m_totalInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void CLoadingProfilerSystem::Clean()
|
||||
{
|
||||
m_iActiveRoot = (m_iActiveRoot + 1) % 2;
|
||||
if (m_pRoot[m_iActiveRoot])
|
||||
{
|
||||
delete m_pRoot[m_iActiveRoot];
|
||||
}
|
||||
m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
struct SLoadingTimeContainer;
|
||||
|
||||
|
||||
struct SLoadingProfilerInfo
|
||||
{
|
||||
string name;
|
||||
double selfTime;
|
||||
double totalTime;
|
||||
uint32 callsTotal;
|
||||
double memorySize;
|
||||
|
||||
DiskOperationInfo selfInfo;
|
||||
DiskOperationInfo totalInfo;
|
||||
};
|
||||
|
||||
|
||||
class CLoadingProfilerSystem
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void ShutDown();
|
||||
static void CreateNoStackList(PodArray<SLoadingTimeContainer>&);
|
||||
static void OutputLoadingTimeStats(ILog* pLog, int nMode);
|
||||
static SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName);
|
||||
static void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler);
|
||||
static const char* GetLoadingProfilerCallstack();
|
||||
static void FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers);
|
||||
static void FlushTimeContainers();
|
||||
static void SaveTimeContainersToFile(const char*, double fMinTotalTime, bool bClean);
|
||||
static void WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime);
|
||||
|
||||
static void UpdateSelfStatistics(SLoadingTimeContainer* p);
|
||||
static void Clean();
|
||||
protected:
|
||||
static void AddTimeContainerFunction(PodArray<SLoadingTimeContainer>&, SLoadingTimeContainer*);
|
||||
protected:
|
||||
static int nLoadingProfileMode;
|
||||
static int nLoadingProfilerNotTrackedAllocations;
|
||||
static CryCriticalSection csLock;
|
||||
static int m_iMaxArraySize;
|
||||
static SLoadingTimeContainer* m_pCurrentLoadingTimeContainer;
|
||||
static SLoadingTimeContainer* m_pRoot[2];
|
||||
static int m_iActiveRoot;
|
||||
static ICVar* m_pEnableProfile;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <ISystem.h>
|
||||
|
||||
#include <AzCore/Socket/AzSocket.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#undef LockDebug
|
||||
//#define LockDebug(str1,str2) {string strMessage;strMessage.Format(str1,str2);if (m_clients.size()) OutputDebugString(strMessage.c_str());}
|
||||
@@ -46,16 +47,13 @@ public:
|
||||
|
||||
const char* path = nullptr; // Don't call GetGameFolder here, it returns a full absolute path and we just really want the game name
|
||||
|
||||
if (ICVar* pVar = gEnv->pConsole->GetCVar("sys_game_folder"))
|
||||
{
|
||||
path = pVar->GetString();
|
||||
}
|
||||
if (!path)
|
||||
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
|
||||
if (projectPath.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pNotificationNetwork->Send("SystemInfo", path, ::strlen(path) + 1);
|
||||
pNotificationNetwork->Send("SystemInfo", projectPath.c_str(), projectPath.size());
|
||||
}
|
||||
} g_queryNotification;
|
||||
|
||||
|
||||
@@ -1243,7 +1243,7 @@ namespace
|
||||
{
|
||||
char path[AZ::IO::IArchive::MaxPath];
|
||||
path[sizeof(path) - 1] = 0;
|
||||
gEnv->pCryPak->AdjustFileName("@cache@\\TestResults\\StreamingLog.txt", path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
gEnv->pCryPak->AdjustFileName("@usercache@\\TestResults\\StreamingLog.txt", path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
|
||||
sFileName = path;
|
||||
}
|
||||
AZ::IO::HandleType fileHandle = fxopen(sFileName, (bFirstTime) ? "wt" : "at");
|
||||
|
||||
@@ -157,7 +157,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
#include "ServerThrottle.h"
|
||||
#include "ILocalMemoryUsage.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "LoadingProfiler.h"
|
||||
#include "HMDBus.h"
|
||||
#include "OverloadSceneManager/OverloadSceneManager.h"
|
||||
#include <IThreadManager.h>
|
||||
@@ -168,7 +167,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
#include "IZStdDecompressor.h"
|
||||
#include "zlib.h"
|
||||
#include "RemoteConsole/RemoteConsole.h"
|
||||
#include "BootProfiler.h"
|
||||
|
||||
#include <PNoise3.h>
|
||||
#include <StringUtils.h>
|
||||
@@ -320,10 +318,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
m_bIsAsserting = false;
|
||||
m_pSystemEventDispatcher = new CSystemEventDispatcher(); // Must be first.
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CBootProfiler::GetInstance().Init(this);
|
||||
#endif
|
||||
|
||||
if (m_pSystemEventDispatcher)
|
||||
{
|
||||
m_pSystemEventDispatcher->RegisterListener(this);
|
||||
@@ -422,7 +416,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
|
||||
// m_sys_filecache = NULL;
|
||||
m_gpu_particle_physics = NULL;
|
||||
m_pCpu = NULL;
|
||||
m_sys_game_folder = NULL;
|
||||
|
||||
m_bInitializedSuccessfully = false;
|
||||
m_bShaderCacheGenMode = false;
|
||||
@@ -634,10 +627,6 @@ void CSystem::ShutDown()
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemShutdown, *this);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CLoadingProfilerSystem::ShutDown();
|
||||
#endif
|
||||
|
||||
if (m_pUserCallback)
|
||||
{
|
||||
m_pUserCallback->OnShutdown();
|
||||
@@ -657,7 +646,7 @@ void CSystem::ShutDown()
|
||||
SAFE_DELETE(m_pTextModeConsole);
|
||||
|
||||
KillPhysicsThread();
|
||||
|
||||
|
||||
if (m_sys_firstlaunch)
|
||||
{
|
||||
m_sys_firstlaunch->Set("0");
|
||||
@@ -828,7 +817,7 @@ void CSystem::ShutDown()
|
||||
void CSystem::Quit()
|
||||
{
|
||||
CryLogAlways("CSystem::Quit invoked from thread %" PRI_THREADID " (main is %" PRI_THREADID ")", GetCurrentThreadId(), gEnv->mMainThreadId);
|
||||
|
||||
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
|
||||
|
||||
// If this was set from anywhere but the main thread, bail and let the main thread handle shutdown
|
||||
@@ -857,13 +846,13 @@ void CSystem::Quit()
|
||||
|
||||
/*
|
||||
* TODO: This call to _exit, _Exit, TerminateProcess etc. needs to
|
||||
* eventually be removed. This causes an extremely early exit before we
|
||||
* actually perform cleanup. When this gets called most managers are
|
||||
* eventually be removed. This causes an extremely early exit before we
|
||||
* actually perform cleanup. When this gets called most managers are
|
||||
* simply never deleted and we leave it to the OS to clean up our mess
|
||||
* which is just really bad practice. However there are LOTS of issues
|
||||
* with shutdown at the moment. Removing this will simply cause
|
||||
* a crash when either the Editor or Launcher initiate shutdown. Both
|
||||
* applications crash differently too. Bugs will be logged about those
|
||||
* with shutdown at the moment. Removing this will simply cause
|
||||
* a crash when either the Editor or Launcher initiate shutdown. Both
|
||||
* applications crash differently too. Bugs will be logged about those
|
||||
* issues.
|
||||
*/
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
@@ -1117,7 +1106,7 @@ void CSystem::CreatePhysicsThread()
|
||||
#include AZ_RESTRICTED_FILE(System_cpp)
|
||||
#endif
|
||||
|
||||
{
|
||||
{
|
||||
m_PhysThread = new CPhysicsThreadTask;
|
||||
GetIThreadTaskManager()->RegisterTask(m_PhysThread, threadParams);
|
||||
}
|
||||
@@ -1412,7 +1401,7 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
ti.xscale = ti.yscale = 1.2f;
|
||||
|
||||
const int viewportHeight = GetViewCamera().GetViewSurfaceZ();
|
||||
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_8
|
||||
#include AZ_RESTRICTED_FILE(System_cpp)
|
||||
@@ -1428,7 +1417,7 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
switch (stat.GetType())
|
||||
{
|
||||
case AZ::IO::Statistic::Type::FloatingPoint:
|
||||
gEnv->pRenderer->DrawTextQueued(Vec3(10, y, 1.0f), ti,
|
||||
gEnv->pRenderer->DrawTextQueued(Vec3(10, y, 1.0f), ti,
|
||||
AZStd::string::format("%s/%s: %.3f", stat.GetOwner().data(), stat.GetName().data(), stat.GetFloatValue()).c_str());
|
||||
break;
|
||||
case AZ::IO::Statistic::Type::Integer:
|
||||
@@ -2744,10 +2733,10 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam
|
||||
{
|
||||
// System event translation
|
||||
case WM_CLOSE:
|
||||
/*
|
||||
/*
|
||||
Trigger CSystem to call Quit() the next time
|
||||
it calls Update(). HandleMessages can get messages
|
||||
pumped to it from SyncMainWithRender which would
|
||||
pumped to it from SyncMainWithRender which would
|
||||
be called recurively by Quit(). Doing so would
|
||||
cause the render thread to deadlock and the main
|
||||
thread to spin in SRenderThread::WaitFlushFinishedCond.
|
||||
@@ -2897,11 +2886,6 @@ std::shared_ptr<AZ::IO::FileIOBase> CSystem::CreateLocalFileIO()
|
||||
return std::make_shared<AZ::IO::LocalFileIO>();
|
||||
}
|
||||
|
||||
const char* CSystem::GetAssetsPlatform() const
|
||||
{
|
||||
return m_assetPlatform.c_str();
|
||||
}
|
||||
|
||||
IViewSystem* CSystem::GetIViewSystem()
|
||||
{
|
||||
return m_pViewSystem;
|
||||
|
||||
@@ -45,7 +45,6 @@ struct IConsoleCmdArgs;
|
||||
class CServerThrottle;
|
||||
struct ICryFactoryRegistryImpl;
|
||||
struct IZLibCompressor;
|
||||
class CLoadingProfilerSystem;
|
||||
class CWatchdogThread;
|
||||
class CThreadManager;
|
||||
|
||||
@@ -437,11 +436,6 @@ public:
|
||||
|
||||
uint32 GetUsedMemory();
|
||||
|
||||
//! For asset processor, we need to know what kind of assets we're loading. This comes all the way from bootstrap.cfg
|
||||
//! It will be a string like "pc" or "es3" or such and controls what kind of assets we have access to (its used when for example
|
||||
//! attempting to open a file when multiple different assets for different platforms are available.)
|
||||
const char* GetAssetsPlatform() const;
|
||||
|
||||
virtual void DumpMemoryUsageStatistics(bool bUseKB);
|
||||
virtual void DumpMemoryCoverage();
|
||||
void CollectMemInfo(SCryEngineStatsGlobalMemInfo&);
|
||||
@@ -685,7 +679,7 @@ private:
|
||||
bool InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInitParams& initParams);
|
||||
|
||||
bool InitFont(const SSystemInitParams& initParams);
|
||||
bool InitFileSystem(const SSystemInitParams& initParams);
|
||||
bool InitFileSystem();
|
||||
bool InitFileSystem_LoadEngineFolders(const SSystemInitParams& initParams);
|
||||
bool InitStreamEngine();
|
||||
bool Init3DEngine(const SSystemInitParams& initParams);
|
||||
@@ -792,7 +786,7 @@ public:
|
||||
|
||||
const CTimeValue& GetLastTickTime(void) const { return m_lastTickTime; }
|
||||
const ICVar* GetDedicatedMaxRate(void) const { return m_svDedicatedMaxRate; }
|
||||
|
||||
|
||||
const char* GetRenderingDriverName(void) const
|
||||
{
|
||||
if(m_rDriver)
|
||||
@@ -802,7 +796,7 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<AZ::IO::FileIOBase> CreateLocalFileIO();
|
||||
|
||||
// Gets the dimensions (in pixels) of the primary physical display.
|
||||
@@ -914,7 +908,6 @@ private: // ------------------------------------------------------
|
||||
|
||||
// DLL names
|
||||
ICVar* m_sys_dll_response_system;
|
||||
ICVar* m_sys_game_folder;
|
||||
#if !defined(_RELEASE)
|
||||
ICVar* m_sys_resource_cache_folder;
|
||||
#endif
|
||||
@@ -950,7 +943,6 @@ private: // ------------------------------------------------------
|
||||
ICVar* m_rFullscreenWindow;
|
||||
ICVar* m_rFullscreenNativeRes;
|
||||
ICVar* m_rDriver;
|
||||
ICVar* m_cvGameName;
|
||||
ICVar* m_rDisplayInfo;
|
||||
ICVar* m_rOverscanBordersDrawDebugView;
|
||||
ICVar* m_sysNoUpdate;
|
||||
@@ -1040,8 +1032,6 @@ private: // ------------------------------------------------------
|
||||
|
||||
uint64 m_nUpdateCounter;
|
||||
|
||||
int sys_ProfileLevelLoading, sys_ProfileLevelLoadingDump;
|
||||
|
||||
bool m_executedCommandLine = false;
|
||||
|
||||
AZStd::unique_ptr<AzFramework::MissingAssetLogger> m_missingAssetLogger;
|
||||
@@ -1072,18 +1062,6 @@ public:
|
||||
void CloseLanguageAudioPak(const char* sLanguage);
|
||||
void UpdateMovieSystem(const int updateFlags, const float fFrameTime, const bool bPreUpdate);
|
||||
|
||||
// level loading profiling
|
||||
virtual void OutputLoadingTimeStats();
|
||||
virtual struct SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName);
|
||||
virtual void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler);
|
||||
virtual const char* GetLoadingProfilerCallstack();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual CBootProfilerRecord* StartBootSectionProfiler(const char* name, const char* args);
|
||||
virtual void StopBootSectionProfiler(CBootProfilerRecord* record);
|
||||
virtual void StartBootProfilerSessionFrames(const char* pName);
|
||||
virtual void StopBootProfilerSessionFrames();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CryAssert and error related.
|
||||
virtual bool RegisterErrorObserver(IErrorObserver* errorObserver);
|
||||
@@ -1139,17 +1117,9 @@ protected: // -------------------------------------------------------------
|
||||
ITextModeConsole* m_pTextModeConsole;
|
||||
INotificationNetwork* m_pNotificationNetwork;
|
||||
|
||||
string m_binariesDir;
|
||||
string m_currentLanguageAudio;
|
||||
string m_assetPlatform; // ("es3" / "pc" / etc) describes the KIND of assets we load and controls where they're loaded from
|
||||
string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_es3.cfg or system_android_opengl.cfg or system_windows_pc.cfg
|
||||
|
||||
// the following variables capture what was set up in the systeminitparams after/during InitFileSystem
|
||||
// They are not actually to be used except to establish aliases like @user@
|
||||
string m_userRootDir;
|
||||
string m_cacheDir;
|
||||
string m_logsDir;
|
||||
|
||||
std::vector< std::pair<CTimeValue, float> > m_updateTimes;
|
||||
|
||||
CMemoryFragmentationProfiler m_MemoryFragmentationProfiler;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include "SystemCFG.h"
|
||||
@@ -252,15 +253,8 @@ void CSystem::LogVersion()
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::LogBuildInfo()
|
||||
{
|
||||
ICVar* pGameName = m_env.pConsole->GetCVar("sys_game_name");
|
||||
if (pGameName)
|
||||
{
|
||||
CryLogAlways("GameName: %s", pGameName->GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
CryLogAlways("Couldn't find game name in cvar sys_game_name");
|
||||
}
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
CryLogAlways("GameName: %s", projectName.c_str());
|
||||
CryLogAlways("BuildTime: " __DATE__ " " __TIME__);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#include <LoadScreenBus.h>
|
||||
#include <LyShine/Bus/UiSystemBus.h>
|
||||
#include <AzFramework/Logging/MissingAssetLogger.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
#include <AzFramework/API/AtomActiveInterface.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
@@ -115,8 +116,6 @@
|
||||
#include "SystemCFG.h"
|
||||
#include "AutoDetectSpec.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "LoadingProfiler.h"
|
||||
#include "BootProfiler.h"
|
||||
#include "VisRegTest.h"
|
||||
#include "MTSafeAllocator.h"
|
||||
#include "NotificationNetwork.h"
|
||||
@@ -498,7 +497,7 @@ struct SysSpecOverrideSinkConsole
|
||||
else
|
||||
{
|
||||
// If the cvar doesn't exist, calling this function only saves the value in case it's registered later where
|
||||
// at that point it will be set from the stored value. This is required because otherwise registering the
|
||||
// at that point it will be set from the stored value. This is required because otherwise registering the
|
||||
// cvar bypasses any callbacks and uses values directly from the cvar group files.
|
||||
gEnv->pConsole->LoadConfigVar(szKey, szValue);
|
||||
}
|
||||
@@ -618,13 +617,13 @@ static void LoadDetectedSpec(ICVar* pVar)
|
||||
if (gEnv->IsEditor())
|
||||
{
|
||||
ESystemConfigPlatform configPlatform = GetISystem()->GetConfigPlatform();
|
||||
// Check if the config platform is set first.
|
||||
// Check if the config platform is set first.
|
||||
if (configPlatform != CONFIG_INVALID_PLATFORM)
|
||||
{
|
||||
platform = configPlatform;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AZStd::string configFile;
|
||||
GetSpecConfigFileToLoad(pVar, configFile, platform);
|
||||
if (configFile.length())
|
||||
@@ -779,7 +778,7 @@ static void LoadDetectedSpec(ICVar* pVar)
|
||||
MobileSysInspect::GetSpecForGPUAndAPI(adapterDesc, apiver, gpuConfigFile);
|
||||
GetISystem()->LoadConfiguration(gpuConfigFile.c_str(), pSysSpecOverrideSinkConsole);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (bMultiGPUEnabled)
|
||||
{
|
||||
@@ -832,23 +831,7 @@ AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDynamiclibrary(const cha
|
||||
{
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> handle = AZ::DynamicModuleHandle::Create(dllName);
|
||||
|
||||
bool libraryLoaded = false;
|
||||
#ifdef WIN32
|
||||
if (m_binariesDir.empty())
|
||||
{
|
||||
libraryLoaded = handle->Load(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
char currentDirectory[1024];
|
||||
AZ::Utils::GetExecutableDirectory(currentDirectory, AZ_ARRAY_SIZE(currentDirectory));
|
||||
SetCurrentDirectory(m_binariesDir.c_str());
|
||||
libraryLoaded = handle->Load(false);
|
||||
SetCurrentDirectory(currentDirectory);
|
||||
}
|
||||
#else
|
||||
libraryLoaded = handle->Load(false);
|
||||
#endif
|
||||
bool libraryLoaded = handle->Load(false);
|
||||
// We need to inject the environment first thing so that allocators are available immediately
|
||||
InjectEnvironmentFunction injectEnv = handle->GetFunction<InjectEnvironmentFunction>(INJECT_ENVIRONMENT_FUNCTION);
|
||||
if (injectEnv)
|
||||
@@ -974,7 +957,7 @@ bool CSystem::InitializeEngineModule(const char* dllName, const char* moduleClas
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
#else
|
||||
|
||||
dllfile.append(dllName);
|
||||
|
||||
@@ -1007,7 +990,7 @@ bool CSystem::InitializeEngineModule(const char* dllName, const char* moduleClas
|
||||
if (CryCreateClassInstance(moduleClassName, pModule))
|
||||
{
|
||||
bResult = pModule->Initialize(m_env, initParams);
|
||||
|
||||
|
||||
// After initializing the module, give it a chance to register any AZ console vars
|
||||
// declared within the module.
|
||||
pModule->RegisterConsoleVars();
|
||||
@@ -1453,7 +1436,7 @@ bool CSystem::InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInit
|
||||
|
||||
displayWidth *= scaleFactor;
|
||||
displayHeight *= scaleFactor;
|
||||
|
||||
|
||||
const int maxWidth = m_rMaxWidth->GetIVal();
|
||||
if (maxWidth > 0 && maxWidth < displayWidth)
|
||||
{
|
||||
@@ -1524,7 +1507,7 @@ bool CSystem::InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInit
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::InitFileSystem(const SSystemInitParams& initParams)
|
||||
bool CSystem::InitFileSystem()
|
||||
{
|
||||
LOADING_TIME_PROFILE_SECTION;
|
||||
using namespace AzFramework::AssetSystem;
|
||||
@@ -1549,136 +1532,6 @@ bool CSystem::InitFileSystem(const SSystemInitParams& initParams)
|
||||
}
|
||||
#endif // !defined(_RELEASE)
|
||||
|
||||
bool usingAssetCache = initParams.UseAssetCache();
|
||||
const char* rootPath = usingAssetCache ? initParams.rootPathCache : initParams.rootPath;
|
||||
const char* assetsPath = usingAssetCache ? initParams.assetsPathCache : initParams.assetsPath;
|
||||
|
||||
if (rootPath == 0)
|
||||
{
|
||||
AZ_Assert(false, "No root path specified in SystemInitParams");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (assetsPath == 0)
|
||||
{
|
||||
AZ_Assert(false, "No assets path specified in SystemInitParams");
|
||||
return false;
|
||||
}
|
||||
|
||||
// establish the root folder and assets folder immediately.
|
||||
// Other folders that can be computed from the root can be specified later.
|
||||
m_env.pFileIO->SetAlias("@root@", rootPath);
|
||||
m_env.pFileIO->SetAlias("@assets@", assetsPath);
|
||||
|
||||
if (initParams.userPath[0] == 0)
|
||||
{
|
||||
string outPath = PathUtil::Make(m_env.pFileIO->GetAlias("@root@"), "user");
|
||||
|
||||
m_env.pFileIO->SetAlias("@user@", outPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_env.pFileIO->SetAlias("@user@", initParams.userPath);
|
||||
}
|
||||
|
||||
if (initParams.logPath[0] == 0)
|
||||
{
|
||||
char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
||||
|
||||
m_env.pFileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
|
||||
string outPath = PathUtil::Make(resolveBuffer, "log");
|
||||
m_env.pFileIO->SetAlias("@log@", outPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_env.pFileIO->SetAlias("@log@", initParams.logPath);
|
||||
}
|
||||
|
||||
m_env.pFileIO->CreatePath("@root@");
|
||||
m_env.pFileIO->CreatePath("@user@");
|
||||
m_env.pFileIO->CreatePath("@log@");
|
||||
|
||||
if ((!m_env.IsInToolMode()) || (m_bShaderCacheGenMode)) // in tool mode, the promise is that you won't access @cache@!
|
||||
{
|
||||
string finalCachePath;
|
||||
if (initParams.cachePath[0] == 0)
|
||||
{
|
||||
char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
||||
|
||||
m_env.pFileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
|
||||
finalCachePath = PathUtil::Make(resolveBuffer, "cache");
|
||||
}
|
||||
else
|
||||
{
|
||||
finalCachePath = initParams.cachePath;
|
||||
}
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
// Search for a non-locked cache directory because shaders require separate caches for each running instance.
|
||||
// We only need to do this check for Windows, because consoles can't have multiple instances running simultaneously.
|
||||
// Ex: running editor and game, running multiple games, or multiple non-interactive editor instances
|
||||
// for parallel level exports.
|
||||
|
||||
string originalPath = finalCachePath;
|
||||
#if defined(REMOTE_ASSET_PROCESSOR)
|
||||
bool allowEngineConnection = !initParams.bToolMode && !initParams.bTestMode;
|
||||
bool allowRemoteIO = allowEngineConnection && initParams.remoteFileIO && !initParams.bEditor;
|
||||
|
||||
if (!allowRemoteIO) // not running on VFS
|
||||
#endif
|
||||
{
|
||||
int attemptNumber = 0;
|
||||
|
||||
// The number of max attempts ultimately dictates the number of Lumberyard instances that can run
|
||||
// simultaneously. This should be a reasonably high number so that it doesn't artificially limit
|
||||
// the number of instances (ex: parallel level exports via multiple Editor runs). It also shouldn't
|
||||
// be set *infinitely* high - each cache folder is GBs in size, and finding a free directory is a
|
||||
// linear search, so the more instances we allow, the longer the search will take.
|
||||
// 128 seems like a reasonable compromise.
|
||||
constexpr int maxAttempts = 128;
|
||||
|
||||
char workBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
||||
while (attemptNumber < maxAttempts)
|
||||
{
|
||||
finalCachePath = originalPath;
|
||||
if (attemptNumber != 0)
|
||||
{
|
||||
azsnprintf(workBuffer, AZ_MAX_PATH_LEN, "%s%i", originalPath.c_str(), attemptNumber);
|
||||
finalCachePath = workBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalCachePath = originalPath;
|
||||
}
|
||||
|
||||
++attemptNumber; // do this here so we don't forget
|
||||
|
||||
m_env.pFileIO->CreatePath(finalCachePath.c_str());
|
||||
// if the directory already exists, check for locked file
|
||||
string outLockPath = PathUtil::Make(finalCachePath.c_str(), "lockfile.txt");
|
||||
|
||||
// note, the zero here after GENERIC_READ|GENERIC_WRITE indicates no share access at all
|
||||
g_cacheLock = CreateFileA(outLockPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
|
||||
if (g_cacheLock != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attemptNumber >= maxAttempts)
|
||||
{
|
||||
AZ_Assert(false, "Couldn't find a valid asset cache folder for the Asset Processor after %i attempts.", attemptNumber);
|
||||
AZ_Printf("FileSystem", "Couldn't find a valid asset cache folder for the Asset Processor after %i attempts.", attemptNumber);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(AZ_PLATFORM_WINDOWS)
|
||||
AZ_Printf("FileSystem", "Using %s folder for asset cache.\n", finalCachePath.c_str());
|
||||
m_env.pFileIO->SetAlias("@cache@", finalCachePath.c_str());
|
||||
m_env.pFileIO->CreatePath("@cache@");
|
||||
}
|
||||
|
||||
m_env.pCryPak = AZ::Interface<AZ::IO::IArchive>::Get();
|
||||
m_env.pFileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(m_env.pCryPak, "CryPak has not been initialized on AZ::Interface");
|
||||
@@ -1755,7 +1608,6 @@ void CSystem::ShutdownFileSystem()
|
||||
bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams& initParams)
|
||||
{
|
||||
LOADING_TIME_PROFILE_SECTION;
|
||||
// Load value of sys_game_folder from system.cfg into the sys_game_folder console variable
|
||||
{
|
||||
ILoadConfigurationEntrySink* pCVarsWhiteListConfigSink = GetCVarsWhiteListConfigSink();
|
||||
LoadConfiguration(m_systemConfigName.c_str(), pCVarsWhiteListConfigSink);
|
||||
@@ -1767,17 +1619,19 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams& initPara
|
||||
#endif
|
||||
|
||||
GetISystem()->SetConfigPlatform(GetDevicePlatform());
|
||||
|
||||
|
||||
#if defined(CRY_ENABLE_RC_HELPER)
|
||||
if (!m_env.pResourceCompilerHelper)
|
||||
{
|
||||
m_env.pResourceCompilerHelper = new CResourceCompilerHelper();
|
||||
}
|
||||
#endif
|
||||
// you may not set these in game.cfg or in system.cfg
|
||||
m_sys_game_folder->ForceSet(initParams.gameFolderName);
|
||||
|
||||
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "GameDir: %s\n", m_sys_game_folder->GetString());
|
||||
auto projectPath = AZ::Utils::GetProjectPath();
|
||||
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Path: %s\n", projectPath.empty() ? "None specified" : projectPath.c_str());
|
||||
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Name: %s\n", projectName.empty() ? "None specified" : projectName.c_str());
|
||||
|
||||
// simply open all paks if fast load pak can't be found
|
||||
if (!m_pResourceManager->LoadFastLoadPaks(true))
|
||||
@@ -1857,7 +1711,7 @@ bool CSystem::InitFont(const SSystemInitParams& initParams)
|
||||
bool CSystem::Init3DEngine(const SSystemInitParams& initParams)
|
||||
{
|
||||
LOADING_TIME_PROFILE_SECTION(GetISystem());
|
||||
|
||||
|
||||
if (!InitializeEngineModule(DLL_3DENGINE, "EngineModule_Cry3DEngine", initParams))
|
||||
{
|
||||
return false;
|
||||
@@ -1930,7 +1784,7 @@ bool CSystem::InitVTuneProfiler()
|
||||
LOADING_TIME_PROFILE_SECTION(GetISystem());
|
||||
|
||||
#ifdef PROFILE_WITH_VTUNE
|
||||
|
||||
|
||||
WIN_HMODULE hModule = LoadDLL("VTuneApi.dll");
|
||||
if (!hModule)
|
||||
{
|
||||
@@ -2033,7 +1887,7 @@ void CSystem::OpenBasicPaks()
|
||||
bBasicPaksLoaded = true;
|
||||
|
||||
LOADING_TIME_PROFILE_SECTION;
|
||||
|
||||
|
||||
// open pak files
|
||||
constexpr AZStd::string_view paksFolder = "@assets@/*.pak"; // (@assets@ assumed)
|
||||
m_env.pCryPak->OpenPacks(paksFolder);
|
||||
@@ -2188,12 +2042,6 @@ string GetUniqueLogFileName(string logFileName)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OnLevelLoadingDump([[maybe_unused]] ICVar* pArgs)
|
||||
{
|
||||
gEnv->pSystem->OutputLoadingTimeStats();
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
static wstring GetErrorStringUnsupportedCPU()
|
||||
{
|
||||
@@ -2415,8 +2263,8 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
signal(SIGILL, CryEngineSignalHandler);
|
||||
#endif // AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
|
||||
|
||||
// Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the
|
||||
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is
|
||||
// Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the
|
||||
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is
|
||||
// initialized to this instance's SSystemGlobalEnvironment (m_env), we will force set it again here
|
||||
// to m_env
|
||||
if (!gEnv)
|
||||
@@ -2451,7 +2299,7 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
// Linux is all console for now and so no room for dialog boxes!
|
||||
m_env.bNoAssertDialog = true;
|
||||
#endif
|
||||
|
||||
|
||||
m_pCmdLine = new CCmdLine(startupParams.szSystemCmdLine);
|
||||
|
||||
AZCoreLogSink::Connect();
|
||||
@@ -2461,25 +2309,25 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
{
|
||||
azConsole->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
|
||||
}
|
||||
|
||||
m_assetPlatform = startupParams.assetsPlatform;
|
||||
|
||||
// compute system config name
|
||||
if (m_assetPlatform.empty())
|
||||
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
|
||||
{
|
||||
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n"
|
||||
R"(This can be set via the via the --regset "%s/assets=<value>" command line option)"
|
||||
R"(, by setting value at the "%s/assets path" within a *.setreg file that is loaded by the application)"
|
||||
R"( or by setting the "assets" field in the bootstrap.cfg.)",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
return false;
|
||||
}
|
||||
AZ::SettingsRegistryInterface::FixedValueString assetPlatform;
|
||||
if (!AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, assetPlatform,
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "assets"))
|
||||
{
|
||||
assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
|
||||
AZ_Warning(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n"
|
||||
R"(This typically done by setting he "assets" field in the bootstrap.cfg for within a .setreg file)""\n"
|
||||
R"(A fallback of %s will be used.)",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
assetPlatform.c_str());
|
||||
}
|
||||
|
||||
m_systemConfigName = "system_" AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER "_";
|
||||
m_systemConfigName += m_assetPlatform;
|
||||
m_systemConfigName += ".cfg";
|
||||
m_systemConfigName = "system_" AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER "_";
|
||||
m_systemConfigName += assetPlatform.c_str();
|
||||
m_systemConfigName += ".cfg";
|
||||
}
|
||||
|
||||
AZ_Assert(CryMemory::IsHeapValid(), "Memory heap must be valid before continuing SystemInit.");
|
||||
|
||||
@@ -2518,11 +2366,6 @@ AZ_POP_DISABLE_WARNING
|
||||
m_hInst = (WIN_HINSTANCE)startupParams.hInstance;
|
||||
m_hWnd = (WIN_HWND)startupParams.hWnd;
|
||||
|
||||
m_userRootDir = startupParams.userPath;
|
||||
m_logsDir = startupParams.logPath;
|
||||
m_cacheDir = startupParams.cachePath;
|
||||
|
||||
m_binariesDir = startupParams.szBinariesDir;
|
||||
m_bEditor = startupParams.bEditor;
|
||||
m_bPreviewMode = startupParams.bPreview;
|
||||
m_bTestMode = startupParams.bTestMode;
|
||||
@@ -2666,17 +2509,13 @@ AZ_POP_DISABLE_WARNING
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// File system, must be very early
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!InitFileSystem(startupParams))
|
||||
if (!InitFileSystem())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
InlineInitializationProcessing("CSystem::Init InitFileSystem");
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CLoadingProfilerSystem::Init();
|
||||
#endif
|
||||
|
||||
m_missingAssetLogger = AZStd::make_unique<AzFramework::MissingAssetLogger>();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -2786,10 +2625,6 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
GetIRemoteConsole()->RegisterConsoleVariables();
|
||||
|
||||
#ifdef ENABLE_LOADING_PROFILER
|
||||
CBootProfiler::GetInstance().RegisterCVars();
|
||||
#endif
|
||||
|
||||
if (!startupParams.bSkipConsole)
|
||||
{
|
||||
// Register system console variables.
|
||||
@@ -2827,7 +2662,7 @@ AZ_POP_DISABLE_WARNING
|
||||
m_env.pCryPak->OpenPack("@assets@", "Engine.pak");
|
||||
#if defined(AZ_PLATFORM_ANDROID) || defined(AZ_PLATFORM_IOS)
|
||||
MobileSysInspect::LoadDeviceSpecMapping();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
InitFileSystem_LoadEngineFolders(startupParams);
|
||||
|
||||
@@ -3472,10 +3307,6 @@ AZ_POP_DISABLE_WARNING
|
||||
MarkThisThreadForDebugging("Main");
|
||||
}
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CLoadingProfilerSystem::SaveTimeContainersToFile("EngineStart.crylp", 0.0, true);
|
||||
#endif
|
||||
|
||||
InlineInitializationProcessing("CSystem::Init End");
|
||||
|
||||
#if defined(IS_PROSDK)
|
||||
@@ -4188,7 +4019,7 @@ static void ScreenshotCmd(IConsoleCmdArgs* pParams)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to maintain backwards compatibility with our CVar but not force our new code to
|
||||
// Helper to maintain backwards compatibility with our CVar but not force our new code to
|
||||
// pull in CryCommon by routing through an environment variable
|
||||
void CmdSetAwsLogLevel(IConsoleCmdArgs* pArgs)
|
||||
{
|
||||
@@ -4424,7 +4255,6 @@ void CSystem::CreateSystemVars()
|
||||
// Register DLL names as cvars before we load them
|
||||
//
|
||||
EVarFlags dllFlags = (EVarFlags)0;
|
||||
m_sys_game_folder = REGISTER_STRING("sys_game_folder", "EmptyTemplate", VF_READONLY, "Specifies the game folder to read all data from. Can be fully pathed for external folders or relative path for folders inside the root.");
|
||||
m_sys_dll_response_system = REGISTER_STRING("sys_dll_response_system", 0, dllFlags, "Specifies the DLL to load for the dynamic response system");
|
||||
|
||||
m_sys_initpreloadpacks = REGISTER_STRING("sys_initpreloadpacks", "", 0, "Specifies the paks for an engine initialization");
|
||||
@@ -4447,8 +4277,6 @@ void CSystem::CreateSystemVars()
|
||||
m_level_load_screen_minimum_time = REGISTER_FLOAT("level_load_screen_minimum_time", 0.0f, 0, "Minimum amount of time to show the level load screen. Important to prevent short loads from flashing the load screen. 0 means there is no limit.");
|
||||
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
|
||||
|
||||
m_cvGameName = REGISTER_STRING("sys_game_name", "Lumberyard", VF_DUMPTODISK, "Specifies the name to be displayed in the Launcher window title bar");
|
||||
|
||||
REGISTER_INT("cvDoVerboseWindowTitle", 0, VF_NULL, "");
|
||||
|
||||
m_pCVarQuit = REGISTER_INT("ExitOnQuit", 1, VF_NULL, "");
|
||||
@@ -4495,7 +4323,7 @@ void CSystem::CreateSystemVars()
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
attachVariable("sys_PakReadSlice", &g_cvars.archiveVars.nReadSlice, "If non-0, means number of kilobytes to use to read files in portions. Should only be used on Win9x kernels");
|
||||
|
||||
attachVariable("sys_PakInMemorySizeLimit", &g_cvars.archiveVars.nInMemoryPerPakSizeLimit, "Individual pak size limit for being loaded into memory (MB)");
|
||||
@@ -4818,15 +4646,6 @@ void CSystem::CreateSystemVars()
|
||||
"e.g. LoadConfig lowspec.cfg\n"
|
||||
"Usage: LoadConfig <filename>");
|
||||
|
||||
REGISTER_CVAR(sys_ProfileLevelLoading, 0, VF_CHEAT,
|
||||
"Output level loading stats into log\n"
|
||||
"0 = Off\n"
|
||||
"1 = Output basic info about loading time per function\n"
|
||||
"2 = Output full statistics including loading time and memory allocations with call stack info");
|
||||
|
||||
REGISTER_CVAR_CB(sys_ProfileLevelLoadingDump, 0, VF_CHEAT, "Output level loading dump stats into log\n", OnLevelLoadingDump);
|
||||
|
||||
|
||||
assert(m_env.pConsole);
|
||||
m_env.pConsole->CreateKeyBind("alt_keyboard_key_function_F12", "Screenshot");
|
||||
m_env.pConsole->CreateKeyBind("alt_keyboard_key_function_F11", "RecordClip");
|
||||
@@ -4871,7 +4690,7 @@ void CSystem::CreateSystemVars()
|
||||
|
||||
// adding CVAR to toggle assert verbosity level
|
||||
const int defaultAssertValue = 1;
|
||||
REGISTER_CVAR2_CB("sys_asserts", &g_cvars.sys_asserts, defaultAssertValue, VF_CHEAT,
|
||||
REGISTER_CVAR2_CB("sys_asserts", &g_cvars.sys_asserts, defaultAssertValue, VF_CHEAT,
|
||||
"0 = Suppress Asserts\n"
|
||||
"1 = Log Asserts\n"
|
||||
"2 = Show Assert Dialog\n"
|
||||
@@ -4957,78 +4776,6 @@ void CSystem::AddCVarGroupDirectory(const string& sPath)
|
||||
gEnv->pCryPak->FindClose(handle);
|
||||
}
|
||||
|
||||
void CSystem::OutputLoadingTimeStats()
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
if (GetIConsole())
|
||||
{
|
||||
if (ICVar* pVar = GetIConsole()->GetCVar("sys_ProfileLevelLoading"))
|
||||
{
|
||||
CLoadingProfilerSystem::OutputLoadingTimeStats(GetILog(), pVar->GetIVal());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SLoadingTimeContainer* CSystem::StartLoadingSectionProfiling([[maybe_unused]] CLoadingTimeProfiler* pProfiler, [[maybe_unused]] const char* szFuncName)
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
return CLoadingProfilerSystem::StartLoadingSectionProfiling(pProfiler, szFuncName);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSystem::EndLoadingSectionProfiling([[maybe_unused]] CLoadingTimeProfiler* pProfiler)
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CLoadingProfilerSystem::EndLoadingSectionProfiling(pProfiler);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* CSystem::GetLoadingProfilerCallstack()
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
return CLoadingProfilerSystem::GetLoadingProfilerCallstack();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
CBootProfilerRecord* CSystem::StartBootSectionProfiler([[maybe_unused]] const char* name, [[maybe_unused]] const char* args)
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CBootProfiler& profiler = CBootProfiler::GetInstance();
|
||||
return profiler.StartBlock(name, args);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSystem::StopBootSectionProfiler([[maybe_unused]] CBootProfilerRecord* record)
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CBootProfiler& profiler = CBootProfiler::GetInstance();
|
||||
profiler.StopBlock(record);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSystem::StartBootProfilerSessionFrames([[maybe_unused]] const char* pName)
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CBootProfiler& profiler = CBootProfiler::GetInstance();
|
||||
profiler.StartFrame(pName);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CSystem::StopBootProfilerSessionFrames()
|
||||
{
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
CBootProfiler& profiler = CBootProfiler::GetInstance();
|
||||
profiler.StopFrame();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CSystem::RegisterErrorObserver(IErrorObserver* errorObserver)
|
||||
{
|
||||
return stl::push_back_unique(m_errorObservers, errorObserver);
|
||||
|
||||
@@ -92,13 +92,6 @@ static AZStd::vector<AZStd::string> GetModuleNames()
|
||||
moduleNames.push_back("CryFont" MODULE_EXTENSION);
|
||||
moduleNames.push_back("CrySystem" MODULE_EXTENSION);
|
||||
|
||||
if (gEnv && gEnv->pConsole)
|
||||
{
|
||||
string gameModuleNameRaw = gEnv->pConsole->GetCVar("sys_dll_game")->GetString();
|
||||
gameModuleNameRaw.append(MODULE_EXTENSION);
|
||||
moduleNames.push_back(gameModuleNameRaw.c_str());
|
||||
}
|
||||
|
||||
#undef MODULE_EXTENSION
|
||||
|
||||
# if defined(LINUX)
|
||||
@@ -1076,14 +1069,6 @@ void CSystem::FatalError(const char* format, ...)
|
||||
CryLogAlways("<CrySystem> Last System Error: %s", szSysErrorMessage);
|
||||
}
|
||||
|
||||
if (const char* pLoadingProfilerCallstack = GetLoadingProfilerCallstack())
|
||||
{
|
||||
if (pLoadingProfilerCallstack[0])
|
||||
{
|
||||
CryLogAlways("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetUserCallback())
|
||||
{
|
||||
GetUserCallback()->OnError(szBuffer);
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "CrySystem_precompiled.h"
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Memory/AllocatorScope.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <BootProfiler.h>
|
||||
|
||||
#if defined(ENABLE_LOADING_PROFILER)
|
||||
|
||||
namespace UnitTests
|
||||
{
|
||||
using BootProfilerTestAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator, CryStringAllocator>;
|
||||
class BootProfilerTest :
|
||||
public ::testing::Test,
|
||||
BootProfilerTestAllocatorScope,
|
||||
UnitTest::TraceBusRedirector
|
||||
{
|
||||
public:
|
||||
BootProfilerTest()
|
||||
{
|
||||
BootProfilerTestAllocatorScope::ActivateAllocators();
|
||||
UnitTest::TraceBusRedirector::BusConnect();
|
||||
}
|
||||
|
||||
~BootProfilerTest()
|
||||
{
|
||||
UnitTest::TraceBusRedirector::BusDisconnect();
|
||||
BootProfilerTestAllocatorScope::DeactivateAllocators();
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TEST_F(BootProfilerTest, BootProfilerTest_StartStopBlocksInThreads_Success)
|
||||
{
|
||||
CBootProfiler testProfiler;
|
||||
const char scopeName[] = "TestScope";
|
||||
const char blockArg[] = "TestArg";
|
||||
const int numAttempts = 1000;
|
||||
const int numThreads = 10;
|
||||
|
||||
auto switchSessionFunc = [&]() {
|
||||
for (int sessionNum = 0; sessionNum < numAttempts; ++sessionNum)
|
||||
{
|
||||
auto sessionName = AZStd::string::format("TestSession%d", sessionNum);
|
||||
testProfiler.StartSession(sessionName.c_str());
|
||||
testProfiler.StopSession(sessionName.c_str());
|
||||
}
|
||||
};
|
||||
auto testProfileFunc = [&]() {
|
||||
for (int blockNum = 0; blockNum < numAttempts; ++blockNum)
|
||||
{
|
||||
auto someBlock = testProfiler.StartBlock(scopeName, blockArg);
|
||||
testProfiler.StopBlock(someBlock);
|
||||
}
|
||||
};
|
||||
AZStd::thread threadArray[numThreads];
|
||||
|
||||
AZStd::thread sessionThread = AZStd::thread(switchSessionFunc);
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
threadArray[i] = AZStd::thread(testProfileFunc);
|
||||
}
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
threadArray[i].join();
|
||||
}
|
||||
sessionThread.join();
|
||||
}
|
||||
|
||||
class FrameTestBootProfiler : public CBootProfiler
|
||||
{
|
||||
public:
|
||||
FrameTestBootProfiler(int frameCount) : CBootProfiler()
|
||||
{
|
||||
SetFrameCount(frameCount);
|
||||
}
|
||||
};
|
||||
TEST_F(BootProfilerTest, BootProfilerTest_FrameStartStop_Success)
|
||||
{
|
||||
const int numTestFrames = 10;
|
||||
FrameTestBootProfiler testProfiler(numTestFrames);
|
||||
|
||||
for (int i = 0; i < numTestFrames; ++i)
|
||||
{
|
||||
testProfiler.StartFrame("TestFrame");
|
||||
|
||||
testProfiler.StopFrame();
|
||||
}
|
||||
}
|
||||
} // namespace UnitTests
|
||||
|
||||
#endif
|
||||
@@ -75,15 +75,10 @@ TEST(CrySystemMaterialUtilsTests, MaterialUtilsTestPrefixes)
|
||||
TEST(CrySystemMaterialUtilsTests, MaterialUtilsTestGameName)
|
||||
{
|
||||
char tempBuffer[AZ_MAX_PATH_LEN];
|
||||
|
||||
ICVar* pGameNameCVar = nullptr;
|
||||
if ((gEnv)&&(gEnv->pConsole))
|
||||
{
|
||||
pGameNameCVar = gEnv->pConsole->GetCVar("sys_game_folder");
|
||||
}
|
||||
|
||||
azsnprintf(tempBuffer, AZ_MAX_PATH_LEN, ".\\%s\\materials\\blahblah.mat.mat.abc.test", pGameNameCVar ? pGameNameCVar->GetString() : "SamplesProject");
|
||||
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
azsnprintf(tempBuffer, AZ_MAX_PATH_LEN, ".\\%s\\materials\\blahblah.mat.mat.abc.test", projectName.c_str());
|
||||
|
||||
MaterialUtils::UnifyMaterialName(tempBuffer);
|
||||
EXPECT_TRUE(strcmp(tempBuffer, "materials/blahblah.mat.mat.abc") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace CryPakUnitTests
|
||||
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
||||
ASSERT_NE(nullptr, fileIo);
|
||||
|
||||
constexpr const char* testPakPath = "@cache@/archivecontainerlevel.pak";
|
||||
constexpr const char* testPakPath = "@usercache@/archivecontainerlevel.pak";
|
||||
|
||||
char resolvedArchivePath[AZ_MAX_PATH_LEN] = { 0 };
|
||||
EXPECT_TRUE(fileIo->ResolvePath(testPakPath, resolvedArchivePath, AZ_MAX_PATH_LEN));
|
||||
@@ -116,7 +116,7 @@ namespace CryPakUnitTests
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 });
|
||||
|
||||
// helper paths and strings
|
||||
AZStd::string gameFolder = fileIo->GetAlias("@cache@");
|
||||
AZStd::string gameFolder = fileIo->GetAlias("@usercache@");
|
||||
|
||||
AZStd::string testFile = "unittest.bin";
|
||||
AZStd::string testFilePath = gameFolder + "\\" + testFile;
|
||||
|
||||
@@ -297,7 +297,7 @@ void CVisRegTest::CaptureSample(const SCmd& cmd)
|
||||
if (m_cmdFreq == 1) // Final sample
|
||||
{
|
||||
// Screenshot
|
||||
stack_string filename("@cache@/TestResults/VisReg/"); // the default unaliased assets folder is read-only!
|
||||
stack_string filename("@usercache@/TestResults/VisReg/"); // the default unaliased assets folder is read-only!
|
||||
filename += m_testName + "/" + cmd.args.c_str();
|
||||
gEnv->pRenderer->ScreenShot(filename);
|
||||
|
||||
@@ -335,7 +335,7 @@ void CVisRegTest::Finish()
|
||||
|
||||
bool CVisRegTest::WriteResults()
|
||||
{
|
||||
stack_string filename("@cache@/TestResults/VisReg/");
|
||||
stack_string filename("@usercache@/TestResults/VisReg/");
|
||||
filename += m_testName + "/visreg_results.xml";
|
||||
|
||||
AZ::IO::HandleType fileHandle = fxopen(filename.c_str(), "wb");
|
||||
|
||||
@@ -26,7 +26,6 @@ set(FILES
|
||||
IDebugCallStack.cpp
|
||||
AsyncPakManager.cpp
|
||||
Log.cpp
|
||||
BootProfiler.cpp
|
||||
SystemRender.cpp
|
||||
NotificationNetwork.cpp
|
||||
PhysRenderer.cpp
|
||||
@@ -91,7 +90,6 @@ set(FILES
|
||||
WindowsConsole.h
|
||||
XConsole.h
|
||||
XConsoleVariable.h
|
||||
BootProfiler.h
|
||||
crash_face.bmp
|
||||
ImageHandler.h
|
||||
ImageHandler.cpp
|
||||
@@ -121,11 +119,9 @@ set(FILES
|
||||
XML/WriteXMLSource.cpp
|
||||
ZipFile.h
|
||||
ZipFileFormat_info.h
|
||||
LoadingProfiler.cpp
|
||||
PerfHUD.cpp
|
||||
ProfileLogSystem.cpp
|
||||
Sampler.cpp
|
||||
LoadingProfiler.h
|
||||
PerfHUD.h
|
||||
ProfileLogSystem.h
|
||||
Sampler.h
|
||||
|
||||
@@ -902,7 +902,7 @@ void ShadersOptimizeHelper(CallableT setupParserBin, const char* logString)
|
||||
{
|
||||
setupParserBin();
|
||||
CryLogAlways("\nStarting shaders optimizing for %s...", logString);
|
||||
AZStd::string str = "@cache@/" + gRenDev->m_cEF.m_ShadersCache;
|
||||
AZStd::string str = "@usercache@/" + gRenDev->m_cEF.m_ShadersCache;
|
||||
iLog->Log("Optimize shader cache folder: '%s'", gRenDev->m_cEF.m_ShadersCache.c_str());
|
||||
gRenDev->m_cEF.mfOptimiseShaders(str.c_str(), false);
|
||||
}
|
||||
@@ -2396,7 +2396,7 @@ void CRenderer::InitRenderer()
|
||||
|
||||
CV_r_ShaderCompilerFolderSuffix = REGISTER_STRING("r_ShaderCompilerFolderSuffix", "", VF_NULL,
|
||||
"Usage: r_ShaderCompilerFolderSuffix suffix \n"
|
||||
"Default is empty. Set to some other value to append this suffix to the sys_game_folder when compiling shaders");
|
||||
"Default is empty. Set to some other value to append this suffix to the project name when compiling shaders");
|
||||
|
||||
{
|
||||
const SFileVersion& ver = gEnv->pSystem->GetFileVersion();
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <AzCore/Socket/AzSocket.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/PlatformId/PlatformId.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Network/SocketConnection.h>
|
||||
#include <AzFramework/Asset/AssetSystemTypes.h>
|
||||
|
||||
@@ -306,23 +307,20 @@ namespace NRemoteCompiler
|
||||
|
||||
m_RequestLineRootFolder = "";
|
||||
|
||||
ICVar* pGameFolder = gEnv->pConsole->GetCVar("sys_game_folder");
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
ICVar* pCompilerFolderSuffix = CRenderer::CV_r_ShaderCompilerFolderSuffix;
|
||||
|
||||
if (pGameFolder)
|
||||
if (!projectName.empty())
|
||||
{
|
||||
string folder = pGameFolder->GetString();
|
||||
folder.Trim();
|
||||
if (!folder.empty())
|
||||
if (pCompilerFolderSuffix)
|
||||
{
|
||||
if (pCompilerFolderSuffix)
|
||||
{
|
||||
string suffix = pCompilerFolderSuffix->GetString();
|
||||
suffix.Trim();
|
||||
folder.append(suffix);
|
||||
}
|
||||
m_RequestLineRootFolder = folder + string("/");
|
||||
string suffix = pCompilerFolderSuffix->GetString();
|
||||
suffix.Trim();
|
||||
projectName.append(suffix);
|
||||
}
|
||||
|
||||
projectName.append("/");
|
||||
m_RequestLineRootFolder.assign(projectName.c_str(), projectName.size());
|
||||
}
|
||||
|
||||
if (m_RequestLineRootFolder.empty())
|
||||
|
||||
@@ -13,16 +13,61 @@
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Memory/AllocatorScope.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
#include "Mocks/IConsoleMock.h"
|
||||
#include "Mocks/ICVarMock.h"
|
||||
#include "Mocks/ISystemMock.h"
|
||||
#include "RemoteCompiler.h"
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class SettingsRegistrySimpleMock;
|
||||
using NiceSettingsRegistrySimpleMock = ::testing::NiceMock<SettingsRegistrySimpleMock>;
|
||||
|
||||
class SettingsRegistrySimpleMock : public AZ::SettingsRegistryInterface
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD1(GetType, Type(AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Visit, bool(Visitor&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Visit, bool(const VisitorCallback&, AZStd::string_view));
|
||||
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(const NotifyCallback&));
|
||||
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(NotifyCallback&&));
|
||||
|
||||
MOCK_CONST_METHOD2(Get, bool(bool&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(s64&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(u64&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(double&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(AZStd::string&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD2(Get, bool(FixedValueString&, AZStd::string_view));
|
||||
MOCK_CONST_METHOD3(GetObject, bool(void*, Uuid, AZStd::string_view));
|
||||
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, bool));
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, s64));
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, u64));
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, double));
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, AZStd::string_view));
|
||||
MOCK_METHOD2(Set, bool(AZStd::string_view, const char*));
|
||||
MOCK_METHOD3(SetObject, bool(AZStd::string_view, const void*, Uuid));
|
||||
|
||||
MOCK_METHOD1(Remove, bool(AZStd::string_view));
|
||||
|
||||
MOCK_METHOD3(MergeCommandLineArgument, bool(AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&));
|
||||
MOCK_METHOD2(MergeSettings, bool(AZStd::string_view, Format));
|
||||
MOCK_METHOD4(MergeSettingsFile, bool(AZStd::string_view, Format, AZStd::string_view, AZStd::vector<char>*));
|
||||
MOCK_METHOD5(
|
||||
MergeSettingsFolder,
|
||||
bool(AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
namespace NRemoteCompiler
|
||||
{
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::DoAll;
|
||||
|
||||
using SystemAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator, CryStringAllocator>;
|
||||
|
||||
@@ -65,9 +110,12 @@ namespace NRemoteCompiler
|
||||
SystemAllocatorScope::ActivateAllocators();
|
||||
|
||||
m_priorEnv = gEnv;
|
||||
m_priorSettingsRegistry = AZ::SettingsRegistry::Get();
|
||||
|
||||
m_data.reset(new DataMembers);
|
||||
|
||||
AZ::SettingsRegistry::Register(&m_data->m_settings);
|
||||
|
||||
ON_CALL(m_data->m_console, GetCVar(_))
|
||||
.WillByDefault(Return(&m_data->m_cvarMock));
|
||||
|
||||
@@ -89,6 +137,11 @@ namespace NRemoteCompiler
|
||||
void TearDown() override
|
||||
{
|
||||
gEnv = m_priorEnv;
|
||||
AZ::SettingsRegistry::Unregister(&m_data->m_settings);
|
||||
if (m_priorSettingsRegistry)
|
||||
{
|
||||
AZ::SettingsRegistry::Register(m_priorSettingsRegistry);
|
||||
}
|
||||
m_data.reset();
|
||||
SystemAllocatorScope::DeactivateAllocators();
|
||||
AllocatorsTestFixture::TearDown();
|
||||
@@ -99,12 +152,14 @@ namespace NRemoteCompiler
|
||||
NiceMock<SystemMock> m_system;
|
||||
NiceMock<ConsoleMock> m_console;
|
||||
NiceMock<CVarMock> m_cvarMock;
|
||||
AZ::NiceSettingsRegistrySimpleMock m_settings;
|
||||
SSystemGlobalEnvironment m_stubEnv;
|
||||
};
|
||||
|
||||
AZStd::unique_ptr<DataMembers> m_data;
|
||||
|
||||
SSystemGlobalEnvironment* m_priorEnv = nullptr;
|
||||
AZ::SettingsRegistryInterface* m_priorSettingsRegistry = nullptr;
|
||||
};
|
||||
|
||||
// allow punch through to PRIVATE functions so that they do not need to be made PUBLIC.
|
||||
@@ -130,7 +185,9 @@ namespace NRemoteCompiler
|
||||
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithNoGameName_Fails)
|
||||
{
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString());
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString regResult;
|
||||
EXPECT_CALL(m_data->m_settings, Get(regResult, _));
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
@@ -140,8 +197,10 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithValidGameName_Succeeds)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
}
|
||||
@@ -149,11 +208,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_EmptyData_Fails)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
std::vector<uint8> testVector;
|
||||
|
||||
@@ -165,11 +225,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidData_EmptyServerList_Fails)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillRepeatedly(Return("")); // empty server list
|
||||
@@ -186,11 +247,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidInputs_Succeeds)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
@@ -207,11 +269,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_EmptyData_Fails)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
@@ -221,7 +284,7 @@ namespace NRemoteCompiler
|
||||
std::vector<uint8> testVector;
|
||||
std::string testString("empty");
|
||||
|
||||
// test for empty data - recvfailed expected (error emitted)
|
||||
// test for empty data - RecvFailed expected (error emitted)
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
testString = "empty";
|
||||
testVector.assign(testString.begin(), testString.end());
|
||||
@@ -232,11 +295,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_IncompleteData_Fails)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
@@ -247,7 +311,7 @@ namespace NRemoteCompiler
|
||||
std::string testString("incomplete");
|
||||
testVector.assign(testString.begin(), testString.end());
|
||||
|
||||
// test for incomplete data - recvfailed expected
|
||||
// test for incomplete data - RecvFailed expected
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
@@ -256,11 +320,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CorruptData_Fails)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
@@ -271,7 +336,7 @@ namespace NRemoteCompiler
|
||||
std::string testString("corrupt");
|
||||
testVector.assign(testString.begin(), testString.end());
|
||||
|
||||
// test for incomplete data - recvfailed expected
|
||||
// test for incomplete data - RecvFailed expected
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
@@ -280,11 +345,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CompileError_Fails_ReturnsText)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
@@ -294,11 +360,11 @@ namespace NRemoteCompiler
|
||||
std::vector<uint8> testVector;
|
||||
std::string testString("corrupt");
|
||||
testVector.assign(testString.begin(), testString.end());
|
||||
// test for an actual compile error - decompressed compile erro rexpected to be attached.
|
||||
// test for an actual compile error - decompressed compile error expected to be attached.
|
||||
testString = "compile_failure";
|
||||
testVector.assign(testString.begin(), testString.end());
|
||||
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESCompileError);
|
||||
// validate hte compile erorr decompressed successfully
|
||||
// validate the compile error decompressed successfully
|
||||
const char* expected_decode = "decompressed_plaintext";
|
||||
EXPECT_EQ(testVector.size(), strlen(expected_decode));
|
||||
EXPECT_EQ(memcmp(testVector.data(), expected_decode, strlen(expected_decode)), 0);
|
||||
@@ -307,11 +373,12 @@ namespace NRemoteCompiler
|
||||
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_ValidInput_Succeeds_ReturnsText)
|
||||
{
|
||||
// when we construct the server it calls get on the game name
|
||||
EXPECT_CALL(m_data->m_cvarMock, GetString())
|
||||
.WillOnce(Return("StarterGame"));
|
||||
using namespace ::testing;
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
|
||||
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
|
||||
|
||||
ShaderSrvUnitTestAccessor srv;
|
||||
|
||||
|
||||
// After this, it will repeatedly call get cvar to get the server address:
|
||||
const char* testList = "10.20.30.40";
|
||||
|
||||
@@ -375,7 +375,7 @@ void CShaderMan::mfInitShadersCacheMissLog()
|
||||
// create valid path
|
||||
gEnv->pCryPak->MakeDir(g_szTestResults);
|
||||
|
||||
m_ShaderCacheMissPath = string("@cache@\\Shaders\\ShaderCacheMisses.txt"); // do we want this here, or maybe in @log@ ?
|
||||
m_ShaderCacheMissPath = string("@usercache@\\Shaders\\ShaderCacheMisses.txt"); // do we want this here, or maybe in @log@ ?
|
||||
|
||||
// load data which is already stored
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
@@ -2175,9 +2175,10 @@ void CShaderMan::mfOptimiseShaders(const char* szFolder, bool bForce)
|
||||
for (i = 0; i < Names.size(); i++)
|
||||
{
|
||||
const char* szName = Names[i].c_str();
|
||||
if (!strncmp(szName, "@cache@/", 7))
|
||||
constexpr AZStd::string_view userCache = "@usercache@/";
|
||||
if (szName == userCache)
|
||||
{
|
||||
szName += 7;
|
||||
szName += userCache.size();
|
||||
}
|
||||
pCache = CHWShader::mfInitCache(szName, NULL, false, 0, false);
|
||||
if (!pCache || !pCache->m_pRes[CACHE_USER])
|
||||
|
||||
@@ -1589,7 +1589,7 @@ void CShaderMan::mfInit (void)
|
||||
#else
|
||||
m_ShadersCache = CONCAT_PATHS(g_shaderCache, "D3D11");
|
||||
#endif
|
||||
m_szCachePath = "@cache@/";
|
||||
m_szCachePath = "@usercache@/";
|
||||
|
||||
if (CRenderer::CV_r_shadersImport == 3)
|
||||
{
|
||||
|
||||
@@ -162,7 +162,7 @@ bool CShaderSerialize::OpenSResource(const char* szName, SSShaderRes* pSR, CSha
|
||||
stack_string szReadOnly = szName;
|
||||
|
||||
// ShaderCacheGen behavior:
|
||||
// CACHE_READONLY is not really used when exporting the .fxb, so we append the @cache@ alias to the relative shader path
|
||||
// CACHE_READONLY is not really used when exporting the .fxb, so we append the @usercache@ alias to the relative shader path
|
||||
// here as well. We cannot just leave this as the relative Shaders/Cache/Foo.fxb value because then it creates a new
|
||||
// file in the asset cache as @assets@/Shaders/Cache/Foo.fxb, which is illegal (since only AP has the authority to write here)
|
||||
// Game runtime behavior:
|
||||
|
||||
@@ -3490,7 +3490,7 @@ void CHWShader_D3D::mfSaveCGFile(const char* scr, const char* path)
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(name, "@cache@/shaders/fxerror/%s(GL%llx)/(LT%x)(RT%llx)/(MD%x)(MDV%x)(PSS%llx)(ST%llx).cg", GetName(), m_pCurInst->m_Ident.m_GLMask, m_pCurInst->m_Ident.m_LightMask, m_pCurInst->m_Ident.m_RTMask, m_pCurInst->m_Ident.m_MDMask, m_pCurInst->m_Ident.m_MDVMask, m_pCurInst->m_Ident.m_pipelineState.opaque, m_pCurInst->m_Ident.m_STMask);
|
||||
sprintf_s(name, "@usercache@/shaders/fxerror/%s(GL%llx)/(LT%x)(RT%llx)/(MD%x)(MDV%x)(PSS%llx)(ST%llx).cg", GetName(), m_pCurInst->m_Ident.m_GLMask, m_pCurInst->m_Ident.m_LightMask, m_pCurInst->m_Ident.m_RTMask, m_pCurInst->m_Ident.m_MDMask, m_pCurInst->m_Ident.m_MDVMask, m_pCurInst->m_Ident.m_pipelineState.opaque, m_pCurInst->m_Ident.m_STMask);
|
||||
}
|
||||
|
||||
AZ::IO::HandleType fileHandle;
|
||||
@@ -4672,7 +4672,7 @@ bool CAsyncShaderTask::CompileAsyncShader(SShaderAsyncInfo* pAsync)
|
||||
CryFixedStringT<1024> hlslPath;
|
||||
|
||||
// Create a directory for this shader type, strip the .fxcb extension from the folder name
|
||||
shaderSourceOutputFolder.Format("@cache@/%s",pAsync->m_pShader->m_pDevCache->m_Name.c_str());
|
||||
shaderSourceOutputFolder.Format("@usercache@/%s",pAsync->m_pShader->m_pDevCache->m_Name.c_str());
|
||||
PathUtil::RemoveExtension(shaderSourceOutputFolder);
|
||||
gEnv->pFileIO->CreatePath(shaderSourceOutputFolder);
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#endif
|
||||
|
||||
#include "../Common/RenderCapabilities.h"
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#ifdef WIN32
|
||||
// Count monitors helper
|
||||
@@ -1634,8 +1635,8 @@ WIN_HWND CD3D9Renderer::Init([[maybe_unused]] int x, [[maybe_unused]] int y, int
|
||||
iLog->Log ("Direct3D driver is creating...");
|
||||
iLog->Log ("Crytek Direct3D driver version %4.2f (%s <%s>)", VERSION_D3D, __DATE__, __TIME__);
|
||||
|
||||
const char* sGameName = iConsole->GetCVar("sys_game_name")->GetString();
|
||||
cry_strcpy(m_WinTitle, sGameName);
|
||||
auto projectName = AZ::Utils::GetProjectName();
|
||||
cry_strcpy(m_WinTitle, projectName.c_str());
|
||||
|
||||
iLog->Log ("Creating window called '%s' (%dx%d)", m_WinTitle, width, height);
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
|
||||
#include <AzCore/Memory/AllocationRecords.h>
|
||||
|
||||
#include <AzCore/Memory/OverrunDetectionAllocator.h>
|
||||
@@ -99,6 +101,18 @@ namespace AZ
|
||||
return environment ? environment->Get() : nullptr;
|
||||
}
|
||||
|
||||
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter() noexcept= default;
|
||||
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter(bool skipDelete) noexcept
|
||||
: m_skipDelete{skipDelete}
|
||||
{}
|
||||
void ComponentApplication::EventLoggerDeleter::operator()(AZ::Debug::LocalFileEventLogger* ptr)
|
||||
{
|
||||
if (!m_skipDelete)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ComponentApplication::Descriptor
|
||||
// [5/30/2012]
|
||||
@@ -159,6 +173,80 @@ namespace AZ
|
||||
return true;
|
||||
};
|
||||
|
||||
//! SettingsRegistry notifier handler which updates relevant registry settings based
|
||||
//! on an update to '/Amazon/AzCore/Bootstrap/project_path' key.
|
||||
struct UpdateProjectSettingsEventHandler
|
||||
{
|
||||
UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry)
|
||||
: m_registry{ registry }
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
UpdateProjectSpecializationInRegistry(path);
|
||||
}
|
||||
|
||||
//! Add the project name as a specialization underneath the /Amazon/AzCore/Settings/Specializations path
|
||||
//! and remove the current project name specialization if one exists.
|
||||
void UpdateProjectSpecializationInRegistry(AZStd::string_view path)
|
||||
{
|
||||
auto projectPathKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/project_path";
|
||||
if (path == projectPathKey)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString newProjectPath;
|
||||
if (m_registry.Get(newProjectPath, path) && !newProjectPath.empty())
|
||||
{
|
||||
// Make the path absolute by appending to app root, in case project path is relative.
|
||||
// If the project path is already absolute it will remain the same.
|
||||
// If we turn it from a relative path to an absolute path, write-back the absolute path to the registry.
|
||||
AZ::IO::FixedMaxPath projectPath = AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath;
|
||||
if (projectPath.Compare(newProjectPath.c_str()))
|
||||
{
|
||||
m_registry.Set(path, projectPath.Native());
|
||||
}
|
||||
|
||||
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
|
||||
AZ::IO::FixedMaxPath projectMetadataFile{ projectPath };
|
||||
projectMetadataFile /= "project.json";
|
||||
m_registry.MergeSettingsFile(projectMetadataFile.Native(),
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
|
||||
|
||||
// Get the 'project_name' value from what was in the 'project.json' file...
|
||||
auto projectNameKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
|
||||
+ "/project_name";
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectSpecialization;
|
||||
if (m_registry.Get(projectSpecialization, projectNameKey))
|
||||
{
|
||||
auto specializationKey = AZ::SettingsRegistryInterface::FixedValueString::format(
|
||||
"%s/%s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, projectSpecialization.c_str());
|
||||
if (m_currentSpecialization != specializationKey)
|
||||
{
|
||||
m_registry.Set(specializationKey, true);
|
||||
if (!m_currentSpecialization.empty())
|
||||
{
|
||||
// Remove the previous Project Name from the specialization path if it was set.
|
||||
m_registry.Remove(m_currentSpecialization);
|
||||
}
|
||||
m_currentSpecialization = specializationKey;
|
||||
|
||||
// Update all the runtime file paths based on the new "project_path" value.
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_currentSpecialization;
|
||||
AZ::SettingsRegistryInterface& m_registry;
|
||||
};
|
||||
|
||||
void ComponentApplication::Descriptor::AllocatorRemapping::Reflect(ReflectContext* context, ComponentApplication* app)
|
||||
{
|
||||
(void)app;
|
||||
@@ -276,6 +364,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
ComponentApplication::ComponentApplication(int argC, char** argV)
|
||||
: m_eventLogger{}
|
||||
{
|
||||
if (argV)
|
||||
{
|
||||
@@ -284,11 +373,23 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
|
||||
azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
|
||||
// use a "valid" value here. This is because Qt and potentially other third party libraries require
|
||||
// that ArgC be 'at least 1' and that (*argV)[0] be a valid pointer to a real null terminated string.
|
||||
m_argC = 1;
|
||||
m_argV = &m_commandLineBufferAddress;
|
||||
m_argC = 1;
|
||||
m_argV = &m_commandLineBufferAddress;
|
||||
}
|
||||
|
||||
// Create the Event logger if it doesn't exist, otherwise reuse the one registered
|
||||
// with the AZ::Interface
|
||||
if (AZ::Interface<AZ::Debug::IEventLogger>::Get() == nullptr)
|
||||
{
|
||||
m_eventLogger.reset(new AZ::Debug::LocalFileEventLogger);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eventLogger = EventLoggerPtr(static_cast<AZ::Debug::LocalFileEventLogger*>(AZ::Interface<AZ::Debug::IEventLogger>::Get()),
|
||||
EventLoggerDeleter{ true });
|
||||
}
|
||||
|
||||
// Initializes the OSAllocator and SystemAllocator as soon as possible
|
||||
@@ -297,6 +398,7 @@ namespace AZ
|
||||
|
||||
// Now that the Allocators are initialized, the Command Line parameters can be parsed
|
||||
m_commandLine.Parse(m_argC, m_argV);
|
||||
ParseCommandLine(m_commandLine);
|
||||
|
||||
// Create the settings registry and register it with the AZ interface system
|
||||
// This is done after the AppRoot has been calculated so that the Bootstrap.cfg
|
||||
@@ -304,62 +406,34 @@ namespace AZ
|
||||
m_settingsRegistry = AZStd::make_unique<SettingsRegistryImpl>();
|
||||
|
||||
// Register the Settings Registry with the AZ Interface if there isn't one registered already
|
||||
if (AZ::SettingsRegistry::Get() == nullptr)
|
||||
if (SettingsRegistry::Get() == nullptr)
|
||||
{
|
||||
SettingsRegistry::Register(m_settingsRegistry.get());
|
||||
}
|
||||
|
||||
// Add the Command Line arguments into the SettingsRegistry
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_StoreCommandLine(*m_settingsRegistry, m_commandLine);
|
||||
SettingsRegistryMergeUtils::StoreCommandLineToRegistry(*m_settingsRegistry, m_commandLine);
|
||||
|
||||
// Merge Command Line arguments
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
|
||||
// Query for the Executable Path using OS specific functions
|
||||
CalculateExecutablePath();
|
||||
|
||||
// Determine the path to the engine
|
||||
CalculateEngineRoot();
|
||||
|
||||
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
|
||||
// for the application root other the application root is found by scanning upwards from the Executable Directory
|
||||
// for a bootstrap.cfg file
|
||||
CalculateAppRoot(nullptr);
|
||||
// for the application root.
|
||||
CalculateAppRoot();
|
||||
|
||||
// Add a notifier to update the /Amazon/AzCore/Settings/Specializations
|
||||
// when the sys_game_folder property changes within the SettingsRegistry
|
||||
|
||||
// currentGameName is bound by value in order to allow the lambda to have a member variable that
|
||||
// can track the current game specialization before it changes
|
||||
AZ::SettingsRegistryInterface::FixedValueString currentGameSpecialization;
|
||||
auto GameProjectChanged = [currentGameSpecialization](AZStd::string_view path, AZ::SettingsRegistryInterface::Type type) mutable
|
||||
{
|
||||
constexpr auto projectKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
|
||||
+ "/sys_game_folder";
|
||||
if (projectKey == path && type == AZ::SettingsRegistryInterface::Type::String)
|
||||
{
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
AZ::SettingsRegistryInterface::FixedValueString newGameName;
|
||||
if (registry && registry->Get(newGameName, path) && !newGameName.empty())
|
||||
{
|
||||
auto specializationKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, newGameName.c_str());
|
||||
|
||||
if (currentGameSpecialization != specializationKey)
|
||||
{
|
||||
registry->Set(specializationKey, true);
|
||||
if (!currentGameSpecialization.empty())
|
||||
{
|
||||
// Remove the previous Game Name from the specialization path if it was set
|
||||
registry->Remove(currentGameSpecialization);
|
||||
}
|
||||
// Update the currentGameSpecialization
|
||||
currentGameSpecialization = specializationKey;
|
||||
|
||||
// Update all the runtime filepaths based on the new "sys_game_folder" value
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
m_gameProjectChangedHandler = m_settingsRegistry->RegisterNotifier(AZStd::move(GameProjectChanged));
|
||||
// when the 'project_path' property changes within the SettingsRegistry
|
||||
m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry });
|
||||
|
||||
// Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created.
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry);
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
|
||||
|
||||
@@ -391,11 +465,11 @@ namespace AZ
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// The m_gameProjectChangedHandler stores an AZStd::function internally
|
||||
// The m_projectChangedHandler stores an AZStd::function internally
|
||||
// which allocates using the AZ SystemAllocator
|
||||
// m_gameProjectChangedHandler is being default value initialized
|
||||
// m_projectChangedHandler is being default value initialized
|
||||
// to clear out the AZStd::function
|
||||
m_gameProjectChangedHandler = {};
|
||||
m_projectChangedHandler = {};
|
||||
|
||||
// Delete the AZ::IConsole if it was created by this application instance
|
||||
if (m_ownsConsole)
|
||||
@@ -412,6 +486,10 @@ namespace AZ
|
||||
}
|
||||
m_settingsRegistry.reset();
|
||||
|
||||
// Set AZ::CommandLine to an empty object to clear out allocated memory before the allocators
|
||||
// are destroyed
|
||||
m_commandLine = {};
|
||||
|
||||
DestroyAllocator();
|
||||
}
|
||||
|
||||
@@ -421,17 +499,6 @@ namespace AZ
|
||||
AZ_Assert(!m_isStarted, "Component application already started!");
|
||||
|
||||
m_startupParameters = startupParameters;
|
||||
// Invokes CalculateAppRoot() again this time with the appRootOverride startup parameter
|
||||
// supplied in order to allow overriding the AppRoot calculated in the constructor
|
||||
if (m_startupParameters.m_appRootOverride)
|
||||
{
|
||||
CalculateAppRoot(m_startupParameters.m_appRootOverride);
|
||||
// Re-check for the bootstrap.cfg file again using the appRoot override and update the file paths
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry);
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
|
||||
}
|
||||
|
||||
m_descriptor = descriptor;
|
||||
|
||||
@@ -461,20 +528,14 @@ namespace AZ
|
||||
void ComponentApplication::CreateCommon()
|
||||
{
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString registryValue;
|
||||
m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage);
|
||||
AZ::IO::FixedMaxPath outputPath{ registryValue };
|
||||
AZ::IO::FixedMaxPath outputPath;
|
||||
m_settingsRegistry->Get(outputPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage);
|
||||
outputPath /= "eventlogger";
|
||||
|
||||
registryValue.clear();
|
||||
|
||||
AZ::IO::FixedMaxPathString baseFileName{ "EventLog" }; // default name
|
||||
if (m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::BuildTargetNameKey))
|
||||
{
|
||||
baseFileName = registryValue;
|
||||
}
|
||||
m_settingsRegistry->Get(baseFileName, AZ::SettingsRegistryMergeUtils::BuildTargetNameKey);
|
||||
|
||||
m_eventLogger.Start(outputPath.c_str(), baseFileName.c_str());
|
||||
m_eventLogger->Start(outputPath.Native(), baseFileName);
|
||||
}
|
||||
|
||||
CreateDrillers();
|
||||
@@ -515,7 +576,9 @@ namespace AZ
|
||||
LoadModules();
|
||||
|
||||
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
|
||||
m_console->ExecuteConfigFile("@root@/user.cfg");
|
||||
AZ::IO::FixedMaxPath platformCachePath;
|
||||
m_settingsRegistry->Get(platformCachePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
|
||||
m_console->ExecuteConfigFile((platformCachePath / "user.cfg").Native());
|
||||
|
||||
// Parse the command line parameters for console commands after modules have loaded
|
||||
m_console->ExecuteCommandLine(m_commandLine);
|
||||
@@ -601,7 +664,7 @@ namespace AZ
|
||||
m_drillerManager = nullptr;
|
||||
}
|
||||
|
||||
m_eventLogger.Stop();
|
||||
m_eventLogger->Stop();
|
||||
|
||||
// Clear the descriptor to deallocate all strings (owned by ModuleDescriptor)
|
||||
m_descriptor = Descriptor();
|
||||
@@ -775,6 +838,46 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::ParseCommandLine(const AZ::CommandLine& commandLine)
|
||||
{
|
||||
struct OptionKeyToRegsetKey
|
||||
{
|
||||
AZStd::string_view m_optionKey;
|
||||
AZStd::string m_regsetKey;
|
||||
};
|
||||
|
||||
// Provide overrides for the engine root, the project root and the project cache root
|
||||
AZStd::array commandOptions = {
|
||||
OptionKeyToRegsetKey{ "engine-path", AZStd::string::format("%s/engine_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
|
||||
OptionKeyToRegsetKey{ "project-path", AZStd::string::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
|
||||
OptionKeyToRegsetKey{ "project-cache-path", AZStd::string::format("%s/project_cache_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) }
|
||||
};
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, commandOptions.size()> overrideArgs;
|
||||
|
||||
for (auto&& [optionKey, regsetKey] : commandOptions)
|
||||
{
|
||||
if (size_t optionCount = commandLine.GetNumSwitchValues(optionKey); optionCount > 0)
|
||||
{
|
||||
// Use the last supplied command option value to override previous values
|
||||
auto overrideArg = AZStd::string::format(R"(--regset="%s=%s")", regsetKey.c_str(),
|
||||
commandLine.GetSwitchValue(optionKey, optionCount - 1).c_str());
|
||||
overrideArgs.emplace_back(AZStd::move(overrideArg));
|
||||
}
|
||||
}
|
||||
|
||||
if (!overrideArgs.empty())
|
||||
{
|
||||
// Dump the input command line, add the additional option overrides
|
||||
// and Parse the new command line into the Component Application command line
|
||||
AZ::CommandLine::ParamContainer commandLineArgs;
|
||||
commandLine.Dump(commandLineArgs);
|
||||
commandLineArgs.insert(commandLineArgs.end(), AZStd::make_move_iterator(overrideArgs.begin()),
|
||||
AZStd::make_move_iterator(overrideArgs.end()));
|
||||
m_commandLine.Parse(commandLineArgs);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
|
||||
{
|
||||
SettingsRegistryInterface::Specializations specializations;
|
||||
@@ -788,14 +891,14 @@ namespace AZ
|
||||
// In development builds apply the developer registry and the command line to allow early overrides. This will
|
||||
// allow developers to override things like default paths or Asset Processor connection settings. Any additional
|
||||
// values will be replaced by later loads, so this step will happen again at the end of loading.
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_DevRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_UserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
|
||||
#endif
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
#if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_DevRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_UserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
|
||||
#endif
|
||||
}
|
||||
@@ -1013,7 +1116,7 @@ namespace AZ
|
||||
struct GemModuleLoadData
|
||||
{
|
||||
AZ::OSString m_gemName;
|
||||
AZ::OSString m_dynamicLibraryPath;
|
||||
AZStd::vector<AZ::OSString> m_dynamicLibraryPaths;
|
||||
bool m_autoLoad{ true };
|
||||
};
|
||||
|
||||
@@ -1069,19 +1172,18 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
|
||||
void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
|
||||
{
|
||||
if (valueName == "Module" && !value.empty())
|
||||
// Remove last path segment and check if the key corresponds to the Modules array
|
||||
AZStd::optional<AZStd::string_view> moduleIndex = AZ::StringFunc::TokenizeLast(path, "/");
|
||||
if (path.ends_with("/Modules"))
|
||||
{
|
||||
// Strip off the Module entry from the path
|
||||
auto moduleKey = AZ::StringFunc::TokenizeLast(path, "/");
|
||||
if (!moduleKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Remove the "Modules" path segment to be at the GemName key
|
||||
AZ::StringFunc::TokenizeLast(path, "/");
|
||||
if (auto moduleLoadData = FindGemModuleEntry(path); moduleLoadData != nullptr)
|
||||
{
|
||||
moduleLoadData->m_dynamicLibraryPath = value;
|
||||
// Just use Json Serialization to load all the array elements
|
||||
moduleLoadData->m_dynamicLibraryPaths.emplace_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1111,30 +1213,41 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
constexpr size_t RegistryKeySize = 64;
|
||||
auto gemModuleKey = AZStd::fixed_string<RegistryKeySize>::format("%s/Gems", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
|
||||
auto gemModuleKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/Gems", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
|
||||
ModuleDescriptorList gemModules;
|
||||
{
|
||||
GemModuleVisitor moduleVisitor;
|
||||
m_settingsRegistry->Visit(moduleVisitor, gemModuleKey);
|
||||
m_settingsRegistry->Visit(moduleVisitor, gemModuleKey);
|
||||
for (GemModuleLoadData& moduleLoadData : moduleVisitor.m_modulesLoadData)
|
||||
{
|
||||
// Add all auto loadable non-asset gems to the list of gem modules to load
|
||||
if (moduleLoadData.m_autoLoad && !moduleLoadData.m_dynamicLibraryPath.empty())
|
||||
if (!moduleLoadData.m_autoLoad)
|
||||
{
|
||||
gemModules.emplace_back(DynamicModuleDescriptor{ AZStd::move(moduleLoadData.m_dynamicLibraryPath) });
|
||||
break;
|
||||
}
|
||||
for (AZ::OSString& dynamicLibraryPath : moduleLoadData.m_dynamicLibraryPaths)
|
||||
{
|
||||
auto CompareDynamicModuleDescriptor = [&dynamicLibraryPath](const DynamicModuleDescriptor& entry)
|
||||
{
|
||||
return entry.m_dynamicLibraryPath.contains(dynamicLibraryPath);
|
||||
};
|
||||
if (auto moduleIter = AZStd::find_if(gemModules.begin(), gemModules.end(), CompareDynamicModuleDescriptor);
|
||||
moduleIter == gemModules.end())
|
||||
{
|
||||
gemModules.emplace_back(DynamicModuleDescriptor{ AZStd::move(dynamicLibraryPath) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The settings registry in the settings registry are prioritized to load before the modules in the ComponetnApplication descriptor
|
||||
// The modules in the settings registry are prioritized to load before the modules in the ComponentApplication descriptor
|
||||
// in the order in which they were found
|
||||
for (auto&& moduleDescriptor : m_descriptor.m_modules)
|
||||
{
|
||||
// Append new dynamic library modules to the descriptor array
|
||||
auto CompareDynamicModuleDescriptor = [&moduleDescriptor](const DynamicModuleDescriptor& entry)
|
||||
{
|
||||
return entry.m_dynamicLibraryPath.find(moduleDescriptor.m_dynamicLibraryPath) != AZStd::string_view::npos;
|
||||
return entry.m_dynamicLibraryPath.contains(moduleDescriptor.m_dynamicLibraryPath);
|
||||
};
|
||||
if (auto foundModuleIter = AZStd::find_if(gemModules.begin(), gemModules.end(), CompareDynamicModuleDescriptor);
|
||||
foundModuleIter == gemModules.end())
|
||||
@@ -1260,17 +1373,8 @@ namespace AZ
|
||||
m_exeDirectory.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
|
||||
void ComponentApplication::CalculateAppRoot(const char* appRootOverride)
|
||||
void ComponentApplication::CalculateAppRoot()
|
||||
{
|
||||
if (appRootOverride)
|
||||
{
|
||||
m_appRoot = appRootOverride;
|
||||
if (!m_appRoot.empty() && !m_appRoot.ends_with(AZ_CORRECT_FILESYSTEM_SEPARATOR))
|
||||
{
|
||||
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (AZStd::optional<AZ::StringFunc::Path::FixedString> appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
|
||||
{
|
||||
m_appRoot = AZStd::move(*appRootPath);
|
||||
@@ -1279,26 +1383,14 @@ namespace AZ
|
||||
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_appRoot = AZ::SettingsRegistryMergeUtils::GetAppRoot(m_settingsRegistry.get()).Native();
|
||||
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CheckEngineMarkerFile
|
||||
//=========================================================================
|
||||
bool ComponentApplication::CheckPathForEngineMarker(const char* fullPath) const
|
||||
void ComponentApplication::CalculateEngineRoot()
|
||||
{
|
||||
static const char* engineMarkerFileName = "engine.json";
|
||||
char engineMarkerFullPathToCheck[AZ_MAX_PATH_LEN] = "";
|
||||
|
||||
azstrcpy(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), fullPath);
|
||||
azstrcat(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), "/");
|
||||
azstrcat(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), engineMarkerFileName);
|
||||
|
||||
return AZ::IO::SystemFile::Exists(engineMarkerFullPathToCheck);
|
||||
if (m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native(); !m_engineRoot.empty())
|
||||
{
|
||||
m_engineRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_COMPONENT_APPLICATION_H
|
||||
#define AZCORE_COMPONENT_APPLICATION_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Debug/ProfileModuleInit.h>
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
#include <AzCore/Memory/AllocationRecords.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
@@ -40,12 +38,15 @@ namespace AZ
|
||||
class IConsole;
|
||||
class Module;
|
||||
class ModuleManager;
|
||||
}
|
||||
namespace AZ::Debug
|
||||
{
|
||||
class DrillerManager;
|
||||
class LocalFileEventLogger;
|
||||
}
|
||||
|
||||
namespace Debug
|
||||
{
|
||||
class DrillerManager;
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectionEnvironment
|
||||
{
|
||||
public:
|
||||
@@ -167,15 +168,6 @@ namespace AZ
|
||||
//! \note Dynamic AZ::Modules are specified in the ComponentApplication::Descriptor.
|
||||
CreateStaticModulesCallback m_createStaticModulesCallback = nullptr;
|
||||
|
||||
//! If set, this is used as the app root folder instead of it being calculated.
|
||||
const char* m_appRootOverride = nullptr;
|
||||
|
||||
//! The path to root of the asset cache folder. For instance: ./cache/<project>/pc
|
||||
const char* m_cacheRootPath = nullptr;
|
||||
|
||||
//! The path to the project in the asset cache folder. For instance: ./cache/<project>/pc/<project>
|
||||
const char* m_cacheProjectPath = nullptr;
|
||||
|
||||
//! Specifies which system components to create & activate. If no tags specified, all system components are used. Specify as comma separated list.
|
||||
const char* m_systemComponentTags = nullptr;
|
||||
|
||||
@@ -226,6 +218,8 @@ namespace AZ
|
||||
/// Returns the working root folder that has been registered with the app, if there is one.
|
||||
/// It's expected that derived applications will implement an application root.
|
||||
const char* GetAppRoot() const override { return m_appRoot.c_str(); }
|
||||
/// Returns the path to the engine.
|
||||
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
|
||||
/// Returns the path to the folder the executable is in.
|
||||
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
|
||||
|
||||
@@ -331,6 +325,9 @@ namespace AZ
|
||||
/// Create the drillers
|
||||
void CreateDrillers();
|
||||
|
||||
/// Parse ComponentApplication specific command line arguments
|
||||
void ParseCommandLine(const AZ::CommandLine& commandLine);
|
||||
|
||||
virtual void MergeSettingsToRegistry(SettingsRegistryInterface& registry);
|
||||
|
||||
//! Sets the specializations that will be used when loading the Settings Registry. Extend this in derived
|
||||
@@ -363,17 +360,11 @@ namespace AZ
|
||||
/// Calculates the directory the application executable comes from.
|
||||
void CalculateExecutablePath();
|
||||
|
||||
/// Calculates the directory where the bootstrap.cfg file resides.
|
||||
void CalculateAppRoot(const char* appRootOverride = {});
|
||||
/// Calculates the root directory of the engine.
|
||||
void CalculateEngineRoot();
|
||||
|
||||
/**
|
||||
* Check/verify a given path for the engine marker (file) so that we can identify that
|
||||
* a given path is the engine root. This is only valid for target platforms that are built
|
||||
* for the host platform and not deployable (ie windows, mac).
|
||||
* @param fullPath The full path to look for the engine marker
|
||||
* @return true if the input path contains the engine marker file, false if not
|
||||
*/
|
||||
virtual bool CheckPathForEngineMarker(const char* fullPath) const;
|
||||
/// Calculates the directory where the bootstrap.cfg file resides.
|
||||
void CalculateAppRoot();
|
||||
|
||||
template<typename Iterator>
|
||||
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
|
||||
@@ -398,10 +389,11 @@ namespace AZ
|
||||
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
|
||||
IAllocatorAllocate* m_osAllocator{ nullptr };
|
||||
EntitySetType m_entities;
|
||||
AZ::StringFunc::Path::FixedString m_exeDirectory;
|
||||
AZ::StringFunc::Path::FixedString m_appRoot;
|
||||
AZ::IO::FixedMaxPathString m_exeDirectory;
|
||||
AZ::IO::FixedMaxPathString m_engineRoot;
|
||||
AZ::IO::FixedMaxPathString m_appRoot;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_gameProjectChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectChangedHandler;
|
||||
|
||||
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
|
||||
// from the m_console member when it goes out of scope
|
||||
@@ -427,9 +419,15 @@ namespace AZ
|
||||
// Created early to allow events to be logged before anything else. These will be kept in memory until
|
||||
// a file is associated with the logger. The internal buffer is limited to 64kb and once full unexpected
|
||||
// behavior may happen. The LocalFileEventLogger will register itself automatically with AZ::Interface<IEventLogger>.
|
||||
AZ::Debug::LocalFileEventLogger m_eventLogger;
|
||||
|
||||
struct EventLoggerDeleter
|
||||
{
|
||||
EventLoggerDeleter() noexcept;
|
||||
EventLoggerDeleter(bool skipDelete) noexcept;
|
||||
void operator()(AZ::Debug::LocalFileEventLogger* ptr);
|
||||
bool m_skipDelete{};
|
||||
};
|
||||
using EventLoggerPtr = AZStd::unique_ptr<AZ::Debug::LocalFileEventLogger, EventLoggerDeleter>;
|
||||
EventLoggerPtr m_eventLogger;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZCORE_COMPONENT_APPLICATION_H
|
||||
#pragma once
|
||||
|
||||
@@ -183,6 +183,11 @@ namespace AZ
|
||||
* @return A pointer to the name of the app's root folder, if a root folder was registered.
|
||||
*/
|
||||
virtual const char* GetAppRoot() const = 0;
|
||||
/**
|
||||
* Gets the path of the working engine folder that the app is a part of.
|
||||
* @return A pointer to the engine path.
|
||||
*/
|
||||
virtual const char* GetEngineRoot() const = 0;
|
||||
/**
|
||||
* Gets the path to the directory that contains the application's executable.
|
||||
* @return A pointer to the name of the path that contains the application's executable.
|
||||
|
||||
@@ -161,9 +161,11 @@ namespace AZ
|
||||
|
||||
void Console::ExecuteCommandLine(const AZ::CommandLine& commandLine)
|
||||
{
|
||||
for (const auto& [switchKey, switchValues] : commandLine.GetSwitchList())
|
||||
for (auto&& commandArgument : commandLine)
|
||||
{
|
||||
ConsoleCommandContainer commandArgs(switchValues.begin(), switchValues.end());
|
||||
const auto& switchKey = commandArgument.m_option;
|
||||
const auto& switchValue = commandArgument.m_value;
|
||||
ConsoleCommandContainer commandArgs{ switchValue };
|
||||
PerformCommand(switchKey, commandArgs, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,25 +67,7 @@ namespace AZ
|
||||
return a != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
inline OpenMode operator | (OpenMode a, OpenMode b)
|
||||
{
|
||||
return static_cast<OpenMode>(static_cast<AZ::u32>(a) | static_cast<AZ::u32>(b));
|
||||
}
|
||||
|
||||
inline OpenMode operator & (OpenMode a, OpenMode b)
|
||||
{
|
||||
return static_cast<OpenMode>(static_cast<AZ::u32>(a) & static_cast<AZ::u32>(b));
|
||||
}
|
||||
|
||||
inline OpenMode& operator |= (OpenMode& a, OpenMode b)
|
||||
{
|
||||
return a = a | b;
|
||||
}
|
||||
|
||||
inline OpenMode& operator &= (OpenMode& a, OpenMode b)
|
||||
{
|
||||
return a = a & b;
|
||||
}
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(OpenMode)
|
||||
|
||||
OpenMode GetOpenModeFromStringMode(const char* mode);
|
||||
|
||||
@@ -250,12 +232,14 @@ namespace AZ
|
||||
virtual bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const = 0;
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> ConvertToAlias(const AZ::IO::PathView& path) const;
|
||||
|
||||
/// ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
|
||||
/// also ensures that the path is absolute
|
||||
/// returns true if path was resolved, false otherwise
|
||||
/// note that all of the above file-finding and opening functions automatically resolve the path before operating
|
||||
/// so you should not need to call this except in very exceptional circumstances where you absolutely need to
|
||||
/// hit a physical file and don't want to use SystemFile
|
||||
//! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
|
||||
//! also ensures that the path is absolute
|
||||
//! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used
|
||||
//! which has the effect of making the path relative to the @assets@/ folder
|
||||
//! returns true if path was resolved, false otherwise
|
||||
//! note that all of the above file-finding and opening functions automatically resolve the path before operating
|
||||
//! so you should not need to call this except in very exceptional circumstances where you absolutely need to
|
||||
//! hit a physical file and don't want to use SystemFile
|
||||
virtual bool ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const = 0;
|
||||
|
||||
//! ResolvePath - Replaces any @ aliases in the supplied path with their the resolved alias values
|
||||
@@ -265,6 +249,11 @@ namespace AZ
|
||||
virtual bool ResolvePath(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path) const = 0;
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> ResolvePath(const AZ::IO::PathView& path) const;
|
||||
|
||||
//! ReplaceAliases - If the path starts with an @...@ alias it is substituted with the alias value
|
||||
//! otherwise the path is copied as is to the resolvedAlias path value
|
||||
//! returns true if the resulting path can fit within AZ::IO::FixedMaxPath buffer
|
||||
virtual bool ReplaceAlias(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path) const = 0;
|
||||
|
||||
/// Divulge the filename used to originally open that handle.
|
||||
virtual bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const = 0;
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace AZ::IO
|
||||
//! Check whether the path is not absolute
|
||||
[[nodiscard]] constexpr bool IsRelative() const;
|
||||
//! Check whether the path is relative to the base path
|
||||
[[nodiscard]] constexpr bool IsRelativeTo(const PathView & base) const;
|
||||
[[nodiscard]] constexpr bool IsRelativeTo(const PathView& base) const;
|
||||
|
||||
//! Normalizes a path in a purely lexical manner.
|
||||
//! # Path separators are converted to their preferred path separator
|
||||
@@ -517,7 +517,7 @@ namespace AZ::IO
|
||||
//! Checks if the path has a root directory
|
||||
[[nodiscard]] constexpr bool HasRootDirectory() const;
|
||||
//! Checks whether the entire root path portion of the path is empty
|
||||
//! The root portion ofthe path is made up of root_name() / root_directory()
|
||||
//! The root portion of the path is made up of root_name() / root_directory()
|
||||
[[nodiscard]] constexpr bool HasRootPath() const;
|
||||
//! checks whether the relative part of path is empty
|
||||
//! (C:\\ lumberyard\dev\)
|
||||
@@ -539,8 +539,8 @@ namespace AZ::IO
|
||||
[[nodiscard]] constexpr bool IsAbsolute() const;
|
||||
//! Check whether the path is not absolute
|
||||
[[nodiscard]] constexpr bool IsRelative() const;
|
||||
//! Check whether the path is relative to the input path
|
||||
[[nodiscard]] constexpr bool IsRelativeTo() const;
|
||||
//! Check whether the path is relative to the base path
|
||||
[[nodiscard]] constexpr bool IsRelativeTo(const PathView& base) const;
|
||||
|
||||
// decomposition
|
||||
//! Given a windows path of "C:\lumberyard\foo\bar\name.txt" and a posix path of
|
||||
|
||||
@@ -1035,7 +1035,7 @@ namespace AZ::IO
|
||||
// move the parser from the end to a valid filename by decrementing
|
||||
for(--pathParserEnd, --patternParserEnd; pathParserEnd && patternParserEnd; --pathParserEnd, --patternParserEnd)
|
||||
{
|
||||
if (!AZStd::wildcard_match(*patternParserEnd, *pathParserEnd))
|
||||
if (!AZStd::wildcard_match_case(*patternParserEnd, *pathParserEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1830,9 +1830,9 @@ namespace AZ::IO
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
[[nodiscard]] constexpr bool BasicPath<StringType>::IsRelativeTo() const
|
||||
[[nodiscard]] constexpr bool BasicPath<StringType>::IsRelativeTo(const PathView& base) const
|
||||
{
|
||||
return static_cast<PathView>(*this).IsRelative();
|
||||
return static_cast<PathView>(*this).IsRelativeTo(base);
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
|
||||
@@ -369,6 +369,7 @@ namespace AZ
|
||||
context.Serializer<JsonVector2Serializer>()->HandlesType<Vector2>();
|
||||
context.Serializer<JsonVector3Serializer>()->HandlesType<Vector3>();
|
||||
context.Serializer<JsonVector4Serializer>()->HandlesType<Vector4>();
|
||||
context.Serializer<JsonQuaternionSerializer>()->HandlesType<Quaternion>();
|
||||
}
|
||||
|
||||
void MathReflect(ReflectContext* context)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Math/MathVectorSerializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
@@ -238,4 +239,56 @@ namespace AZ
|
||||
{
|
||||
return JsonMathVectorSerializerInternal::Store<Vector4, 4>(outputValue, inputValue, defaultValue, valueTypeId, context);
|
||||
}
|
||||
|
||||
// Quaternion
|
||||
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonQuaternionSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result JsonQuaternionSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
|
||||
|
||||
// check for "yaw, pitch, roll" object
|
||||
if (inputValue.IsObject())
|
||||
{
|
||||
if (inputValue.GetObject().ObjectEmpty())
|
||||
{
|
||||
Quaternion* outQuaternion = reinterpret_cast<Quaternion*>(outputValue);
|
||||
*outQuaternion = Quaternion::CreateIdentity();
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity quaternion for empty object.");
|
||||
}
|
||||
|
||||
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
|
||||
if (!floatSerializer)
|
||||
{
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the json float serializer.");
|
||||
}
|
||||
|
||||
constexpr const char* names[3] = {"yaw", "pitch", "roll"};
|
||||
float values[3];
|
||||
int i = 0;
|
||||
for (auto itr = inputValue.MemberBegin(); itr != inputValue.MemberEnd(); ++i, ++itr)
|
||||
{
|
||||
ScopedContextPath subPath(context, names[i]);
|
||||
JSR::Result intermediate = floatSerializer->Load(values + i, azrtti_typeid<float>(), itr->value, context);
|
||||
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
|
||||
{
|
||||
return intermediate;
|
||||
}
|
||||
}
|
||||
|
||||
auto eulerAnglesDegrees = Vector3::CreateFromFloat3(values);
|
||||
reinterpret_cast<Quaternion*>(outputValue)->SetFromEulerDegrees(eulerAnglesDegrees);
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read quaternion.");
|
||||
}
|
||||
|
||||
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result JsonQuaternionSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
|
||||
{
|
||||
return JsonMathVectorSerializerInternal::Store<Quaternion, 4>(outputValue, inputValue, defaultValue, valueTypeId, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,16 @@ namespace AZ
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
|
||||
const Uuid& valueTypeId, JsonSerializerContext& context) override;
|
||||
};
|
||||
|
||||
class JsonQuaternionSerializer
|
||||
: public BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context) override;
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
|
||||
const Uuid& valueTypeId, JsonSerializerContext& context) override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Settings/CommandLine.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/AzCore_Traits_Platform.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/Settings/CommandLine.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -29,6 +29,21 @@ namespace AZ
|
||||
|
||||
return lowerStr;
|
||||
}
|
||||
|
||||
AZStd::string_view UnquoteArgument(AZStd::string_view arg)
|
||||
{
|
||||
if (arg.size() < 2)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
return arg.front() == '"' && arg.back() == '"' ? AZStd::string_view{ arg.begin() + 1, arg.end() - 1 } : arg;
|
||||
}
|
||||
|
||||
AZStd::string QuoteArgument(AZStd::string_view arg)
|
||||
{
|
||||
return !arg.empty() ? AZStd::string::format(R"("%.*s")", aznumeric_cast<int>(arg.size()), arg.data()) : AZStd::string{ arg };
|
||||
}
|
||||
}
|
||||
|
||||
CommandLine::CommandLine()
|
||||
@@ -41,15 +56,62 @@ namespace AZ
|
||||
{
|
||||
}
|
||||
|
||||
void CommandLine::AddArgument(AZStd::string currentArg, AZStd::string& currentSwitch)
|
||||
void CommandLine::ParseOptionArgument(AZStd::string_view newOption, AZStd::string_view newValue,
|
||||
CommandArgument* inProgressArgument)
|
||||
{
|
||||
StringFunc::Strip(currentArg, " ", false, true, true);
|
||||
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
|
||||
AZStd::string_view unquotedValue = UnquoteArgument(newValue);
|
||||
if (unquotedValue != newValue)
|
||||
{
|
||||
// Update the inProgressArgument before adding new arguments
|
||||
if(inProgressArgument)
|
||||
{
|
||||
inProgressArgument->m_value = unquotedValue;
|
||||
// Set the inProgressArgument to nullptr to indicate that the inProgressArgument has been fulfilled
|
||||
inProgressArgument = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_allValues.push_back({ newOption, unquotedValue });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZStd::vector<AZStd::string_view> tokens;
|
||||
auto splitArgument = [&tokens](AZStd::string_view token)
|
||||
{
|
||||
tokens.emplace_back(AZ::StringFunc::StripEnds(token));
|
||||
};
|
||||
AZ::StringFunc::TokenizeVisitor(newValue, splitArgument, ",;");
|
||||
|
||||
// we do it this way because you are allowed to do odd things like
|
||||
// -root=abc -root=hij,klm -root whee -root fun;days
|
||||
// and roots value should be { abc, hij, klm, whee, fun, days }
|
||||
for (AZStd::string_view optionValue : tokens)
|
||||
{
|
||||
if (inProgressArgument)
|
||||
{
|
||||
inProgressArgument->m_value = optionValue;
|
||||
// Set the inProgressArgument to nullptr to indicate that the inProgressArgument has been fulfilled
|
||||
inProgressArgument = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_allValues.push_back({ newOption, optionValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLine::AddArgument(AZStd::string_view currentArg, AZStd::string& currentSwitch)
|
||||
{
|
||||
currentArg = AZ::StringFunc::StripEnds(currentArg);
|
||||
if (!currentArg.empty())
|
||||
{
|
||||
if (AZStd::string_view(currentArg.begin(), currentArg.begin() + 1).find_first_of(m_commandLineOptionPrefix) != AZStd::string_view::npos)
|
||||
if (m_commandLineOptionPrefix.contains(currentArg.front()))
|
||||
{
|
||||
// its possible that its a key-value-pair like /blah=whatever
|
||||
// we support this too, for compatibilty.
|
||||
// its possible that its a key-value-pair like -blah=whatever
|
||||
// we support this too, for compatibility.
|
||||
|
||||
currentArg = currentArg.substr(1);
|
||||
if (currentArg[0] == '-') // for -- extra
|
||||
@@ -57,93 +119,41 @@ namespace AZ
|
||||
currentArg = currentArg.substr(1);
|
||||
}
|
||||
|
||||
AZStd::size_t foundPos = StringFunc::Find(currentArg.c_str(), '=');
|
||||
AZStd::size_t foundPos = AZ::StringFunc::Find(currentArg, "=");
|
||||
if (foundPos != AZStd::string::npos)
|
||||
{
|
||||
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
|
||||
if (currentArg.length() > (foundPos + 2) && currentArg[foundPos + 1] == '"' && currentArg[currentArg.length() - 1] == '"')
|
||||
{
|
||||
AZStd::string argName = currentArg.substr(0, foundPos);
|
||||
argName = ToLower(argName);
|
||||
StringFunc::Strip(argName, " ", false, true, true);
|
||||
m_switches[argName].emplace_back(currentArg.substr(foundPos + 2, currentArg.length() - (foundPos + 2) - 1));
|
||||
currentSwitch.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// its in '=' format
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
StringFunc::Tokenize(currentArg.substr(foundPos + 1).c_str(), tokens, ",;");
|
||||
currentArg.resize(foundPos);
|
||||
currentArg = ToLower(currentArg);
|
||||
StringFunc::Strip(currentArg, " ", false, true, true);
|
||||
m_switches.insert_key(currentArg); // returns pair<iter, bool>
|
||||
// we do it this way because you are allowed to do odd things like
|
||||
// /root=abc /root=hij,klm /root whee /root fun;days
|
||||
// and roots value should be { abc, hij, klm, whee, fun, days }
|
||||
for (AZStd::string& switchValue : tokens)
|
||||
{
|
||||
StringFunc::Strip(switchValue, " ", false, true, true);
|
||||
m_switches[currentArg].push_back(switchValue);
|
||||
}
|
||||
AZStd::string_view argumentView{ currentArg };
|
||||
AZStd::string_view option = AZ::StringFunc::StripEnds(argumentView.substr(0, foundPos));
|
||||
AZStd::string_view value = AZ::StringFunc::StripEnds(argumentView.substr(foundPos + 1));
|
||||
ParseOptionArgument(ToLower(option), value, nullptr);
|
||||
currentSwitch.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// its in this format /switchName switchvalue
|
||||
// its in this format -switchName switchvalue
|
||||
// (no equals)
|
||||
currentSwitch = currentArg;
|
||||
currentSwitch = ToLower(currentSwitch);
|
||||
m_switches.insert_key(currentSwitch);
|
||||
currentSwitch = ToLower(currentArg);
|
||||
m_allValues.push_back({ currentSwitch, "" });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentSwitch.empty())
|
||||
{
|
||||
m_miscValues.push_back(currentArg);
|
||||
m_allValues.push_back({ "", UnquoteArgument(currentArg) });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
|
||||
if (currentArg[0] == '"' && currentArg.length() >= 2 && currentArg[currentArg.length() - 1] == '"')
|
||||
{
|
||||
m_switches[currentSwitch].emplace_back(currentArg.substr(1, currentArg.length() - 2));
|
||||
currentSwitch.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
StringFunc::Tokenize(currentArg.c_str(), tokens, ",;");
|
||||
for (AZStd::string& switchValue : tokens)
|
||||
{
|
||||
StringFunc::Strip(switchValue, " ", false, true, true);
|
||||
m_switches[currentSwitch].push_back(switchValue);
|
||||
}
|
||||
|
||||
ParseOptionArgument(currentSwitch, currentArg, &m_allValues.back());
|
||||
currentSwitch.clear();
|
||||
}
|
||||
|
||||
currentSwitch.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLine::Parse(const ParamContainer& commandLine)
|
||||
{
|
||||
m_switches.clear();
|
||||
m_miscValues.clear();
|
||||
|
||||
AZStd::string currentSwitch;
|
||||
for (int i = 1; i < commandLine.size(); ++i)
|
||||
{
|
||||
AddArgument(commandLine[i], currentSwitch);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLine::Parse(int argc, char** argv)
|
||||
{
|
||||
m_switches.clear();
|
||||
m_miscValues.clear();
|
||||
m_allValues.clear();
|
||||
|
||||
AZStd::string currentSwitch;
|
||||
// Start on 1 because 0 is the executable name
|
||||
@@ -157,91 +167,145 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLine::Dump(ParamContainer& commandLineDumpOutput)
|
||||
void CommandLine::Parse(const ParamContainer& commandLine)
|
||||
{
|
||||
// Push back an empty argument as the Parse function always skips parsing the first argument
|
||||
commandLineDumpOutput.emplace_back(AZStd::string());
|
||||
for (const AZStd::string& miscValue : m_miscValues)
|
||||
{
|
||||
commandLineDumpOutput.push_back(miscValue);
|
||||
}
|
||||
m_allValues.clear();
|
||||
|
||||
if (!m_commandLineOptionPrefix.empty())
|
||||
// This version of Parse does not skip over 0th index
|
||||
AZStd::string currentSwitch;
|
||||
for (int i = 0; i < commandLine.size(); ++i)
|
||||
{
|
||||
for (const auto& [switchKey, switchValues] : m_switches)
|
||||
{
|
||||
AZStd::string prefixSwitchKey = m_commandLineOptionPrefix.front() + switchKey;
|
||||
if (switchValues.empty())
|
||||
{
|
||||
// There are no value for the switch so just push it back of the command line dump output
|
||||
commandLineDumpOutput.emplace_back(prefixSwitchKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& switchValue : switchValues)
|
||||
{
|
||||
commandLineDumpOutput.emplace_back(prefixSwitchKey);
|
||||
commandLineDumpOutput.emplace_back(switchValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddArgument(commandLine[i], currentSwitch);
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
void CommandLine::Dump(ParamContainer& commandLineDumpOutput) const
|
||||
{
|
||||
AZ_Error("CommandLine", !m_commandLineOptionPrefix.empty(),
|
||||
"Cannot dump command line switches from a command line with an empty option prefix");
|
||||
|
||||
for (const CommandArgument& argument : m_allValues)
|
||||
{
|
||||
AZ_Error("CommandLine", false, "Cannot dump command line switches from a command line with an empty option prefix");
|
||||
if (!argument.m_option.empty())
|
||||
{
|
||||
commandLineDumpOutput.emplace_back(m_commandLineOptionPrefix.front() + argument.m_option);
|
||||
}
|
||||
if (!argument.m_value.empty())
|
||||
{
|
||||
commandLineDumpOutput.emplace_back(QuoteArgument(argument.m_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandLine::HasSwitch(AZStd::string_view switchName) const
|
||||
{
|
||||
return m_switches.find(ToLower(switchName)) != m_switches.end();
|
||||
auto commandArgumentIter = AZStd::find_if(m_allValues.begin(), m_allValues.end(),
|
||||
[optionName = ToLower(switchName)](const CommandArgument& argument) { return argument.m_option == optionName; });
|
||||
return commandArgumentIter != m_allValues.end();
|
||||
}
|
||||
|
||||
AZStd::size_t CommandLine::GetNumSwitchValues(AZStd::string_view switchName) const
|
||||
{
|
||||
ParamMap::const_iterator switchFound = m_switches.find(ToLower(switchName));
|
||||
if (switchFound == m_switches.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return switchFound->second.size();
|
||||
return AZStd::count_if(m_allValues.begin(), m_allValues.end(),
|
||||
[optionName = ToLower(switchName)](const CommandArgument& argument) { return argument.m_option == optionName; });
|
||||
}
|
||||
|
||||
const AZStd::string& CommandLine::GetSwitchValue(AZStd::string_view switchName, AZStd::size_t index) const
|
||||
{
|
||||
ParamMap::const_iterator switchFound = m_switches.find(ToLower(switchName));
|
||||
if (switchFound == m_switches.end())
|
||||
AZStd::string optionName = ToLower(switchName);
|
||||
size_t currentPosIndex{};
|
||||
auto findArgumentAt = [&optionName, index, ¤tPosIndex](const CommandArgument& argument)
|
||||
{
|
||||
if (argument.m_option == optionName)
|
||||
{
|
||||
return currentPosIndex++ == index;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
auto commandArgumentIter = AZStd::find_if(m_allValues.begin(), m_allValues.end(), findArgumentAt);
|
||||
if (!optionName.empty())
|
||||
{
|
||||
AZ_Assert(index < currentPosIndex, R"(Invalid Command line optional argument lookup of "%s" at index %zu)",
|
||||
optionName.c_str(), index);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(index < currentPosIndex, R"(Invalid Command line positional argument lookup at index %zu)", index);
|
||||
}
|
||||
if (commandArgumentIter == m_allValues.end())
|
||||
{
|
||||
return m_emptyValue;
|
||||
}
|
||||
|
||||
AZ_Assert(index < switchFound->second.size(), "Invalid Command line switch lookup");
|
||||
if (index >= switchFound->second.size())
|
||||
{
|
||||
return m_emptyValue;
|
||||
}
|
||||
|
||||
return switchFound->second.at(index);
|
||||
return commandArgumentIter->m_value;
|
||||
}
|
||||
|
||||
AZStd::size_t CommandLine::GetNumMiscValues() const
|
||||
{
|
||||
return m_miscValues.size();
|
||||
return GetNumSwitchValues("");
|
||||
}
|
||||
|
||||
const AZStd::string& CommandLine::GetMiscValue(AZStd::size_t index) const
|
||||
{
|
||||
AZ_Assert(index < m_miscValues.size(), "Invalid Command line lookup");
|
||||
if (index >= m_miscValues.size())
|
||||
{
|
||||
return m_emptyValue;
|
||||
}
|
||||
return m_miscValues[index];
|
||||
// Positional arguments option value is an empty string
|
||||
return GetSwitchValue("", index);
|
||||
}
|
||||
|
||||
const CommandLine::ParamMap& CommandLine::GetSwitchList() const
|
||||
[[nodiscard]] bool CommandLine::empty() const
|
||||
{
|
||||
return m_switches;
|
||||
return m_allValues.empty();
|
||||
}
|
||||
|
||||
auto CommandLine::size() const -> ArgumentVector::size_type
|
||||
{
|
||||
return m_allValues.size();
|
||||
}
|
||||
auto CommandLine::begin() -> ArgumentVector::iterator
|
||||
{
|
||||
return m_allValues.begin();
|
||||
}
|
||||
auto CommandLine::begin() const -> ArgumentVector::const_iterator
|
||||
{
|
||||
return m_allValues.begin();
|
||||
}
|
||||
auto CommandLine::cbegin() const -> ArgumentVector::const_iterator
|
||||
{
|
||||
return m_allValues.cbegin();
|
||||
}
|
||||
auto CommandLine::end() -> ArgumentVector::iterator
|
||||
{
|
||||
return m_allValues.end();
|
||||
}
|
||||
auto CommandLine::end() const -> ArgumentVector::const_iterator
|
||||
{
|
||||
return m_allValues.end();
|
||||
}
|
||||
auto CommandLine::cend() const -> ArgumentVector::const_iterator
|
||||
{
|
||||
return m_allValues.cend();
|
||||
}
|
||||
auto CommandLine::rbegin() -> ArgumentVector::reverse_iterator
|
||||
{
|
||||
return m_allValues.rbegin();
|
||||
}
|
||||
auto CommandLine::rbegin() const -> ArgumentVector::const_reverse_iterator
|
||||
{
|
||||
return m_allValues.rbegin();
|
||||
}
|
||||
auto CommandLine::crbegin() const -> ArgumentVector::const_reverse_iterator
|
||||
{
|
||||
return m_allValues.crbegin();
|
||||
}
|
||||
auto CommandLine::rend() -> ArgumentVector::reverse_iterator
|
||||
{
|
||||
return m_allValues.rend();
|
||||
}
|
||||
auto CommandLine::rend() const -> ArgumentVector::const_reverse_iterator
|
||||
{
|
||||
return m_allValues.rend();
|
||||
}
|
||||
auto CommandLine::crend() const -> ArgumentVector::const_reverse_iterator
|
||||
{
|
||||
return m_allValues.crend();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,36 +36,47 @@ namespace AZ
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(CommandLine, AZ::SystemAllocator, 0);
|
||||
|
||||
using ParamContainer = AZStd::vector<AZStd::string>;
|
||||
|
||||
struct CommandArgument
|
||||
{
|
||||
AZStd::string m_option;
|
||||
AZStd::string m_value;
|
||||
};
|
||||
using ArgumentVector = AZStd::vector<CommandArgument>;
|
||||
|
||||
CommandLine();
|
||||
|
||||
/**
|
||||
* Initializes a CommandLine instance which uses the provided commandLineOptionPreix for parsing switches
|
||||
*/
|
||||
CommandLine(AZStd::string_view commandLineOptionPrefix);
|
||||
|
||||
using ParamContainer = AZStd::vector<AZStd::string>;
|
||||
using ParamMap = AZStd::unordered_map<AZStd::string, ParamContainer>;
|
||||
/**
|
||||
* Construct a command line parser.
|
||||
* It will load parameters from the given ARGC/ARGV parameters instead of process command line.
|
||||
* Skips over the first parameter as it assumes it is the executable name
|
||||
*/
|
||||
void Parse(int argc, char** argv);
|
||||
/**
|
||||
* Parses each element of the command line as parameter.
|
||||
* Unlike the ARGC/ARGV version above, this function doesn't skip over the first parameter
|
||||
* It allows for round trip conversion with the Dump() method
|
||||
*/
|
||||
void Parse(const ParamContainer& commandLine);
|
||||
|
||||
/**
|
||||
* Will dump command line parameters from the CommandLine in a format such that switches
|
||||
* are prefixed with the option prefix followed by their value(s) which are comma separated
|
||||
* The result of this function can be supplied to Parse() to re-create an eqiuvlanet command line obect
|
||||
* The result of this function can be supplied to Parse() to re-create an equivalent command line object
|
||||
* Ex, If the command line has the current list of parsed miscellaneous values and switches of
|
||||
* MiscValue = ["Foo", "Bat"]
|
||||
* Switches = ["GameFolder" : [], "RemoteIp" : ["10.0.0.1"], "ScanFolders" : ["\a\b\c", "\d\e\f"]
|
||||
* CommandLineOptionPrefix = "-/"
|
||||
*
|
||||
* Then the resulting dumped value would be
|
||||
* Dump = ["", "Foo", "Bat", "-GameFolder", "-RemoteIp", "10.0.0.1", "-ScanFolders", "\a\b\c", "-ScanFolders", "\d\e\f"]
|
||||
* NOTE: The first parameter is always empty string as the Parse function skips over it
|
||||
* Dump = ["Foo", "Bat", "-GameFolder", "-RemoteIp", "10.0.0.1", "-ScanFolders", "\a\b\c", "-ScanFolders", "\d\e\f"]
|
||||
*/
|
||||
void Dump(ParamContainer& commandLineDumpOutput);
|
||||
void Dump(ParamContainer& commandLineDumpOutput) const;
|
||||
|
||||
/**
|
||||
* Determines whether a switch is present in the command line
|
||||
@@ -97,16 +108,28 @@ namespace AZ
|
||||
*/
|
||||
const AZStd::string& GetMiscValue(AZStd::size_t index) const;
|
||||
|
||||
/*
|
||||
* Return the list of parsed switches
|
||||
*/
|
||||
const ParamMap& GetSwitchList() const;
|
||||
|
||||
// Range accessors
|
||||
[[nodiscard]] bool empty() const;
|
||||
auto size() const -> ArgumentVector::size_type;
|
||||
auto begin() -> ArgumentVector::iterator;
|
||||
auto begin() const -> ArgumentVector::const_iterator;
|
||||
auto cbegin() const -> ArgumentVector::const_iterator;
|
||||
auto end() -> ArgumentVector::iterator;
|
||||
auto end() const -> ArgumentVector::const_iterator;
|
||||
auto cend() const -> ArgumentVector::const_iterator;
|
||||
auto rbegin() -> ArgumentVector::reverse_iterator;
|
||||
auto rbegin() const -> ArgumentVector::const_reverse_iterator;
|
||||
auto crbegin() const -> ArgumentVector::const_reverse_iterator;
|
||||
auto rend() -> ArgumentVector::reverse_iterator;
|
||||
auto rend() const -> ArgumentVector::const_reverse_iterator;
|
||||
auto crend() const -> ArgumentVector::const_reverse_iterator;
|
||||
|
||||
private:
|
||||
void AddArgument(AZStd::string currentArg, AZStd::string& currentSwitch);
|
||||
void AddArgument(AZStd::string_view currentArg, AZStd::string& currentSwitch);
|
||||
void ParseOptionArgument(AZStd::string_view newOption, AZStd::string_view newValue, CommandArgument* inProgressArgument);
|
||||
|
||||
ParamMap m_switches;
|
||||
ParamContainer m_miscValues;
|
||||
ArgumentVector m_allValues;
|
||||
AZStd::string m_emptyValue;
|
||||
|
||||
inline static constexpr size_t MaxCommandOptionPrefixes = 8;
|
||||
|
||||
@@ -173,7 +173,15 @@ namespace AZ
|
||||
path.remove_prefix(1); // Remove the leading slash as the StackedString will add this back in.
|
||||
jsonPath.Push(path);
|
||||
}
|
||||
Visit(visitor, jsonPath, "", *value);
|
||||
// Extract the last token of the JSON pointer to use as the valueName
|
||||
AZStd::string_view valueName;
|
||||
size_t pointerTokenCount = pointer.GetTokenCount();
|
||||
if (pointerTokenCount > 0)
|
||||
{
|
||||
const rapidjson::Pointer::Token& lastToken = pointer.GetTokens()[pointerTokenCount - 1];
|
||||
valueName = AZStd::string_view(lastToken.name, lastToken.length);
|
||||
}
|
||||
Visit(visitor, jsonPath, valueName, *value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,68 +41,197 @@ namespace AZ::Internal
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject(
|
||||
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
|
||||
{
|
||||
// projectPath needs to be an absolute path here.
|
||||
using namespace AZ::SettingsRegistryMergeUtils;
|
||||
bool projectJsonMerged = false;
|
||||
auto projectJsonPath = projectPath / "project.json";
|
||||
if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
|
||||
{
|
||||
projectJsonMerged = settingsRegistry.MergeSettingsFile(
|
||||
projectJsonPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ProjectSettingsRootKey);
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString engineMoniker;
|
||||
if (projectJsonMerged)
|
||||
{
|
||||
// In project.json look for the "engine" key.
|
||||
auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine", ProjectSettingsRootKey);
|
||||
settingsRegistry.Get(engineMoniker, engineMonikerKey);
|
||||
}
|
||||
|
||||
return engineMoniker;
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath ReconcileEngineRootFromProjectPath(SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
|
||||
{
|
||||
// Find the engine root via the engine manifest file and project.json
|
||||
// Locate the engine manifest file and merge it to settings registry.
|
||||
// Visit over the engine paths list and merge the engine.json files to settings registry.
|
||||
// Merge project.json to settings registry. That will give us an "engine" key.
|
||||
// When we find a match for "engine_name" value against the "engine" value from before, we can stop and use that engine root.
|
||||
// Finally set the BootstrapSettingsRootKey/engine_path setting so that subsequent calls to GetEngineRoot will use that
|
||||
// and avoid all this logic.
|
||||
|
||||
using namespace AZ::SettingsRegistryMergeUtils;
|
||||
|
||||
AZ::IO::FixedMaxPath engineRoot;
|
||||
if (auto engineManifestPath = AZ::Utils::GetEngineManifestPath(); !engineManifestPath.empty())
|
||||
{
|
||||
bool manifestLoaded{false};
|
||||
|
||||
if (AZ::IO::SystemFile::Exists(engineManifestPath.c_str()))
|
||||
{
|
||||
manifestLoaded = settingsRegistry.MergeSettingsFile(
|
||||
engineManifestPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineManifestRootKey);
|
||||
}
|
||||
|
||||
struct EngineInfo
|
||||
{
|
||||
AZ::IO::FixedMaxPath m_path;
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_moniker;
|
||||
};
|
||||
|
||||
struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
void Visit(
|
||||
[[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
|
||||
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
|
||||
{
|
||||
m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}});
|
||||
}
|
||||
|
||||
AZStd::vector<EngineInfo> m_enginePaths{};
|
||||
};
|
||||
|
||||
EnginePathsVisitor pathVisitor;
|
||||
if (manifestLoaded)
|
||||
{
|
||||
auto enginePathsKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engines", EngineManifestRootKey);
|
||||
settingsRegistry.Visit(pathVisitor, enginePathsKey);
|
||||
}
|
||||
|
||||
const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
|
||||
|
||||
for (EngineInfo& engineInfo : pathVisitor.m_enginePaths)
|
||||
{
|
||||
AZ::IO::FixedMaxPath engineSettingsPath{engineInfo.m_path};
|
||||
engineSettingsPath /= "engine.json";
|
||||
|
||||
if (AZ::IO::SystemFile::Exists(engineSettingsPath.c_str()))
|
||||
{
|
||||
if (settingsRegistry.MergeSettingsFile(
|
||||
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
|
||||
{
|
||||
settingsRegistry.Get(engineInfo.m_moniker, engineMonikerKey);
|
||||
}
|
||||
}
|
||||
|
||||
auto engineMoniker = Internal::GetEngineMonikerForProject(settingsRegistry, engineInfo.m_path / projectPath);
|
||||
if (!engineMoniker.empty() && engineMoniker == engineInfo.m_moniker)
|
||||
{
|
||||
engineRoot = engineInfo.m_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return engineRoot;
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath ScanUpRootLocator(AZStd::string_view rootFileToLocate)
|
||||
{
|
||||
|
||||
AZStd::fixed_string<AZ::IO::MaxPathLength> executableDir;
|
||||
if (Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity()) == Utils::ExecutablePathResult::Success)
|
||||
{
|
||||
// Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string
|
||||
// stored within it
|
||||
executableDir.resize_no_construct(AZStd::char_traits<char>::length(executableDir.data()));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath engineRootCandidate{ executableDir };
|
||||
|
||||
bool rootPathVisited = false;
|
||||
do
|
||||
{
|
||||
if (AZ::IO::SystemFile::Exists((engineRootCandidate / rootFileToLocate).c_str()))
|
||||
{
|
||||
return engineRootCandidate;
|
||||
}
|
||||
|
||||
// Note for posix filesystems the parent directory of '/' is '/' and for windows
|
||||
// the parent directory of 'C:\\' is 'C:\\'
|
||||
|
||||
// Validate that the parent directory isn't itself, that would imply
|
||||
// that it is the filesystem root path
|
||||
AZ::IO::PathView parentPath = engineRootCandidate.ParentPath();
|
||||
rootPathVisited = (engineRootCandidate == parentPath);
|
||||
// Recurse upwards one directory
|
||||
engineRootCandidate = AZStd::move(parentPath);
|
||||
|
||||
} while (!rootPathVisited);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace AZ::Internal
|
||||
|
||||
namespace AZ::SettingsRegistryMergeUtils
|
||||
{
|
||||
AZ::IO::FixedMaxPath GetAppRoot(SettingsRegistryInterface* settingsRegistry)
|
||||
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry)
|
||||
{
|
||||
constexpr size_t MaxAppRootPathsToScan = 16;
|
||||
AZStd::fixed_vector<AZStd::string_view, MaxAppRootPathsToScan> appRootScanPaths;
|
||||
AZ::IO::FixedMaxPath engineRoot;
|
||||
|
||||
// Check the commandLine for --app-root parameter
|
||||
// If it exist use that instead
|
||||
auto appRootOverrideKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/app-root", AZ::SettingsRegistryMergeUtils::CommandLineSwitchRootKey);
|
||||
if (settingsRegistry)
|
||||
// This is the 'external' engine root key, as in passed from command-line or .setreg files.
|
||||
auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey);
|
||||
if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty())
|
||||
{
|
||||
AZ::IO::FixedMaxPath appRootOverridePath;
|
||||
if (settingsRegistry->Get(appRootOverridePath.Native(), appRootOverrideKey))
|
||||
{
|
||||
return appRootOverridePath;
|
||||
}
|
||||
return engineRoot;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<AZ::IO::MaxPathLength>> appRootPath = Utils::GetDefaultAppRootPath();
|
||||
if (appRootPath.has_value())
|
||||
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
|
||||
if (engineRoot = Internal::ScanUpRootLocator("engine.json"); !engineRoot.empty())
|
||||
{
|
||||
appRootScanPaths.push_back(*appRootPath);
|
||||
settingsRegistry.Set(engineRootKey, engineRoot.c_str());
|
||||
return engineRoot;
|
||||
}
|
||||
AZStd::fixed_string<AZ::IO::MaxPathLength> executableDir;
|
||||
AZ::Utils::ExecutablePathResult pathResult = Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity());
|
||||
if (pathResult == Utils::ExecutablePathResult::Success)
|
||||
|
||||
AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry);
|
||||
if (projectRoot.empty())
|
||||
{
|
||||
// Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string stored within it
|
||||
executableDir.resize_no_construct(AZStd::char_traits<char>::length(executableDir.data()));
|
||||
if (auto foundIt = AZStd::find(appRootScanPaths.begin(), appRootScanPaths.end(), executableDir);
|
||||
foundIt == appRootScanPaths.end())
|
||||
{
|
||||
appRootScanPaths.push_back(executableDir);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
for (AZStd::string_view appRootScanPath : appRootScanPaths)
|
||||
|
||||
// Use the project.json and engine manifest to locate the engine root.
|
||||
if (engineRoot = Internal::ReconcileEngineRootFromProjectPath(settingsRegistry, projectRoot); !engineRoot.empty())
|
||||
{
|
||||
AZ::IO::FixedMaxPath appRootCandidate{ appRootScanPath };
|
||||
|
||||
// Search for the application root
|
||||
bool rootPathVisited = false;
|
||||
do
|
||||
{
|
||||
if (AZ::IO::SystemFile::Exists((appRootCandidate / "bootstrap.cfg").c_str()))
|
||||
{
|
||||
return appRootCandidate;
|
||||
}
|
||||
|
||||
// Validate that the parent directory isn't itself, that would imply
|
||||
// that it is the root path
|
||||
AZ::IO::PathView parentPath = appRootCandidate.ParentPath();
|
||||
rootPathVisited = appRootCandidate == parentPath;
|
||||
// Recurse upwards one directory
|
||||
appRootCandidate = AZStd::move(parentPath);
|
||||
|
||||
} while (!rootPathVisited);
|
||||
// Note for posix filesystems the parent directory of '/' is '/' and for windows
|
||||
// the parent directory of 'C:\\' is 'C:\\'
|
||||
settingsRegistry.Set(engineRootKey, engineRoot.c_str());
|
||||
return engineRoot;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
|
||||
{
|
||||
AZ::IO::FixedMaxPath projectRoot;
|
||||
// This is the 'external' project root key, as in passed from command-line or .setreg files.
|
||||
auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
|
||||
if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
|
||||
{
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
if (projectRoot = Internal::ScanUpRootLocator("project.json"); !projectRoot.empty())
|
||||
{
|
||||
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -111,9 +240,9 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
constexpr AZStd::string_view commentPrefixes = ";#";
|
||||
for (char commentPrefix : commentPrefixes)
|
||||
{
|
||||
if (line.starts_with(commentPrefix))
|
||||
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
|
||||
{
|
||||
return {};
|
||||
return line.substr(0, commentOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +252,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
AZStd::string_view ConfigParserSettings::DefaultSectionHeaderFilter(AZStd::string_view line)
|
||||
{
|
||||
AZStd::string_view sectionName;
|
||||
constexpr char sectionHeaderStart= '[';
|
||||
constexpr char sectionHeaderStart = '[';
|
||||
constexpr char sectionHeaderEnd = ']';
|
||||
if (line.starts_with(sectionHeaderStart) && line.ends_with(sectionHeaderEnd))
|
||||
{
|
||||
@@ -184,7 +313,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
void QuerySpecializationsFromRegistry(SettingsRegistryInterface& registry, SettingsRegistryInterface::Specializations& specializations)
|
||||
{
|
||||
// Append any specializations stored in the registry
|
||||
// Append any specializations stored in the registry
|
||||
struct SpecializationsVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
@@ -222,7 +351,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath,
|
||||
const ConfigParserSettings& configParserSettings)
|
||||
{
|
||||
auto configPath = GetAppRoot(®istry) / filePath;
|
||||
auto configPath = FindEngineRoot(registry) / filePath;
|
||||
IO::SystemFile configFile;
|
||||
if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
{
|
||||
@@ -348,9 +477,13 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
ConfigParserSettings parserSettings;
|
||||
parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view
|
||||
{
|
||||
if (line.starts_with("--") || line.starts_with(';') || line.starts_with('#'))
|
||||
constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" };
|
||||
for (AZStd::string_view commentPrefix : commentPrefixes)
|
||||
{
|
||||
return {};
|
||||
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
|
||||
{
|
||||
return line.substr(0, commentOffset);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
};
|
||||
@@ -365,71 +498,104 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
|
||||
|
||||
// Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
|
||||
AZ::IO::FixedMaxPath appRoot = GetAppRoot(®istry);
|
||||
registry.Set(FilePathKey_EngineRootFolder, appRoot.LexicallyNormal().Native());
|
||||
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
|
||||
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
|
||||
|
||||
constexpr size_t bufferSize = 64;
|
||||
auto buffer = AZStd::fixed_string<bufferSize>::format("%s/sys_game_folder", BootstrapSettingsRootKey);
|
||||
auto buffer = AZStd::fixed_string<bufferSize>::format("%s/project_path", BootstrapSettingsRootKey);
|
||||
|
||||
AZStd::string_view projectPath(buffer);
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectPathKey(buffer);
|
||||
SettingsRegistryInterface::FixedValueString projectPathValue;
|
||||
if (registry.Get(projectPathValue, projectPath))
|
||||
if (registry.Get(projectPathValue, projectPathKey))
|
||||
{
|
||||
// Cache folder
|
||||
// Get the name of the asset platform assigned by the bootstrap. First check for platform version such as "windows_assets"
|
||||
// and if that's missing just get "asset".
|
||||
// and if that's missing just get "assets".
|
||||
constexpr char platformName[] = AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER;
|
||||
|
||||
SettingsRegistryInterface::FixedValueString assetPlatform;
|
||||
buffer = AZStd::fixed_string<bufferSize>::format("%s/%s_assets", BootstrapSettingsRootKey, platformName);
|
||||
AZStd::string_view assetPlatformatPath(buffer);
|
||||
if (!registry.Get(assetPlatform, assetPlatformatPath))
|
||||
AZStd::string_view assetPlatformKey(buffer);
|
||||
if (!registry.Get(assetPlatform, assetPlatformKey))
|
||||
{
|
||||
buffer = AZStd::fixed_string<bufferSize>::format("%s/assets", BootstrapSettingsRootKey);
|
||||
assetPlatformatPath = AZStd::string_view(buffer);
|
||||
if (!registry.Get(assetPlatform, assetPlatformatPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
assetPlatformKey = AZStd::string_view(buffer);
|
||||
registry.Get(assetPlatform, assetPlatformKey);
|
||||
}
|
||||
|
||||
// Source game folder - corresponds to the @devassets@ alias
|
||||
path = appRoot / projectPathValue;
|
||||
// Project path - corresponds to the @devassets@ alias
|
||||
// NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
|
||||
path = engineRoot / projectPathValue;
|
||||
|
||||
AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(path.c_str()),
|
||||
R"(Project Path "%s" does not exist. Is the "sys_game_folder" entry set to valid project folder?)"
|
||||
, path.c_str());
|
||||
registry.Set(FilePathKey_SourceGameFolder, path.LexicallyNormal().Native());
|
||||
R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to valid absolute path?)"
|
||||
, path.c_str(), aznumeric_cast<int>(projectPathKey.size()), projectPathKey.data());
|
||||
|
||||
// Source game name - The filename of the project directory and the name of the project
|
||||
registry.Set(FilePathKey_SourceGameName, path.Filename().Native());
|
||||
AZ::IO::FixedMaxPath normalizedProjectPath = path.LexicallyNormal();
|
||||
registry.Set(FilePathKey_ProjectPath, normalizedProjectPath.Native());
|
||||
|
||||
// Cache root folder - corresponds to the @root@ alias
|
||||
#if AZ_TRAIT_USE_ASSET_CACHE_FOLDER
|
||||
path = appRoot / "Cache" / projectPathValue / assetPlatform;
|
||||
#else
|
||||
// Use the Engine Root/App Root as the Asset root directory in this case
|
||||
path = appRoot;
|
||||
#endif
|
||||
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
|
||||
|
||||
// check for a default write storage path, fall back to the cache root if not
|
||||
// Add an alias to the project "user" directory
|
||||
AZ::IO::FixedMaxPath projectUserPath = (normalizedProjectPath / "user").LexicallyNormal();
|
||||
registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
|
||||
// check for a default write storage path, fall back to the project's user/ directory if not
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
|
||||
registry.Set(FilePathKey_DevWriteStorage,
|
||||
devWriteStorage.has_value() ?
|
||||
devWriteStorage.value() :
|
||||
path.LexicallyNormal().Native());
|
||||
registry.Set(FilePathKey_DevWriteStorage, devWriteStorage.has_value()
|
||||
? devWriteStorage.value()
|
||||
: projectUserPath.Native());
|
||||
|
||||
// Cache game folder - corresponds to the @assets@ alias
|
||||
AZStd::to_lower(projectPathValue.begin(), projectPathValue.end());
|
||||
path /= projectPathValue;
|
||||
registry.Set(FilePathKey_CacheGameFolder, path.LexicallyNormal().Native());
|
||||
// Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
|
||||
auto projectNameKey =
|
||||
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
|
||||
+ "/project_name";
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
if (!registry.Get(projectName, projectNameKey))
|
||||
{
|
||||
projectName = path.Filename().Native();
|
||||
registry.Set(projectNameKey, projectName);
|
||||
}
|
||||
|
||||
// Cache folders - sets up various paths in registry for the cache.
|
||||
// Make sure the asset platform is set before setting these cache paths.
|
||||
if (!assetPlatform.empty())
|
||||
{
|
||||
// Cache: project root - no corresponding fileIO alias, but this is where the asset database lives.
|
||||
// A registry override is accepted using the "project_cache_path" key.
|
||||
buffer = AZStd::fixed_string<bufferSize>::format("%s/project_cache_path", BootstrapSettingsRootKey);
|
||||
AZStd::string_view projectCacheRootOverrideKey(buffer);
|
||||
// Clear path to make sure that the `project_cache_path` value isn't concatenated to the project path
|
||||
path.clear();
|
||||
if (registry.Get(path.Native(), projectCacheRootOverrideKey))
|
||||
{
|
||||
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
|
||||
path /= assetPlatform;
|
||||
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cache: root - same as the @root@ alias, this is the starting path for cache files.
|
||||
path = normalizedProjectPath / "Cache";
|
||||
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
|
||||
path /= assetPlatform;
|
||||
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf("SettingsRegistryMergeUtils",
|
||||
R"(Current Project game folder "%.*s" isn't set in the Settings Registry. Project-related filepaths will not be set)" "\n",
|
||||
aznumeric_cast<int>(projectPath.size()), projectPath.data());
|
||||
R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n",
|
||||
aznumeric_cast<int>(projectPathKey.size()), projectPathKey.data());
|
||||
}
|
||||
|
||||
#if !AZ_TRAIT_USE_ASSET_CACHE_FOLDER
|
||||
// Setup the cache and user paths for Platforms where the Asset Cache Folder isn't used
|
||||
path = engineRoot;
|
||||
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
|
||||
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
|
||||
registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
|
||||
registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native());
|
||||
#endif // AZ_TRAIT_USE_ASSET_CACHE_FOLDER
|
||||
}
|
||||
|
||||
void MergeSettingsToRegistry_TargetBuildDependencyRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
@@ -535,7 +701,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString sourceGamePath;
|
||||
if (registry.Get(sourceGamePath, FilePathKey_SourceGameFolder))
|
||||
if (registry.Get(sourceGamePath, FilePathKey_ProjectPath))
|
||||
{
|
||||
AZ::IO::FixedMaxPath mergePath{ sourceGamePath };
|
||||
mergePath /= SettingsRegistryInterface::RegistryFolder;
|
||||
@@ -543,26 +709,41 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
}
|
||||
}
|
||||
|
||||
void MergeSettingsToRegistry_DevRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
void MergeSettingsToRegistry_UserRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer)
|
||||
{
|
||||
// Unlike other paths, the path can't be overwritten by the dev settings because that would create a circular dependency.
|
||||
auto mergePath = GetAppRoot(®istry);
|
||||
mergePath /= SettingsRegistryInterface::DevUserRegistryFolder;
|
||||
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
|
||||
AZ::IO::FixedMaxPath projectUserPath;
|
||||
if (registry.Get(projectUserPath.Native(), FilePathKey_ProjectPath))
|
||||
{
|
||||
projectUserPath /= SettingsRegistryInterface::DevUserRegistryFolder;
|
||||
registry.MergeSettingsFolder(projectUserPath.Native(), specializations, platform, "", scratchBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands)
|
||||
{
|
||||
const size_t regsetSwitchValues = commandLine.GetNumSwitchValues("regset");
|
||||
for (size_t regsetIndex = 0; regsetIndex < regsetSwitchValues; ++regsetIndex)
|
||||
// Iterate over all the command line options in order to parse the --regset and --regremove
|
||||
// arguments in the order they were supplied
|
||||
for (const CommandLine::CommandArgument& commandArgument : commandLine)
|
||||
{
|
||||
AZStd::string_view regsetValue = commandLine.GetSwitchValue("regset", regsetIndex);
|
||||
if (!registry.MergeCommandLineArgument(regsetValue, AZStd::string_view{}))
|
||||
if (commandArgument.m_option == "regset")
|
||||
{
|
||||
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regset with value of %.*s.",
|
||||
aznumeric_cast<int>(regsetValue.size()), regsetValue.data());
|
||||
continue;
|
||||
if (!registry.MergeCommandLineArgument(commandArgument.m_value, AZStd::string_view{}))
|
||||
{
|
||||
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regset with value of %s.",
|
||||
commandArgument.m_value.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (commandArgument.m_option == "regremove")
|
||||
{
|
||||
if (!registry.Remove(commandArgument.m_value))
|
||||
{
|
||||
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to remove value at JSON Pointer %s for --regremove.",
|
||||
commandArgument.m_value.data());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,16 +766,8 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
DumpSettingsRegistryToStream(registry, regdumpValue, outputStream, dumperSettings);
|
||||
}
|
||||
|
||||
const size_t regdumpallSwitchValues = commandLine.GetNumSwitchValues("regdumpall");
|
||||
if (regdumpallSwitchValues > 0)
|
||||
if (commandLine.HasSwitch("regdumpall"))
|
||||
{
|
||||
AZStd::string_view regdumpallValue = commandLine.GetSwitchValue("regdumpall", 0);
|
||||
if (regdumpallValue.empty())
|
||||
{
|
||||
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regdumpall with value of %.*s.",
|
||||
aznumeric_cast<int>(regdumpallValue.size()), regdumpallValue.data());
|
||||
}
|
||||
|
||||
DumperSettings dumperSettings{ prettifyOutput };
|
||||
AZ::IO::StdoutStream outputStream;
|
||||
DumpSettingsRegistryToStream(registry, "", outputStream, dumperSettings);
|
||||
@@ -602,85 +775,77 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
}
|
||||
}
|
||||
|
||||
void MergeSettingsToRegistry_StoreCommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine)
|
||||
void StoreCommandLineToRegistry(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine)
|
||||
{
|
||||
// Clear out any existing CommandLine settings
|
||||
registry.Remove(CommandLineRootKey);
|
||||
// Add the positional arguments into the Settings Registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString miscValueKey{ CommandLineMiscValuesRootKey };
|
||||
size_t miscKeyRootSize = miscValueKey.size();
|
||||
for (size_t miscIndex = 0; miscIndex < commandLine.GetNumMiscValues(); ++miscIndex)
|
||||
{
|
||||
// Push an array for each positional entry
|
||||
miscValueKey += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", miscIndex);
|
||||
registry.Set(miscValueKey, commandLine.GetMiscValue(miscIndex));
|
||||
miscValueKey.resize(miscKeyRootSize);
|
||||
}
|
||||
// Add the option arguments into the SettingsRegistry
|
||||
for (const auto& [commandOption, value] : commandLine.GetSwitchList())
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString switchKey{ CommandLineSwitchRootKey };
|
||||
switchKey += '/';
|
||||
switchKey += commandOption;
|
||||
size_t switchKeyRootSize = switchKey.size();
|
||||
// Associate an empty array with the commandOption by default
|
||||
rapidjson::Document commandSwitchDocument;
|
||||
rapidjson::Pointer pointer(switchKey.c_str(), switchKey.length());
|
||||
pointer.Set(commandSwitchDocument, rapidjson::Value(rapidjson::kArrayType));;
|
||||
rapidjson::StringBuffer documentBuffer;
|
||||
rapidjson::Writer documentWriter(documentBuffer);
|
||||
commandSwitchDocument.Accept(documentWriter);
|
||||
registry.MergeSettings(AZStd::string_view{ documentBuffer.GetString(), documentBuffer.GetSize() },
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch);
|
||||
|
||||
for (size_t switchIndex = 0; switchIndex < value.size(); ++switchIndex)
|
||||
{
|
||||
// Push an array for each positional entry
|
||||
switchKey += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", switchIndex);
|
||||
registry.Set(switchKey, value[switchIndex]);
|
||||
switchKey.resize(switchKeyRootSize);
|
||||
}
|
||||
AZ::SettingsRegistryInterface::FixedValueString commandLinePath{ CommandLineRootKey };
|
||||
const size_t commandLineRootSize = commandLinePath.size();
|
||||
size_t argumentIndex{};
|
||||
for (const CommandLine::CommandArgument& commandArgument : commandLine)
|
||||
{
|
||||
commandLinePath += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", argumentIndex);
|
||||
registry.Set(commandLinePath + "/Option", commandArgument.m_option);
|
||||
registry.Set(commandLinePath + "/Value", commandArgument.m_value);
|
||||
++argumentIndex;
|
||||
commandLinePath.resize(commandLineRootSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
|
||||
{
|
||||
struct CommandLineVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type
|
||||
, AZStd::string_view value) override
|
||||
{
|
||||
if (valueName == "Option" && !value.empty())
|
||||
{
|
||||
m_arguments.push_back(AZStd::string::format("--%.*s", aznumeric_cast<int>(value.size()), value.data()));
|
||||
}
|
||||
else if (valueName == "Value" && !value.empty())
|
||||
{
|
||||
m_arguments.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
// The first parameter is skipped by the ComamndLine::Parse function so initialize
|
||||
// the container with one empty element
|
||||
AZ::CommandLine::ParamContainer m_arguments{ 1 };
|
||||
};
|
||||
|
||||
CommandLineVisitor commandLineVisitor;
|
||||
if (!registry.Visit(commandLineVisitor, AZ::SettingsRegistryMergeUtils::CommandLineRootKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
commandLine.Parse(commandLineVisitor.m_arguments);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpSettingsRegistryToStream(SettingsRegistryInterface& registry, AZStd::string_view key,
|
||||
AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings)
|
||||
{
|
||||
struct SettingsExportVisitor
|
||||
: SettingsRegistryInterface::Visitor
|
||||
{
|
||||
using SettingsWriter = AZStd::variant<
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>,
|
||||
rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>>;
|
||||
SettingsWriter m_writer;
|
||||
AZ::IO::RapidJSONStreamWriter m_rapidJsonStream;
|
||||
DumperSettings m_dumperSettings;
|
||||
AZStd::stack<bool> m_includeNameStack;
|
||||
bool m_includeName{};
|
||||
bool m_result{ true };
|
||||
|
||||
SettingsExportVisitor(AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings)
|
||||
: m_rapidJsonStream{ &stream }
|
||||
, m_dumperSettings{ dumperSettings }
|
||||
, m_writer{ m_stringBuffer }
|
||||
{
|
||||
if (m_dumperSettings.m_prettifyOutput)
|
||||
{
|
||||
m_writer.emplace<rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>>(m_rapidJsonStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_writer.emplace<rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>>(m_rapidJsonStream);
|
||||
}
|
||||
}
|
||||
void WriteName(AZStd::string_view name)
|
||||
bool WriteName(AZStd::string_view name)
|
||||
{
|
||||
if (m_includeName)
|
||||
// Write the Key if the include name stack the top element is true
|
||||
if (!m_includeNameStack.empty() && m_includeNameStack.top())
|
||||
{
|
||||
AZStd::visit([&name](auto&& writer)
|
||||
{
|
||||
writer.Key(name.data(), aznumeric_caster(name.size()));
|
||||
}, m_writer);
|
||||
return m_writer.Key(name.data(), aznumeric_caster(name.size()), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(
|
||||
AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action,
|
||||
@@ -694,60 +859,43 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
|
||||
{
|
||||
AZ_Assert(type == AZ::SettingsRegistryInterface::Type::Object || type == AZ::SettingsRegistryInterface::Type::Array,
|
||||
"Unexpected type visited: %i.", type);
|
||||
WriteName(valueName);
|
||||
m_result = m_result && WriteName(valueName);
|
||||
if (type == AZ::SettingsRegistryInterface::Type::Object)
|
||||
{
|
||||
auto StartObject = [](auto&& writer)
|
||||
m_result = m_result && m_writer.StartObject();
|
||||
if (m_result)
|
||||
{
|
||||
return writer.StartObject();
|
||||
};
|
||||
m_result = m_result && AZStd::visit(StartObject, m_writer);
|
||||
m_includeNameStack.push(true);
|
||||
m_includeName = true;
|
||||
m_includeNameStack.push(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto StartArray = [](auto&& writer)
|
||||
m_result = m_result && m_writer.StartArray();
|
||||
if (m_result)
|
||||
{
|
||||
return writer.StartArray();
|
||||
};
|
||||
m_result = m_result && AZStd::visit(StartArray, m_writer);
|
||||
m_includeNameStack.push(false);
|
||||
m_includeName = false;
|
||||
m_includeNameStack.push(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
|
||||
{
|
||||
if (type == AZ::SettingsRegistryInterface::Type::Object)
|
||||
{
|
||||
auto EndObject = [](auto&& writer)
|
||||
{
|
||||
return writer.EndObject();
|
||||
};
|
||||
m_result = m_result && AZStd::visit(EndObject, m_writer);
|
||||
m_result = m_result && m_writer.EndObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto EndArray = [](auto&& writer)
|
||||
{
|
||||
return writer.EndArray();
|
||||
};
|
||||
m_result = m_result && AZStd::visit(EndArray, m_writer);
|
||||
m_result = m_result && m_writer.EndArray();
|
||||
}
|
||||
AZ_Assert(!m_includeNameStack.empty(), "Attempting to close a json array or object that wasn't started.");
|
||||
m_includeNameStack.pop();
|
||||
m_includeName = !m_includeNameStack.empty() ? m_includeNameStack.top() : true;
|
||||
}
|
||||
else if (type == AZ::SettingsRegistryInterface::Type::Null)
|
||||
else
|
||||
{
|
||||
WriteName(valueName);
|
||||
auto WriteNull = [](auto&& writer)
|
||||
if (type == AZ::SettingsRegistryInterface::Type::Null)
|
||||
{
|
||||
return writer.Null();
|
||||
};
|
||||
m_result = m_result && AZStd::visit(WriteNull, m_writer);
|
||||
m_result = m_result && WriteName(valueName) && m_writer.Null();
|
||||
}
|
||||
}
|
||||
|
||||
return m_result ?
|
||||
@@ -757,42 +905,27 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
|
||||
{
|
||||
WriteName(valueName);
|
||||
auto WriteBool = [value](auto&& writer)
|
||||
{
|
||||
return writer.Bool(value);
|
||||
};
|
||||
m_result = m_result && AZStd::visit(WriteBool, m_writer);
|
||||
m_result = m_result && WriteName(valueName) && m_writer.Bool(value);
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override
|
||||
{
|
||||
WriteName(valueName);
|
||||
auto WriteInt64 = [value](auto&& writer)
|
||||
{
|
||||
return writer.Int64(value);
|
||||
};
|
||||
m_result = m_result && AZStd::visit(WriteInt64, m_writer);
|
||||
m_result = m_result && WriteName(valueName) && m_writer.Int64(value);
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::u64 value) override
|
||||
{
|
||||
m_result = m_result && WriteName(valueName) && m_writer.Uint64(value);
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, double value)
|
||||
{
|
||||
WriteName(valueName);
|
||||
auto WriteDouble = [value](auto&& writer)
|
||||
{
|
||||
return writer.Double(value);
|
||||
};
|
||||
m_result = m_result && AZStd::visit(WriteDouble, m_writer);
|
||||
m_result = m_result && WriteName(valueName) && m_writer.Double(value);
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
|
||||
{
|
||||
WriteName(valueName);
|
||||
auto WriteString = [&value](auto&& writer)
|
||||
{
|
||||
return writer.String(value.data(), aznumeric_caster(value.size()));
|
||||
};
|
||||
m_result = m_result && AZStd::visit(WriteString, m_writer);
|
||||
m_result = m_result && WriteName(valueName) && m_writer.String(value.data(), aznumeric_caster(value.size()));
|
||||
}
|
||||
|
||||
bool Finalize()
|
||||
@@ -802,8 +935,46 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
AZ_Assert(false, "m_includeNameStack is expected to be empty. This means that there was an object or array what wasn't closed.");
|
||||
return false;
|
||||
}
|
||||
return m_result;
|
||||
|
||||
// Root the JSON document underneath the JSON pointer prefix if non-empty
|
||||
// Parse non-anchored JSON data into a document and then re-root
|
||||
// that document under the prefix value
|
||||
rapidjson::Document document;
|
||||
document.Parse(m_stringBuffer.GetString(), m_stringBuffer.GetSize());
|
||||
|
||||
rapidjson::Document rootDocument;
|
||||
AZStd::string_view jsonPrefix{ m_dumperSettings.m_jsonPointerPrefix };
|
||||
if (!jsonPrefix.empty())
|
||||
{
|
||||
rapidjson::Pointer rootPointer(jsonPrefix.data(), jsonPrefix.size());
|
||||
rapidjson::SetValueByPointer(rootDocument, rootPointer, document);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootDocument = AZStd::move(document);
|
||||
}
|
||||
|
||||
if (m_dumperSettings.m_prettifyOutput)
|
||||
{
|
||||
rapidjson::PrettyWriter settingsWriter(m_rapidJsonStream);
|
||||
rootDocument.Accept(settingsWriter);
|
||||
}
|
||||
else
|
||||
{
|
||||
rapidjson::Writer settingsWriter(m_rapidJsonStream);
|
||||
rootDocument.Accept(settingsWriter);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rapidjson::StringBuffer m_stringBuffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> m_writer;
|
||||
|
||||
AZ::IO::RapidJSONStreamWriter m_rapidJsonStream;
|
||||
DumperSettings m_dumperSettings;
|
||||
AZStd::stack<bool> m_includeNameStack;
|
||||
bool m_result{ true };
|
||||
};
|
||||
|
||||
SettingsExportVisitor visitor(stream, dumperSettings);
|
||||
|
||||
@@ -36,27 +36,45 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
inline static constexpr char FilePathsRootKey[] = "/Amazon/AzCore/Runtime/FilePaths";
|
||||
inline static constexpr char FilePathKey_BinaryFolder[] = "/Amazon/AzCore/Runtime/FilePaths/BinaryFolder";
|
||||
inline static constexpr char FilePathKey_EngineRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/EngineRootFolder";
|
||||
|
||||
//! Stores the absolute path to root of a project's cache. No asset platform in this path, this is where the asset database file lives.
|
||||
//! i.e. <ProjectPath>/Cache
|
||||
inline static constexpr char FilePathKey_CacheProjectRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheProjectRootFolder";
|
||||
|
||||
//! Stores the absolute path to the cache root for an asset platform. This is the @root@ alias.
|
||||
//! i.e. <ProjectPath>/Cache/<assetplatform>
|
||||
inline static constexpr char FilePathKey_CacheRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheRootFolder";
|
||||
inline static constexpr char FilePathKey_CacheGameFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheGameFolder";
|
||||
inline static constexpr char FilePathKey_SourceGameFolder[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameFolder";
|
||||
//! Stores the filename of the Game Project Directory which is equivalent to the project name
|
||||
inline static constexpr char FilePathKey_SourceGameName[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameName";
|
||||
|
||||
//! Stores the absolute path of the Game Project Directory
|
||||
inline static constexpr char FilePathKey_ProjectPath[] = "/Amazon/AzCore/Runtime/FilePaths/SourceProjectPath";
|
||||
|
||||
//! Store the absolute path to the Projects "user" directory, which is a transient directory where per user
|
||||
//! project settings can be stored
|
||||
inline static constexpr char FilePathKey_ProjectUserPath[] = "/Amazon/AzCore/Runtime/FilePaths/SourceProjectUserPath";
|
||||
|
||||
//! Development write storage path may be considered temporary or cache storage on some platforms
|
||||
inline static constexpr char FilePathKey_DevWriteStorage[] = "/Amazon/AzCore/Runtime/FilePaths/DevWriteStorage";
|
||||
|
||||
//! Root key for where command line are stored at witin the settings registry
|
||||
//! Root key for where command line are stored at within the settings registry
|
||||
inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine";
|
||||
//! Root key for command line switches(arguments that start with "-" or "--")
|
||||
inline static constexpr char CommandLineSwitchRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/Switches";
|
||||
//! Root key for command line positional arguments
|
||||
inline static constexpr char CommandLineMiscValuesRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/MiscValues";
|
||||
|
||||
//! Examines the Settings Registry for a "/Amazon/CommandLine/Switches/app-root" key
|
||||
//! to use as an override for the Application Root.
|
||||
//! If that key is not found, it then checks the AZ::Utils::GetDefaultAppRootPath and returns that if it is value
|
||||
//! Root key where raw project settings (project.json) file is merged to settings registry
|
||||
inline static constexpr char ProjectSettingsRootKey[] = "/Amazon/Project/Settings";
|
||||
|
||||
//! Root key where raw engine manifest (o3de_manifest.json) file is merged to settings registry
|
||||
inline static constexpr char EngineManifestRootKey[] = "/Amazon/Engine/Manifest";
|
||||
|
||||
//! Root key where raw engine settings (engine.json) file is merged to settings registry
|
||||
inline static constexpr char EngineSettingsRootKey[] = "/Amazon/Engine/Settings";
|
||||
|
||||
//! Examines the Settings Registry for a "${BootstrapSettingsRootKey}/engine_path" key
|
||||
//! to use as an override for the Engine Root.
|
||||
//! Otherwise a directory walk upwards from the executable directory is performed
|
||||
//! to find the boostrap.cfg file which will be used as the app root
|
||||
AZ::IO::FixedMaxPath GetAppRoot(SettingsRegistryInterface* settingsRegistry = nullptr);
|
||||
//! to find the engine.json file which will be used as the engine root
|
||||
//! If it's still not found, attempt to find the project (by similar means) then reconcile the
|
||||
//! engine root by inspecting project.json and the engine manifest file.
|
||||
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry);
|
||||
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry);
|
||||
|
||||
//! Query the specializations that will be used when loading the Settings Registry.
|
||||
//! The SpecializationsRootKey is visited to retrieve any specializations stored within that section of that registry
|
||||
@@ -151,16 +169,18 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
void MergeSettingsToRegistry_ProjectRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr);
|
||||
|
||||
//! Adds the development settings added by individual users to the Settings Registry.
|
||||
//! Adds the development settings added by individual users of the project to the Settings Registry.
|
||||
//! Note that this function is only called in development builds and is compiled out in release builds.
|
||||
void MergeSettingsToRegistry_DevRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
void MergeSettingsToRegistry_UserRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
|
||||
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr);
|
||||
|
||||
//! Adds the settings set through the command line to the Settings Registry. This will also execute any Settings
|
||||
//! Registry related arguments. Note that --set will be run first and all other commands are run afterwards and only
|
||||
//! if executeCommands is true. The following options are supported:
|
||||
//! Registry related arguments. Note that --regset and -regremove will run in the order in which they are parsed
|
||||
//! --regset <arg> Sets a value in the registry. See MergeCommandLineArgument for options for <arg>
|
||||
//! example: --regset "/My/String/Value=String value set"
|
||||
//! --regremove <arg> Removes a value in the registry
|
||||
//! example: --regremove "/My/String/Value"
|
||||
//! only when executeCommands is true are the following options supported:
|
||||
//! --regdump <path> Dumps the content of the key at path and all it's content/children to output.
|
||||
//! example: --regdump /My/Array/With/Objects
|
||||
//! --regdumpall Dumps the entire settings registry to output.
|
||||
@@ -169,7 +189,11 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
//! Stores the command line settings into the Setting Registry
|
||||
//! The arguments can be used later anywhere the command line is needed
|
||||
void MergeSettingsToRegistry_StoreCommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine);
|
||||
void StoreCommandLineToRegistry(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine);
|
||||
|
||||
//! Query the command line settings from the Setting Registry and stores them
|
||||
//! into the AZ::CommandLine instance
|
||||
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine);
|
||||
|
||||
//! Structure for configuring how values should be dumped from the Settings Registry
|
||||
struct DumperSettings
|
||||
@@ -179,14 +203,25 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
//! Include filter which is used to indicate which paths of the Settings Registry
|
||||
//! should be traversed.
|
||||
//! If the include filter is empty then all paths underneath the JSON pointer path are included
|
||||
//! otherwise the include filter invoked and if it returns true does it proceed with traversal continues down the path
|
||||
//! otherwise the include filter invoked and if it returns true does it proceed with traversal down the path
|
||||
//! The supplied JSON pointer will be a complete path from the root of the registry
|
||||
AZStd::function<bool(AZStd::string_view path)> m_includeFilter;
|
||||
//! JSON pointer prefix to dump all settings underneath
|
||||
//! For example if the prefix is "/Amazon/Settings", then the dumped settings will be placed underneath
|
||||
//! an object at that path
|
||||
//! """
|
||||
//! {
|
||||
//! "Amazon":{
|
||||
//! "Settings":{ <Dumped values> }
|
||||
//! }
|
||||
//! }
|
||||
AZStd::string_view m_jsonPointerPrefix;
|
||||
};
|
||||
|
||||
//! Dumps supplied settings registry from the path specified by key if it exist the the AZ::IO::GenericStream
|
||||
//! key is a JSON pointer path to dumping settings recursively from
|
||||
//! stream is an AZ::IO::GenericStream that supports writing
|
||||
//! dumperSettings are used to determine how to format the dumped output
|
||||
//! @param key is a JSON pointer to recursively dump settings from
|
||||
//! @param stream is an AZ::IO::GenericStream that supports writing
|
||||
//! @param dumperSettings are used to determine how to format the dumped output
|
||||
bool DumpSettingsRegistryToStream(SettingsRegistryInterface& registry, AZStd::string_view key,
|
||||
AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings);
|
||||
|
||||
|
||||
@@ -418,36 +418,37 @@ namespace AZ::StringFunc::Internal
|
||||
return Strip(inout, { &stripCharacter, 1 }, bCaseSensitive, bStripBeginning, bStripEnding);
|
||||
}
|
||||
|
||||
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
if (size_t pos = in.find_first_not_of(stripCharacters); pos != AZStd::string_view::npos)
|
||||
{
|
||||
return in.substr(pos);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
if (size_t pos = in.find_last_not_of(stripCharacters); pos != AZStd::string_view::npos)
|
||||
{
|
||||
return in.substr(0, pos < in.size() ? pos + 1 : pos);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
return LStrip(RStrip(in, stripCharacters), stripCharacters);
|
||||
};
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace StringFunc
|
||||
{
|
||||
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
if (size_t pos = in.find_first_not_of(stripCharacters); pos != AZStd::string_view::npos)
|
||||
{
|
||||
return in.substr(pos);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
if (size_t pos = in.find_last_not_of(stripCharacters); pos != AZStd::string_view::npos)
|
||||
{
|
||||
return in.substr(0, pos < in.size() ? pos + 1 : pos);
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters)
|
||||
{
|
||||
return LStrip(RStrip(in, stripCharacters), stripCharacters);
|
||||
};
|
||||
|
||||
bool Equal(const char* inA, const char* inB, bool bCaseSensitive /*= false*/, size_t n /*= 0*/)
|
||||
{
|
||||
if (!inA || !inB)
|
||||
@@ -483,6 +484,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
bool Equal(AZStd::string_view inA, AZStd::string_view inB, bool bCaseSensitive)
|
||||
{
|
||||
const size_t maxCharsToCompare = inA.size();
|
||||
|
||||
return inA.size() == inB.size() && (bCaseSensitive
|
||||
? strncmp(inA.data(), inB.data(), maxCharsToCompare) == 0
|
||||
: azstrnicmp(inA.data(), inB.data(), maxCharsToCompare) == 0);
|
||||
}
|
||||
|
||||
bool StartsWith(AZStd::string_view sourceValue, AZStd::string_view prefixValue, bool bCaseSensitive)
|
||||
{
|
||||
@@ -496,9 +505,18 @@ namespace AZ
|
||||
&& Equal(sourceValue.substr(sourceValue.size() - suffixValue.size(), AZStd::string_view::npos).data(), suffixValue.data(), bCaseSensitive, suffixValue.size());
|
||||
}
|
||||
|
||||
size_t Find(const char* in, char c, size_t pos /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
|
||||
bool Contains(AZStd::string_view in, char ch, bool bCaseSensitive)
|
||||
{
|
||||
if (!in)
|
||||
return Find(in, ch, 0, false, bCaseSensitive) != AZStd::string_view::npos;
|
||||
}
|
||||
bool Contains(AZStd::string_view in, AZStd::string_view sv, bool bCaseSensitive)
|
||||
{
|
||||
return Find(in, sv, 0, false, bCaseSensitive) != AZStd::string_view::npos;
|
||||
}
|
||||
|
||||
size_t Find(AZStd::string_view in, char c, size_t pos /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
|
||||
{
|
||||
if (in.empty())
|
||||
{
|
||||
return AZStd::string::npos;
|
||||
}
|
||||
@@ -508,7 +526,7 @@ namespace AZ
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
size_t inLen = strlen(in);
|
||||
size_t inLen = in.size();
|
||||
if (inLen < pos)
|
||||
{
|
||||
return AZStd::string::npos;
|
||||
@@ -557,7 +575,13 @@ namespace AZ
|
||||
|
||||
size_t Find(AZStd::string_view in, AZStd::string_view s, size_t offset /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
|
||||
{
|
||||
if (in.empty() || s.empty())
|
||||
// Formally an empty string matches at the offset if it is <= to the size of the input string
|
||||
if (s.empty() && offset <= in.size())
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (in.empty())
|
||||
{
|
||||
return AZStd::string::npos;
|
||||
}
|
||||
@@ -773,7 +797,7 @@ namespace AZ
|
||||
bool bIsSpaces = false;
|
||||
if (!bIsEmpty)
|
||||
{
|
||||
AZStd::string_view strippedNextToken = Internal::StripEnds(*nextToken, " ");
|
||||
AZStd::string_view strippedNextToken = StripEnds(*nextToken, " ");
|
||||
bIsSpaces = strippedNextToken.empty();
|
||||
}
|
||||
|
||||
@@ -805,7 +829,7 @@ namespace AZ
|
||||
bool bIsSpaces = false;
|
||||
if (!bIsEmpty)
|
||||
{
|
||||
AZStd::string_view strippedNextToken = Internal::StripEnds(*nextToken, " ");
|
||||
AZStd::string_view strippedNextToken = StripEnds(*nextToken, " ");
|
||||
bIsSpaces = strippedNextToken.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
@@ -107,6 +108,22 @@ namespace AZ
|
||||
StringFunc::Equal("Hello World", "Hello", true, 3) = true
|
||||
*/
|
||||
bool Equal(const char* inA, const char* inB, bool bCaseSensitive = false, size_t n = 0);
|
||||
bool Equal(AZStd::string_view inA, AZStd::string_view inB, bool bCaseSensitive = false);
|
||||
|
||||
//! Contains
|
||||
/*! Checks if the supplied character or string is contained within the @in parameter
|
||||
*
|
||||
Example: Case Insensitive contains finds character
|
||||
StringFunc::Contains("Hello", 'L') == true
|
||||
Example: Case Sensitive contains finds character
|
||||
StringFunc::Contains("Hello", 'l', true) = true
|
||||
Example: Case Insensitive contains does not find string
|
||||
StringFunc::Contains("Well Hello", "Mello") == false
|
||||
Example: Case Sensitive contains does not find character
|
||||
StringFunc::Contains("HeLlo", 'h', true) == false
|
||||
*/
|
||||
bool Contains(AZStd::string_view in, char ch, bool bCaseSensitive = false);
|
||||
bool Contains(AZStd::string_view in, AZStd::string_view sv, bool bCaseSensitive = false);
|
||||
|
||||
//! Find
|
||||
/*! Find for non AZStd::strings. Ease of use to find the first or last occurrence of a character or substring in a c-string with case sensitivity.
|
||||
@@ -119,7 +136,7 @@ namespace AZ
|
||||
Example: Case Sensitive find first occurrence of substring "Hello" a in a c-string
|
||||
StringFunc::Find("Well Hello", "Hello", false, true) == 5
|
||||
*/
|
||||
size_t Find(const char* in, char c, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
|
||||
size_t Find(AZStd::string_view in, char c, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
|
||||
size_t Find(AZStd::string_view in, AZStd::string_view str, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
|
||||
|
||||
//! First and Last Character
|
||||
@@ -196,6 +213,33 @@ namespace AZ
|
||||
*/
|
||||
AZStd::string& TrimWhiteSpace(AZStd::string& value, bool leading, bool trailing);
|
||||
|
||||
//! LStrip
|
||||
/*! Strips leading characters in the stripCharacters set
|
||||
* Example
|
||||
* Example: Case Insensitive Strip leading 'a' characters
|
||||
* StringFunc::LStrip(s = "Abracadabra", 'a'); s == "bracadabra"
|
||||
* Example: Case Sensitive Strip leading 'a' characters
|
||||
* StringFunc::LStrip(s = "Abracadabra", 'a'); s == "Abracadabra"
|
||||
*/
|
||||
//! RStrip
|
||||
/*! Strips trailing characters in the stripCharacters set
|
||||
* Example
|
||||
* Example: Case Insensitive Strip trailing 'a' characters
|
||||
* StringFunc::RStrip(s = "AbracadabrA", 'a'); s == "Abracadabr"
|
||||
* Example: Case Sensitive Strip trailing 'a' characters
|
||||
* StringFunc::RStrip(s = "AbracadabrA", 'a'); s == "AbracadabrA"
|
||||
*/
|
||||
//! StripEnds
|
||||
/*! Strips leading and trailing characters in the stripCharacters set
|
||||
Example: Case Insensitive Strip all 'a' characters
|
||||
StringFunc::StripEnds(s = "Abracadabra", 'a'); s == "bracadabr"
|
||||
Example: Case Sensitive Strip all 'a' characters
|
||||
StringFunc::StripEnds(s = "Abracadabra", 'a'); s == "Abracadabr"
|
||||
*/
|
||||
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
|
||||
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
|
||||
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
|
||||
|
||||
//! Strip
|
||||
/*! Strip away the leading, trailing or all character(s) or substring(s) in a AZStd::string with
|
||||
*! case sensitivity.
|
||||
@@ -669,8 +713,7 @@ namespace AZ
|
||||
*/
|
||||
namespace Path
|
||||
{
|
||||
inline constexpr size_t MaxPathLength = 1024;
|
||||
using FixedString = AZStd::fixed_string<MaxPathLength>;
|
||||
using FixedString = AZ::IO::FixedMaxPathString;
|
||||
//! Normalize
|
||||
/*! Normalizes a path and returns returns StringFunc::Path::IsValid()
|
||||
*! strips all AZ_FILESYSTEM_INVALID_CHARACTERS
|
||||
@@ -791,7 +834,7 @@ namespace AZ
|
||||
|
||||
//! StripFullName
|
||||
/*! gets rid of the full file name if it has one, returns if it removed one or not
|
||||
*! EX: StringFunc::Path::StripFullName(a="C:\\p4\\game\\info\\some.file") == true; a=="C:\\p4\\game\\info\\"
|
||||
*! EX: StringFunc::Path::StripFullName(a="C:\\p4\\game\\info\\some.file") == true; a=="C:\\p4\\game\\info"
|
||||
*/
|
||||
void StripFullName(AZStd::string& out);
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace UnitTest
|
||||
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
|
||||
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
|
||||
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
|
||||
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
|
||||
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
|
||||
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace AZ
|
||||
MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path));
|
||||
MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize));
|
||||
MOCK_CONST_METHOD2(ResolvePath, bool(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path));
|
||||
MOCK_CONST_METHOD2(ReplaceAlias, bool(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path));
|
||||
MOCK_CONST_METHOD3(GetFilename, bool(HandleType fileHandle, char* filename, AZ::u64 filenameSize));
|
||||
using FileIOBase::ConvertToAlias;
|
||||
using FileIOBase::ResolvePath;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
namespace AZ::Utils
|
||||
@@ -40,6 +42,47 @@ namespace AZ::Utils
|
||||
return result.m_pathStored;
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetEnginePath()
|
||||
{
|
||||
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
|
||||
if (registry->Get(settingsValue, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
return AZ::IO::FixedMaxPathString{settingsValue};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetProjectPath()
|
||||
{
|
||||
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
|
||||
if (registry->Get(settingsValue, AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
|
||||
{
|
||||
return AZ::IO::FixedMaxPathString{settingsValue};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::FixedValueString GetProjectName()
|
||||
{
|
||||
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectNameKey{ AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey };
|
||||
projectNameKey += "/project_name";
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
|
||||
if (registry->Get(settingsValue, projectNameKey))
|
||||
{
|
||||
return settingsValue;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath)
|
||||
{
|
||||
AZ::IO::FixedMaxPath filePathFixed = filePath; // Because FileIOStream requires a null-terminated string
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
@@ -24,9 +26,6 @@ namespace AZ
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
// Common cross platform Utils go here
|
||||
inline constexpr size_t MaxPathLength = 1024;
|
||||
|
||||
//! Protects from allocating too much memory. The choice of a 1MB threshold is arbitrary.
|
||||
//! If you need to work with larger files, please use AZ::IO directly instead of these utility functions.
|
||||
inline constexpr size_t DefaultMaxFileSize = 1024 * 1024;
|
||||
@@ -77,10 +76,22 @@ namespace AZ
|
||||
//! @returns a result object that indicates if the executable directory was able to be stored within the buffer
|
||||
ExecutablePathResult GetExecutableDirectory(char* exeStorageBuffer, size_t exeStorageSize);
|
||||
|
||||
//! Retrieves the full path to the engine from settings registry
|
||||
AZ::IO::FixedMaxPathString GetEnginePath();
|
||||
|
||||
//! Retrieves the full path to the project from settings registry
|
||||
AZ::IO::FixedMaxPathString GetProjectPath();
|
||||
|
||||
//! Retrieves the project name from the settings registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString GetProjectName();
|
||||
|
||||
//! Retrieves the full path where the manifest file lives, i.e. "<userhome>/.o3de/o3de_manifest.json"
|
||||
AZ::IO::FixedMaxPathString GetEngineManifestPath();
|
||||
|
||||
//! Retrieves the App root path to use on the current platform
|
||||
//! If the optional is not engaged the AppRootPath should be calculated based
|
||||
//! on the location of the bootstrap.cfg file
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath();
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath();
|
||||
|
||||
//! Retrieves the development write storage path to use on the current platform, may be considered
|
||||
//! temporary or cache storage
|
||||
@@ -88,7 +99,7 @@ namespace AZ
|
||||
|
||||
// Attempts the supplied path to an absolute path.
|
||||
//! Returns nullopt if path cannot be converted to an absolute path
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path);
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path);
|
||||
|
||||
//! Save a string to a file. Otherwise returns a failure with error message.
|
||||
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath);
|
||||
|
||||
@@ -249,6 +249,11 @@ namespace AZStd
|
||||
|
||||
constexpr auto swap(basic_fixed_string& rhs) -> void;
|
||||
|
||||
// C++23 contains
|
||||
constexpr auto contains(const basic_fixed_string& other) const -> bool;
|
||||
constexpr auto contains(Element ch) const -> bool;
|
||||
constexpr auto contains(const_pointer s) const -> bool;
|
||||
|
||||
constexpr auto find(const basic_fixed_string& rhs, size_type offset = 0) const -> size_type;
|
||||
constexpr auto find(const_pointer ptr, size_type offset, size_type count) const -> size_type;
|
||||
constexpr auto find(const_pointer ptr, size_type offset = 0) const -> size_type;
|
||||
|
||||
@@ -1072,6 +1072,23 @@ namespace AZStd
|
||||
Traits::assign(rhs.m_buffer[rhs.m_size], Element{ 0 });
|
||||
}
|
||||
|
||||
// C++23 contains
|
||||
template<class Element, size_t MaxElementCount, class Traits>
|
||||
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(const basic_fixed_string& other) const -> bool
|
||||
{
|
||||
return find(other) != npos;
|
||||
}
|
||||
template<class Element, size_t MaxElementCount, class Traits>
|
||||
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(value_type c) const -> bool
|
||||
{
|
||||
return find(c) != npos;
|
||||
}
|
||||
template<class Element, size_t MaxElementCount, class Traits>
|
||||
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(const_pointer s) const -> bool
|
||||
{
|
||||
return find(s) != npos;
|
||||
}
|
||||
|
||||
template<class Element, size_t MaxElementCount, class Traits>
|
||||
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::find(const basic_fixed_string& rhs, size_type offset) const -> size_type
|
||||
{
|
||||
|
||||
@@ -961,6 +961,22 @@ namespace AZStd
|
||||
}
|
||||
}
|
||||
|
||||
// C++23 contains
|
||||
bool contains(const basic_string& other) const
|
||||
{
|
||||
return find(other) != npos;
|
||||
}
|
||||
|
||||
bool contains(value_type c) const
|
||||
{
|
||||
return find(c) != npos;
|
||||
}
|
||||
|
||||
bool contains(const_pointer s) const
|
||||
{
|
||||
return find(s) != npos;
|
||||
}
|
||||
|
||||
inline size_type find(const this_type& rhs, size_type offset = 0) const
|
||||
{
|
||||
const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer;
|
||||
|
||||
@@ -669,6 +669,22 @@ namespace AZStd
|
||||
return ends_with(basic_string_view(suffix));
|
||||
}
|
||||
|
||||
// C++23 contains
|
||||
constexpr bool contains(basic_string_view other) const
|
||||
{
|
||||
return find(other) != npos;
|
||||
}
|
||||
|
||||
constexpr bool contains(value_type c) const
|
||||
{
|
||||
return find(c) != npos;
|
||||
}
|
||||
|
||||
constexpr bool contains(const_pointer s) const
|
||||
{
|
||||
return find(s) != npos;
|
||||
}
|
||||
|
||||
// find
|
||||
constexpr size_type find(basic_string_view other, size_type pos = 0) const
|
||||
{
|
||||
|
||||
@@ -52,10 +52,10 @@ namespace AZ
|
||||
// in non-release builds.
|
||||
// If a bootstrap.cfg file is not found in the public storage, it is then searched for
|
||||
// within the APK itself
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
|
||||
{
|
||||
const char* appRoot = AZ::Android::Utils::FindAssetsDirectory();
|
||||
return appRoot ? AZStd::make_optional<AZStd::fixed_string<MaxPathLength>>(appRoot) : AZStd::nullopt;
|
||||
return appRoot ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(appRoot) : AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
@@ -64,10 +64,10 @@ namespace AZ
|
||||
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
{
|
||||
AZStd::fixed_string<MaxPathLength> absolutePath;
|
||||
AZStd::fixed_string<MaxPathLength> srcPath{ path };
|
||||
AZ::IO::FixedMaxPathString absolutePath;
|
||||
AZ::IO::FixedMaxPathString srcPath{ path };
|
||||
if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
|
||||
{
|
||||
return srcPath;
|
||||
|
||||
@@ -64,6 +64,7 @@ set(FILES
|
||||
AzCore/Socket/AzSocket_Platform.h
|
||||
../Common/UnixLike/AzCore/std/time_UnixLike.cpp
|
||||
AzCore/Utils/Utils_Android.cpp
|
||||
../Common/Unimplemented/AzCore/Utils/Utils_Unimplemented.cpp
|
||||
../../AzCore/Android/AndroidEnv.cpp
|
||||
../../AzCore/Android/AndroidEnv.h
|
||||
../../AzCore/Android/APKFileHandler.cpp
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace AZ::Utils
|
||||
{
|
||||
AZ::IO::FixedMaxPathString GetEngineManifestPath()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace AZ::Utils
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -25,10 +25,25 @@ namespace AZ
|
||||
|
||||
void NativeErrorMessageBox(const char*, const char*) {}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
AZ::IO::FixedMaxPathString GetEngineManifestPath()
|
||||
{
|
||||
AZStd::fixed_string<MaxPathLength> absolutePath;
|
||||
AZStd::fixed_string<MaxPathLength> srcPath{ path };
|
||||
if (const char* homePath = std::getenv("HOME"); homePath != nullptr)
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{homePath};
|
||||
if (!path.empty())
|
||||
{
|
||||
path /= ".o3de";
|
||||
path /= "o3de_manifest.json";
|
||||
}
|
||||
return path.Native();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
{
|
||||
AZ::IO::FixedMaxPathString absolutePath;
|
||||
AZ::IO::FixedMaxPathString srcPath{ path };
|
||||
|
||||
if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace AZ
|
||||
return result;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
@@ -57,10 +57,10 @@ namespace AZ
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
{
|
||||
AZStd::fixed_string<MaxPathLength> absolutePath;
|
||||
AZStd::fixed_string<MaxPathLength> srcPath{ path };
|
||||
AZ::IO::FixedMaxPathString absolutePath;
|
||||
AZ::IO::FixedMaxPathString srcPath{ path };
|
||||
char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity());
|
||||
// Force update of the fixed_string size() value
|
||||
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace AZ
|
||||
return result;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace AZ::Utils
|
||||
{
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
+1
-1
@@ -376,7 +376,7 @@ namespace AZ::IO
|
||||
hardwareInfo.m_profile = driveList.size() == 1 ? driveList.front().m_profile : "Generic";
|
||||
hardwareInfo.m_platformData = AZStd::make_any<DriveList>(AZStd::move(driveList));
|
||||
|
||||
return true;
|
||||
return !driveList.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -15,13 +15,27 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Utils
|
||||
{
|
||||
namespace Utils
|
||||
void NativeErrorMessageBox(const char* title, const char* message)
|
||||
{
|
||||
void NativeErrorMessageBox(const char* title, const char* message)
|
||||
::MessageBox(0, message, title, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetEngineManifestPath()
|
||||
{
|
||||
char userProfileBuffer[AZ::IO::MaxPathLength] = {0};
|
||||
size_t variableSize = 0;
|
||||
auto err = getenv_s(&variableSize, userProfileBuffer, AZ::IO::MaxPathLength, "USERPROFILE");
|
||||
if (!err)
|
||||
{
|
||||
::MessageBox(0, message, title, MB_OK | MB_ICONERROR);
|
||||
AZ::IO::FixedMaxPath path{userProfileBuffer};
|
||||
path /= ".o3de";
|
||||
path /= "o3de_manifest.json";
|
||||
return path.Native();
|
||||
}
|
||||
} // namespace Utils
|
||||
} // namespace AZ
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace AZ::Utils
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
namespace AZ::Utils
|
||||
{
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
|
||||
{
|
||||
const char* pathToResources = [[[NSBundle mainBundle] resourcePath] UTF8String];
|
||||
return AZStd::fixed_string<MaxPathLength>::format("%s/assets", pathToResources);
|
||||
return AZ::IO::FixedMaxPathString::format("%s/assets", pathToResources);
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
|
||||
@@ -70,4 +70,5 @@ set(FILES
|
||||
AzCore/Utils/Utils_iOS.mm
|
||||
../Common/Apple/AzCore/Utils/Utils_Apple.cpp
|
||||
../Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp
|
||||
../Common/Unimplemented/AzCore/Utils/Utils_Unimplemented.cpp
|
||||
)
|
||||
|
||||
@@ -2391,6 +2391,27 @@ namespace UnitTest
|
||||
EXPECT_STREQ("AB BA", eraseIfTest.c_str());
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
class ImmutableStringFunctionsFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{};
|
||||
using StringTypesToTest = ::testing::Types<AZStd::string_view, AZStd::string, AZStd::fixed_string<1024>>;
|
||||
TYPED_TEST_CASE(ImmutableStringFunctionsFixture, StringTypesToTest);
|
||||
|
||||
TYPED_TEST(ImmutableStringFunctionsFixture, Contains_Succeeds)
|
||||
{
|
||||
TypeParam testStrValue{ R"(C:\o3de\Assets\Materials\texture\image.png)" };
|
||||
EXPECT_TRUE(testStrValue.contains("Materials"));
|
||||
EXPECT_FALSE(testStrValue.contains("Animations"));
|
||||
EXPECT_TRUE(testStrValue.contains('o'));
|
||||
EXPECT_FALSE(testStrValue.contains('Q'));
|
||||
|
||||
TypeParam typeParamEntry{ R"(texture)" };
|
||||
TypeParam typeParamNoEntry{ R"(physics)" };
|
||||
EXPECT_TRUE(testStrValue.contains(typeParamEntry));
|
||||
EXPECT_FALSE(testStrValue.contains(typeParamNoEntry));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* GetFormatString()
|
||||
{
|
||||
|
||||
@@ -54,8 +54,9 @@ namespace UnitTest
|
||||
AZ::SerializeContext* GetSerializeContext() override { return nullptr; }
|
||||
AZ::BehaviorContext* GetBehaviorContext() override { return m_behaviorContext; }
|
||||
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
|
||||
const char* GetExecutableFolder() const override { return nullptr; }
|
||||
const char* GetAppRoot() const override { return nullptr; }
|
||||
const char* GetEngineRoot() const override { return nullptr; }
|
||||
const char* GetExecutableFolder() const override { return nullptr; }
|
||||
AZ::Debug::DrillerManager* GetDrillerManager() override { return nullptr; }
|
||||
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
|
||||
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
|
||||
|
||||
@@ -416,6 +416,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReplaceAlias(AZ::IO::FixedMaxPath&, const AZ::IO::PathView&) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetAlias(const char*, const char*) override
|
||||
{
|
||||
}
|
||||
|
||||
@@ -58,22 +58,22 @@ namespace UnitTest
|
||||
|
||||
TEST_F(UtilsTestFixture, ConvertToAbsolutePath_OnRelativePath_Succeeds)
|
||||
{
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
EXPECT_TRUE(absolutePath);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTestFixture, ConvertToAbsolutePath_OnAbsolutePath_Succeeds)
|
||||
{
|
||||
char executableDirectory[AZ::Utils::MaxPathLength];
|
||||
AZ::Utils::ExecutablePathResult result = AZ::Utils::GetExecutableDirectory(executableDirectory, AZ_ARRAY_SIZE(executableDirectory));
|
||||
char executableDirectory[AZ::IO::MaxPathLength];
|
||||
AZ::Utils::ExecutablePathResult result = AZ::Utils::GetExecutableDirectory(executableDirectory, AZStd::size(executableDirectory));
|
||||
EXPECT_EQ(AZ::Utils::ExecutablePathResult::Success, result);
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
ASSERT_TRUE(absolutePath);
|
||||
|
||||
// Note that ConvertToAbsolutePath will perform a realpath on the result. The result of AZ::Utils::GetExecutableDirectory
|
||||
// uses AZ::Android::AndroidEnv::Get()->GetAppPrivateStoragePath() which will retrieve the storage path, but that path could
|
||||
// be symlinked, so we need to perform a real path on it before comparison
|
||||
char realExecutableDirectory[AZ::Utils::MaxPathLength];
|
||||
char realExecutableDirectory[AZ::IO::MaxPathLength];
|
||||
ASSERT_TRUE(realpath(executableDirectory, realExecutableDirectory));
|
||||
|
||||
EXPECT_STRCASEEQ(realExecutableDirectory, absolutePath->c_str());
|
||||
|
||||
@@ -27,16 +27,16 @@ namespace UnitTest
|
||||
|
||||
TEST_F(UtilsUnixLikeTestFixture, ConvertToAbsolutePath_OnRelativePath_Succeeds)
|
||||
{
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
EXPECT_TRUE(absolutePath);
|
||||
}
|
||||
|
||||
TEST_F(UtilsUnixLikeTestFixture, ConvertToAbsolutePath_OnAbsolutePath_Succeeds)
|
||||
{
|
||||
char executableDirectory[AZ::Utils::MaxPathLength];
|
||||
char executableDirectory[AZ::IO::MaxPathLength];
|
||||
AZ::Utils::ExecutablePathResult result = AZ::Utils::GetExecutableDirectory(executableDirectory, AZ_ARRAY_SIZE(executableDirectory));
|
||||
EXPECT_EQ(AZ::Utils::ExecutablePathResult::Success, result);
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
ASSERT_TRUE(absolutePath);
|
||||
EXPECT_STRCASEEQ(executableDirectory, absolutePath->c_str());
|
||||
}
|
||||
|
||||
@@ -53,23 +53,23 @@ namespace UnitTest
|
||||
|
||||
TEST_F(UtilsTestFixture, ConvertToAbsolutePath_OnInvalidPath_Fails)
|
||||
{
|
||||
AZStd::fixed_string<AZ::Utils::MaxPathLength> invalidPath{ "Z:\\" };
|
||||
invalidPath.append(AZ::Utils::MaxPathLength - invalidPath.size(), '@');
|
||||
AZ::IO::FixedMaxPathString invalidPath{ "Z:\\" };
|
||||
invalidPath.append(invalidPath.max_size() - invalidPath.size(), '@');
|
||||
EXPECT_FALSE(AZ::Utils::ConvertToAbsolutePath(invalidPath));
|
||||
}
|
||||
|
||||
TEST_F(UtilsTestFixture, ConvertToAbsolutePath_OnRelativePath_Succeeds)
|
||||
{
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath("./");
|
||||
EXPECT_TRUE(absolutePath);
|
||||
}
|
||||
|
||||
TEST_F(UtilsTestFixture, ConvertToAbsolutePath_OnAbsolutePath_Succeeds)
|
||||
{
|
||||
char executableDirectory[AZ::Utils::MaxPathLength];
|
||||
char executableDirectory[AZ::IO::MaxPathLength];
|
||||
AZ::Utils::ExecutablePathResult result = AZ::Utils::GetExecutableDirectory(executableDirectory, AZ_ARRAY_SIZE(executableDirectory));
|
||||
EXPECT_EQ(AZ::Utils::ExecutablePathResult::Success, result);
|
||||
AZStd::optional<AZStd::fixed_string<AZ::Utils::MaxPathLength>> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absolutePath = AZ::Utils::ConvertToAbsolutePath(executableDirectory);
|
||||
ASSERT_TRUE(absolutePath);
|
||||
EXPECT_STRCASEEQ(executableDirectory, absolutePath->c_str());
|
||||
}
|
||||
|
||||
@@ -1237,8 +1237,9 @@ namespace UnitTest
|
||||
SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); }
|
||||
BehaviorContext* GetBehaviorContext() override { return nullptr; }
|
||||
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
|
||||
const char* GetExecutableFolder() const override { return nullptr; }
|
||||
const char* GetAppRoot() const override { return nullptr; }
|
||||
const char* GetEngineRoot() const override { return nullptr; }
|
||||
const char* GetExecutableFolder() const override { return nullptr; }
|
||||
Debug::DrillerManager* GetDrillerManager() override { return nullptr; }
|
||||
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
|
||||
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Serialization/Json/DoubleSerializer.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
|
||||
@@ -142,6 +143,13 @@ namespace JsonSerializationTests
|
||||
constexpr static size_t ElementCount = 4;
|
||||
};
|
||||
|
||||
struct QuaternionDescriptor
|
||||
{
|
||||
using VectorType = AZ::Quaternion;
|
||||
using Serializer = AZ::JsonQuaternionSerializer;
|
||||
constexpr static size_t ElementCount = 4;
|
||||
};
|
||||
|
||||
using JsonMathVectorSerializerTypes = ::testing::Types <
|
||||
Vector2Descriptor, Vector3Descriptor, Vector4Descriptor>;
|
||||
TYPED_TEST_CASE(JsonMathVectorSerializerTests, JsonMathVectorSerializerTypes);
|
||||
|
||||
@@ -25,9 +25,11 @@ namespace UnitTest
|
||||
AZ::CommandLine cmd;
|
||||
EXPECT_FALSE(cmd.HasSwitch(""));
|
||||
EXPECT_EQ(cmd.GetNumSwitchValues("haha"), 0);
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
EXPECT_EQ(cmd.GetSwitchValue("haha", 0), AZStd::string());
|
||||
EXPECT_EQ(cmd.GetNumMiscValues(), 0);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
|
||||
EXPECT_EQ(cmd.GetNumMiscValues(), 0);
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
EXPECT_EQ(cmd.GetMiscValue(1), AZStd::string());
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
@@ -379,7 +381,6 @@ namespace UnitTest
|
||||
{
|
||||
AZ::CommandLine origCommandLine{ "-/" };
|
||||
|
||||
|
||||
const char* argValues[] =
|
||||
{
|
||||
"programname.exe", "/gamefolder", "/RemoteIp", "10.0.0.1", "-ScanFolders", R"(\a\b\c,\d\e\f)", "Foo", "Bat"
|
||||
@@ -396,20 +397,21 @@ namespace UnitTest
|
||||
for (const auto& commandLine : commandLines)
|
||||
{
|
||||
EXPECT_TRUE(commandLine.HasSwitch("gamefolder"));
|
||||
EXPECT_EQ(0, commandLine.GetNumSwitchValues("gamefolder"));
|
||||
EXPECT_EQ(1, commandLine.GetNumSwitchValues("gamefolder"));
|
||||
EXPECT_STREQ("", commandLine.GetSwitchValue("gamefolder", 0).c_str());
|
||||
|
||||
EXPECT_TRUE(commandLine.HasSwitch("remoteip"));
|
||||
EXPECT_EQ(1, commandLine.GetNumSwitchValues("remoteip"));
|
||||
EXPECT_EQ("10.0.0.1", commandLine.GetSwitchValue("remoteip", 0));
|
||||
EXPECT_STREQ("10.0.0.1", commandLine.GetSwitchValue("remoteip", 0).c_str());
|
||||
|
||||
EXPECT_TRUE(commandLine.HasSwitch("scanfolders"));
|
||||
EXPECT_EQ(2, commandLine.GetNumSwitchValues("scanfolders"));
|
||||
EXPECT_EQ(R"(\a\b\c)", commandLine.GetSwitchValue("scanfolders", 0));
|
||||
EXPECT_EQ(R"(\d\e\f)", commandLine.GetSwitchValue("scanfolders", 1));
|
||||
EXPECT_STREQ(R"(\a\b\c)", commandLine.GetSwitchValue("scanfolders", 0).c_str());
|
||||
EXPECT_STREQ(R"(\d\e\f)", commandLine.GetSwitchValue("scanfolders", 1).c_str());
|
||||
|
||||
EXPECT_EQ(2, commandLine.GetNumMiscValues());
|
||||
EXPECT_EQ("Foo", commandLine.GetMiscValue(0));
|
||||
EXPECT_EQ("Bat", commandLine.GetMiscValue(1));
|
||||
EXPECT_STREQ("Foo", commandLine.GetMiscValue(0).c_str());
|
||||
EXPECT_STREQ("Bat", commandLine.GetMiscValue(1).c_str());
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -82,23 +82,25 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
DumpSettings,
|
||||
SettingsRegistryMergeUtilsParamFixture,
|
||||
::testing::Values(
|
||||
DumpSettingsRegistryParams{ AZ::SettingsRegistryInterface::Format::JsonPatch,
|
||||
R"( [
|
||||
{ "op": "add", "path": "/Test", "value": { "Object": {} } },
|
||||
|
||||
{ "op": "add", "path": "/Test/Object/NullType", "value": null },
|
||||
{ "op": "add", "path": "/Test/Object/TrueType", "value": true },
|
||||
{ "op": "add", "path": "/Test/Object/FalseType", "value": false },
|
||||
{ "op": "add", "path": "/Test/Object/IntType", "value": -42 },
|
||||
{ "op": "add", "path": "/Test/Object/UIntType", "value": 42 },
|
||||
{ "op": "add", "path": "/Test/Object/DoubleType", "value": 42.0 },
|
||||
{ "op": "add", "path": "/Test/Object/StringType", "value": "Hello world" },
|
||||
|
||||
{ "op": "add", "path": "/Test/Array", "value": [ null, true, false, -42, 42, 42.0, "Hello world" ] }
|
||||
])",
|
||||
DumpSettingsRegistryParams
|
||||
{
|
||||
AZ::SettingsRegistryInterface::Format::JsonPatch,
|
||||
R"([)" "\n"
|
||||
R"( { "op": "add", "path": "/Test", "value": { "Object": {} } },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/NullType", "value": null },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/TrueType", "value": true },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/FalseType", "value": false },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/IntType", "value": -42 },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/UIntType", "value": 42 },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/DoubleType", "value": 42.0 },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Object/StringType", "value": "Hello world" },)" "\n"
|
||||
R"( { "op": "add", "path": "/Test/Array", "value": [ null, true, false, -42, 42, 42.0, "Hello world" ] })" "\n"
|
||||
R"(])" "\n",
|
||||
R"({"Test":{"Object":{"NullType":null,"TrueType":true,"FalseType":false,"IntType":-42,"UIntType":42)"
|
||||
R"(,"DoubleType":42.0,"StringType":"Hello world"},"Array":[null,true,false,-42,42,42.0,"Hello world"]}})",
|
||||
{ false,
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings
|
||||
{
|
||||
false,
|
||||
[](AZStd::string_view path)
|
||||
{
|
||||
AZStd::string_view prefixPath("/Test");
|
||||
@@ -106,26 +108,30 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
}
|
||||
}
|
||||
},
|
||||
DumpSettingsRegistryParams{ AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({
|
||||
"Test":
|
||||
{
|
||||
"Array0": [ 142, 188 ],
|
||||
"Array1": [ 242, 288 ],
|
||||
"Array2": [ 342, 388 ]
|
||||
}
|
||||
})",
|
||||
DumpSettingsRegistryParams
|
||||
{
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({)" "\n"
|
||||
R"( "Test":)" "\n"
|
||||
R"( {)" "\n"
|
||||
R"( "Array0": [ 142, 188 ], )" "\n"
|
||||
R"( "Array1": [ 242, 288 ], )" "\n"
|
||||
R"( "Array2": [ 342, 388 ] )" "\n"
|
||||
R"( })" "\n"
|
||||
R"(})" "\n",
|
||||
R"({"Test":{"Array0":[142,188],"Array1":[242,288],"Array2":[342,388]}})",
|
||||
{ false,
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings{ false,
|
||||
[](AZStd::string_view path)
|
||||
{
|
||||
AZStd::string_view prefixPath("/Test");
|
||||
return prefixPath.starts_with(path.substr(0, prefixPath.size()));
|
||||
}
|
||||
}
|
||||
},
|
||||
DumpSettingsRegistryParams{ AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({
|
||||
},
|
||||
DumpSettingsRegistryParams
|
||||
{
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({
|
||||
"Test":
|
||||
{
|
||||
"Array0": [ 142, 188 ],
|
||||
@@ -149,23 +155,27 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
R"( ])""\n"
|
||||
R"( })""\n"
|
||||
R"(})",
|
||||
{ true,
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings
|
||||
{
|
||||
true,
|
||||
[](AZStd::string_view path)
|
||||
{
|
||||
AZStd::string_view prefixPath("/Test");
|
||||
return prefixPath.starts_with(path.substr(0, prefixPath.size()));
|
||||
}
|
||||
}
|
||||
},
|
||||
DumpSettingsRegistryParams{ AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({
|
||||
},
|
||||
DumpSettingsRegistryParams
|
||||
{
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({
|
||||
"Test":
|
||||
{
|
||||
"Array0": [ 142, 188 ],
|
||||
"Array1": [ 242, 288 ],
|
||||
"Array2": [ 342, 388 ]
|
||||
}
|
||||
})",
|
||||
})",
|
||||
R"({)""\n"
|
||||
R"( "Array0": [)""\n"
|
||||
R"( 142,)""\n"
|
||||
@@ -180,7 +190,9 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
R"( 388)""\n"
|
||||
R"( ])""\n"
|
||||
R"(})",
|
||||
{ true,
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings
|
||||
{
|
||||
true,
|
||||
[](AZStd::string_view path)
|
||||
{
|
||||
AZStd::string_view prefixPath("/Test");
|
||||
@@ -188,8 +200,19 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
}
|
||||
},
|
||||
"/Test"
|
||||
}
|
||||
)
|
||||
},
|
||||
DumpSettingsRegistryParams{
|
||||
AZ::SettingsRegistryInterface::Format::JsonMergePatch,
|
||||
R"({)" "\n"
|
||||
R"( "Test":)" "\n"
|
||||
R"( {)" "\n"
|
||||
R"( "Array0": [ 142, 188 ])" "\n"
|
||||
R"( })" "\n"
|
||||
R"(})",
|
||||
R"({"Root":{"Path":{"Test":{"Array0":[142,188]}}}})",
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings{ false, {}, "/Root/Path/Test" },
|
||||
"/Test"
|
||||
})
|
||||
);
|
||||
|
||||
//! ConfigFile MergeUtils Test
|
||||
@@ -281,9 +304,13 @@ namespace SettingsRegistryMergeUtilsTests
|
||||
AZ::SettingsRegistryMergeUtils::ConfigParserSettings parserSettings;
|
||||
parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view
|
||||
{
|
||||
if (line.starts_with("--") || line.starts_with(';') || line.starts_with('#'))
|
||||
constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" };
|
||||
for (AZStd::string_view commentPrefix : commentPrefixes)
|
||||
{
|
||||
return {};
|
||||
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
|
||||
{
|
||||
return line.substr(0, commentOffset);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
};
|
||||
@@ -330,7 +357,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
-- android, ios, mac, linux, windows, etc...
|
||||
-- or left unprefixed, to set all platforms not specified. The rules apply in the order they're declared
|
||||
|
||||
sys_game_folder=TestProject
|
||||
project_path=TestProject
|
||||
|
||||
-- remote_filesystem - enable Virtual File System (VFS)
|
||||
-- This feature allows a remote instance of the game to run off assets
|
||||
@@ -363,7 +390,7 @@ mac_assets = osx_gl
|
||||
-- Example: 192.168.1.0/24 will allow any address starting with 192.168.1.
|
||||
-- Example: 192.168.0.0/16 will allow any address starting with 192.168.
|
||||
-- Example: 192.168.0.0/8 will allow any address starting with 192.
|
||||
-- white_list =
|
||||
-- allowed_list =
|
||||
|
||||
-- IP address and optionally port of the asset processor.
|
||||
-- Set your PC IP here: (and uncomment the next line)
|
||||
@@ -405,7 +432,7 @@ mac_wait_for_connect=0
|
||||
|
||||
)"
|
||||
, AZStd::fixed_vector<ConfigFileParams::SettingsKeyValuePair, 20>{
|
||||
ConfigFileParams::SettingsKeyValuePair{"/sys_game_folder", AZStd::string_view{"TestProject"}},
|
||||
ConfigFileParams::SettingsKeyValuePair{"/project_path", AZStd::string_view{"TestProject"}},
|
||||
ConfigFileParams::SettingsKeyValuePair{"/remote_filesystem", AZ::s64{0}},
|
||||
ConfigFileParams::SettingsKeyValuePair{"/android_remote_filesystem", AZ::s64{0}},
|
||||
ConfigFileParams::SettingsKeyValuePair{"/ios_remote_filesystem", AZ::s64{0}},
|
||||
@@ -428,7 +455,7 @@ mac_wait_for_connect=0
|
||||
// Parses a fake AssetProcessorPlatformConfig file which contains sections headers
|
||||
// and does not end with a newline
|
||||
ConfigFileParams{ "fake_AssetProcessorPlatformConfig.ini", R"(
|
||||
; ---- Enable/Disable platforms for the entire project. AssetProcessor will automatically add the current platform by default.
|
||||
; ---- Enable/Disable platforms for the entire project. AssetProcessor will automatically add the current platform by default.
|
||||
|
||||
; PLATFORM DEFINITIONS
|
||||
; [Platform (unique identifier)]
|
||||
@@ -452,7 +479,7 @@ test_asset_processor_tag = test_value
|
||||
tags=tools,renderer,dx12,vulkan
|
||||
|
||||
[Platform es3]
|
||||
tags=android,mobile,renderer,vulkan
|
||||
tags=android,mobile,renderer,vulkan ; With Comments at the end
|
||||
|
||||
[Platform ios]
|
||||
tags=mobile,renderer,metal
|
||||
@@ -489,76 +516,30 @@ tags=tools,renderer,metal)"
|
||||
TEST_F(SettingsRegistryMergeUtilsCommandLineFixture, CommandLineArguments_MergeToSettingsRegistry_Success)
|
||||
{
|
||||
AZ::CommandLine commandLine;
|
||||
commandLine.Parse({ "programname.exe", "--gamefolder", "--RemoteIp", "10.0.0.1", "--ScanFolders", R"(\a\b\c,\d\e\f)", "Foo", "Bat" });
|
||||
commandLine.Parse({ "programname.exe", "--project-path", "--RemoteIp", "10.0.0.1", "--ScanFolders", R"(\a\b\c,\d\e\f)", "Foo", "Bat" });
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_StoreCommandLine(*m_registry, commandLine);
|
||||
AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(*m_registry, commandLine);
|
||||
// Clear the CommandLine instance
|
||||
commandLine = {};
|
||||
|
||||
struct CommandLineVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPointer, AZStd::string_view,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override
|
||||
{
|
||||
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
|
||||
{
|
||||
// Strip off the last key of the jsonPointer and check if the path is the command line root switch key
|
||||
// i.e "/../CommandLine/Switches
|
||||
AZStd::optional<AZStd::string_view> optionName = AZ::StringFunc::TokenizeLast(jsonPointer, '/');
|
||||
if (jsonPointer == AZ::SettingsRegistryMergeUtils::CommandLineSwitchRootKey && optionName && !optionName->empty())
|
||||
{
|
||||
// Add a empty mapping of option name to values if it is not in the map already
|
||||
m_optionArguments[*optionName];
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(*m_registry, commandLine));
|
||||
|
||||
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
|
||||
}
|
||||
ASSERT_TRUE(commandLine.HasSwitch("project-path"));
|
||||
EXPECT_EQ(1, commandLine.GetNumSwitchValues("project-path"));
|
||||
EXPECT_STREQ("", commandLine.GetSwitchValue("project-path", 0).c_str());
|
||||
|
||||
void Visit(AZStd::string_view jsonPointer, AZStd::string_view, AZ::SettingsRegistryInterface::Type
|
||||
, AZStd::string_view value) override
|
||||
{
|
||||
// Strip off the last key from the jsonPointer and return that in the option name parameter
|
||||
AZStd::optional<AZStd::string_view> optionName = AZ::StringFunc::TokenizeLast(jsonPointer, '/');
|
||||
if (jsonPointer == AZ::SettingsRegistryMergeUtils::CommandLineMiscValuesRootKey)
|
||||
{
|
||||
m_positionalArguments.push_back(value);
|
||||
}
|
||||
else if (jsonPointer.starts_with(AZ::SettingsRegistryMergeUtils::CommandLineSwitchRootKey) && optionName)
|
||||
{
|
||||
// Option arguments are stored in array indices, underneath the option key
|
||||
// Therefore remove another token from the jsonPointer to retrieve the actual option name
|
||||
if (optionName = AZ::StringFunc::TokenizeLast(jsonPointer, '/'); optionName && !optionName->empty())
|
||||
{
|
||||
// Add an entry for the option with the specified value
|
||||
m_optionArguments[*optionName].push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(commandLine.HasSwitch("remoteip"));
|
||||
ASSERT_EQ(1, commandLine.GetNumSwitchValues("remoteip"));
|
||||
EXPECT_STREQ("10.0.0.1", commandLine.GetSwitchValue("remoteip", 0).c_str());
|
||||
|
||||
AZ::CommandLine::ParamMap m_optionArguments;
|
||||
AZ::CommandLine::ParamContainer m_positionalArguments;
|
||||
};
|
||||
ASSERT_TRUE(commandLine.HasSwitch("scanfolders"));
|
||||
ASSERT_EQ(2, commandLine.GetNumSwitchValues("scanfolders"));
|
||||
EXPECT_STREQ(R"(\a\b\c)", commandLine.GetSwitchValue("scanfolders", 0).c_str());
|
||||
EXPECT_STREQ(R"(\d\e\f)", commandLine.GetSwitchValue("scanfolders", 1).c_str());
|
||||
|
||||
CommandLineVisitor commandLineVisitor;
|
||||
EXPECT_TRUE(m_registry->Visit(commandLineVisitor, AZ::SettingsRegistryMergeUtils::CommandLineRootKey));
|
||||
EXPECT_EQ(3, commandLineVisitor.m_optionArguments.size());
|
||||
auto optionIter = commandLineVisitor.m_optionArguments.find("gamefolder");
|
||||
ASSERT_NE(commandLineVisitor.m_optionArguments.end(), optionIter);
|
||||
EXPECT_EQ(0, optionIter->second.size());
|
||||
|
||||
optionIter = commandLineVisitor.m_optionArguments.find("remoteip");
|
||||
ASSERT_NE(commandLineVisitor.m_optionArguments.end(), optionIter);
|
||||
ASSERT_EQ(1, optionIter->second.size());
|
||||
EXPECT_STREQ("10.0.0.1", optionIter->second[0].c_str());
|
||||
|
||||
optionIter = commandLineVisitor.m_optionArguments.find("scanfolders");
|
||||
ASSERT_NE(commandLineVisitor.m_optionArguments.end(), optionIter);
|
||||
ASSERT_EQ(2, optionIter->second.size());
|
||||
EXPECT_STREQ(R"(\a\b\c)", optionIter->second[0].c_str());
|
||||
EXPECT_STREQ(R"(\d\e\f)", optionIter->second[1].c_str());
|
||||
|
||||
ASSERT_EQ(2, commandLineVisitor.m_positionalArguments.size());
|
||||
EXPECT_STREQ("Foo", commandLineVisitor.m_positionalArguments[0].c_str());
|
||||
EXPECT_STREQ("Bat", commandLineVisitor.m_positionalArguments[1].c_str());
|
||||
ASSERT_EQ(3, commandLine.GetNumMiscValues());
|
||||
EXPECT_STREQ("programname.exe", commandLine.GetMiscValue(0).c_str());
|
||||
EXPECT_STREQ("Foo", commandLine.GetMiscValue(1).c_str());
|
||||
EXPECT_STREQ("Bat", commandLine.GetMiscValue(2).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +499,38 @@ namespace SettingsRegistryTests
|
||||
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test/Object/Type"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, VisitWithVisitor_EndOfPathArguments_MatchesValueNameArgument_ForAllIterations)
|
||||
{
|
||||
// Validate that every invocation of the Traverse and Visit command supplies a 'path' parameter
|
||||
// whose end matches that of the 'valueName' parameter
|
||||
AZStd::string json{
|
||||
R"({
|
||||
"Test":
|
||||
{
|
||||
"Object":{ "Type": "TestString" }
|
||||
}
|
||||
})"
|
||||
};
|
||||
ASSERT_TRUE(this->m_registry->MergeSettings(json.c_str(), AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
struct : public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction, AZ::SettingsRegistryInterface::Type) override
|
||||
{
|
||||
EXPECT_TRUE(path.ends_with(valueName));
|
||||
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
|
||||
}
|
||||
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type , AZStd::string_view)override
|
||||
{
|
||||
EXPECT_TRUE(path.ends_with(valueName));
|
||||
}
|
||||
} visitor;
|
||||
|
||||
EXPECT_TRUE(this->m_registry->Visit(visitor, ""));
|
||||
EXPECT_TRUE(this->m_registry->Visit(visitor, "/Test"));
|
||||
}
|
||||
|
||||
//
|
||||
// Object
|
||||
//
|
||||
@@ -730,7 +762,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Object", "Object", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Object/NullType", "NullType", SRI::Type::Null, SRI::VisitAction::Value),
|
||||
@@ -752,7 +784,7 @@ namespace SettingsRegistryTests
|
||||
RegistryEntry("/Test/Array/6", "6", SRI::Type::String, SRI::VisitAction::Value),
|
||||
RegistryEntry("/Test/Array", "Array", SRI::Type::Array, SRI::VisitAction::End),
|
||||
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::End)
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
|
||||
};
|
||||
|
||||
Visit(expected, "/Test");
|
||||
@@ -778,11 +810,11 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Layer1/Layer2", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Layer1/Layer2/Layer3/StringType", "StringType", SRI::Type::String, SRI::VisitAction::Value),
|
||||
RegistryEntry("/Layer1/Layer2/Layer3", "Layer3", SRI::Type::Object, SRI::VisitAction::End),
|
||||
RegistryEntry("/Layer1/Layer2", "", SRI::Type::Object, SRI::VisitAction::End)
|
||||
RegistryEntry("/Layer1/Layer2", "Layer2", SRI::Type::Object, SRI::VisitAction::End)
|
||||
};
|
||||
|
||||
Visit(expected, "/Layer1/Layer2");
|
||||
@@ -806,9 +838,9 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Object/0/0", "", SRI::Type::Array, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Object/0/0/0", "0", SRI::Type::String, SRI::VisitAction::Value),
|
||||
RegistryEntry("/Object/0/0", "", SRI::Type::Array, SRI::VisitAction::End)
|
||||
RegistryEntry("/Object/0/0", "0", SRI::Type::Array, SRI::VisitAction::End)
|
||||
};
|
||||
|
||||
Visit(expected, "/Object/0/0");
|
||||
@@ -830,7 +862,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -842,7 +874,7 @@ namespace SettingsRegistryTests
|
||||
RegistryEntry("/Test/Object2/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
RegistryEntry("/Test/Object2", "Object2", SRI::Type::Object, SRI::VisitAction::End),
|
||||
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::End)
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
|
||||
};
|
||||
|
||||
Visit(expected, "/Test");
|
||||
@@ -864,7 +896,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -878,7 +910,7 @@ namespace SettingsRegistryTests
|
||||
RegistryEntry("/Test/Array2/1", "1", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
RegistryEntry("/Test/Array2", "Array2", SRI::Type::Array, SRI::VisitAction::End),
|
||||
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::End)
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::End)
|
||||
};
|
||||
|
||||
Visit(expected, "/Test");
|
||||
@@ -900,7 +932,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -928,7 +960,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Object0", "Object0", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Object0/Field", "Field", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -954,7 +986,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -984,7 +1016,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
AZStd::vector<RegistryEntry> expected =
|
||||
{
|
||||
RegistryEntry("/Test", "", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test", "Test", SRI::Type::Object, SRI::VisitAction::Begin),
|
||||
|
||||
RegistryEntry("/Test/Array0", "Array0", SRI::Type::Array, SRI::VisitAction::Begin),
|
||||
RegistryEntry("/Test/Array0/0", "0", SRI::Type::Integer, SRI::VisitAction::Value),
|
||||
@@ -1700,11 +1732,7 @@ namespace SettingsRegistryTests
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0/Path"));
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_MERGESETTINGSFOLDER_CONFLICTINGSPECIALIZATIONS
|
||||
TEST_F(SettingsRegistryTest, DISABLED_MergeSettingsFolder_ConflictingSpecializations_ReportsErrorAndReturnsFalse)
|
||||
#else
|
||||
TEST_F(SettingsRegistryTest, MergeSettingsFolder_ConflictingSpecializations_ReportsErrorAndReturnsFalse)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_MERGESETTINGSFOLDER_CONFLICTINGSPECIALIZATIONS
|
||||
{
|
||||
CreateTestFile("Memory.test.editor.setreg", "{}");
|
||||
CreateTestFile("Memory.editor.test.setreg", "{}");
|
||||
|
||||
@@ -22,6 +22,35 @@ namespace AZ
|
||||
|
||||
using StringFuncTest = AllocatorsFixture;
|
||||
|
||||
TEST_F(StringFuncTest, Equal_CaseSensitive_OnNonNullTerminatedStringView_Success)
|
||||
{
|
||||
constexpr AZStd::string_view testString1 = "Hello World IceCream";
|
||||
constexpr AZStd::string_view testString2 = "IceCream World Hello";
|
||||
constexpr bool caseSensitive = true;
|
||||
EXPECT_TRUE(AZ::StringFunc::Equal(testString1.substr(6,5), testString2.substr(9,5), caseSensitive));
|
||||
}
|
||||
TEST_F(StringFuncTest, Equal_CaseInsensitive_OnNonNullTerminatedStringView_Success)
|
||||
{
|
||||
constexpr AZStd::string_view testString1 = "Hello World IceCream";
|
||||
constexpr AZStd::string_view testString2 = "IceCream woRLd Hello";
|
||||
constexpr bool caseSensitive = false;
|
||||
EXPECT_TRUE(AZ::StringFunc::Equal(testString1.substr(6, 5), testString2.substr(9, 5), caseSensitive));
|
||||
}
|
||||
TEST_F(StringFuncTest, Equal_CaseSensitive_OnNonNullTerminatedStringView_WithDifferentCases_Fails)
|
||||
{
|
||||
constexpr AZStd::string_view testString1 = "Hello World IceCream";
|
||||
constexpr AZStd::string_view testString2 = "IceCream woRLd Hello";
|
||||
constexpr bool caseSensitive = true;
|
||||
EXPECT_FALSE(AZ::StringFunc::Equal(testString1.substr(6, 5), testString2.substr(9, 5), caseSensitive));
|
||||
}
|
||||
TEST_F(StringFuncTest, Equal_OnNonNullTerminatedStringView_WithDifferentSize_Fails)
|
||||
{
|
||||
constexpr AZStd::string_view testString1 = "Hello World IceCream";
|
||||
constexpr AZStd::string_view testString2 = "IceCream World Hello";
|
||||
|
||||
EXPECT_FALSE(AZ::StringFunc::Equal(testString1.substr(6, 6), testString2.substr(9, 5)));
|
||||
}
|
||||
|
||||
// Strip out any trailing path separators
|
||||
|
||||
TEST_F(StringFuncTest, Strip_ValidInputExtraEndingPathSeparators_Success)
|
||||
|
||||
@@ -70,18 +70,12 @@ namespace AzFramework
|
||||
/// Make path relative to the provided root.
|
||||
virtual void MakePathRelative(AZStd::string& /*fullPath*/, const char* /*rootPath*/) {}
|
||||
|
||||
/// Retrieves the asset root path for the application.
|
||||
virtual const char* GetAssetRoot() const { return nullptr; }
|
||||
|
||||
/// Gets the engine root path where the modules for the current engine are located.
|
||||
virtual const char* GetEngineRoot() const { return nullptr; }
|
||||
|
||||
/// Retrieves the app root path for the application.
|
||||
virtual const char* GetAppRoot() const { return nullptr; }
|
||||
|
||||
/// Sets the asset root path for the application.
|
||||
virtual void SetAssetRoot(const char* /*assetRoot*/) {}
|
||||
|
||||
#pragma push_macro("GetCommandLine")
|
||||
#undef GetCommandLine
|
||||
/// Get the Command Line arguments passed in.
|
||||
@@ -117,8 +111,8 @@ namespace AzFramework
|
||||
/// Resolve a path thats relative to the engine folder to an absolute path
|
||||
virtual void ResolveEnginePath(AZStd::string& /*engineRelativePath*/) const {}
|
||||
|
||||
/// Calculate the branch token from the current application's asset root
|
||||
virtual void CalculateBranchTokenForAppRoot(AZStd::string& token) const = 0;
|
||||
/// Calculate the branch token from the current application's engine root
|
||||
virtual void CalculateBranchTokenForEngineRoot(AZStd::string& token) const = 0;
|
||||
|
||||
/*!
|
||||
* Returns a Type Uuid of the component for the given componentId and entityId.
|
||||
|
||||
@@ -154,12 +154,12 @@ namespace AzFramework
|
||||
return AZ::Success(AZStd::move(loadedDescriptor));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Application::Application()
|
||||
: Application(nullptr, nullptr)
|
||||
: Application(nullptr, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Application::Application(int* argc, char*** argv)
|
||||
: ComponentApplication(
|
||||
argc ? *argc : 0,
|
||||
@@ -188,7 +188,6 @@ namespace AzFramework
|
||||
SetFileIOAliases();
|
||||
}
|
||||
|
||||
|
||||
ApplicationRequests::Bus::Handler::BusConnect();
|
||||
AZ::UserSettingsFileLocatorBus::Handler::BusConnect();
|
||||
NetSystemRequestBus::Handler::BusConnect();
|
||||
@@ -238,14 +237,8 @@ namespace AzFramework
|
||||
{
|
||||
AZ::Entity* systemEntity = Create(descriptor, startupParameters);
|
||||
|
||||
// Attempt to use the "CacheGameFolder" key in the settings registry to set the asset root
|
||||
// If that fails, fallback to using the App root as the asset root
|
||||
if (!m_settingsRegistry->Get(m_assetRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheGameFolder))
|
||||
{
|
||||
m_assetRoot = GetAppRoot();
|
||||
}
|
||||
// Sets FileIOAliases again in case the App root was overridden by the
|
||||
// startupParamets in ComponentApplication::Create
|
||||
// startupParameters in ComponentApplication::Create
|
||||
SetFileIOAliases();
|
||||
|
||||
if (systemEntity)
|
||||
@@ -268,7 +261,7 @@ namespace AzFramework
|
||||
void Application::PreModuleLoad()
|
||||
{
|
||||
// Calculate the engine root by reading the engine.json file
|
||||
AZStd::string engineJsonPath = AZStd::string_view{ m_appRoot };
|
||||
AZStd::string engineJsonPath = AZStd::string_view{ m_engineRoot };
|
||||
engineJsonPath += s_engineConfigFileName;
|
||||
AzFramework::StringFunc::Path::Normalize(engineJsonPath);
|
||||
AZ::IO::LocalFileIO localFileIO;
|
||||
@@ -276,7 +269,7 @@ namespace AzFramework
|
||||
|
||||
if (readJsonResult.IsSuccess())
|
||||
{
|
||||
SetRootPath(RootPathType::EngineRoot, m_appRoot.c_str());
|
||||
SetRootPath(RootPathType::EngineRoot, m_engineRoot.c_str());
|
||||
AZ_TracePrintf(s_azFrameworkWarningWindow, "Engine Path: %s\n", m_engineRoot.c_str());
|
||||
}
|
||||
else
|
||||
@@ -410,17 +403,16 @@ namespace AzFramework
|
||||
azrtti_typeid<AzFramework::StreamingInstall::StreamingInstallSystemComponent>(),
|
||||
azrtti_typeid<AzFramework::SpawnableSystemComponent>(),
|
||||
AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}"), // ScriptDebugAgent
|
||||
});
|
||||
});
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
AZStd::string Application::ResolveFilePath(AZ::u32 providerId)
|
||||
// UserSettingsFileLocatorBus
|
||||
AZStd::string Application::ResolveFilePath([[maybe_unused]] AZ::u32 providerId)
|
||||
{
|
||||
(void)providerId;
|
||||
|
||||
AZStd::string result;
|
||||
AzFramework::StringFunc::Path::Join(GetAppRoot(), "UserSettings.xml", result, /*bCaseInsenitive*/false);
|
||||
AzFramework::StringFunc::Path::Join(GetEngineRoot(), "UserSettings.xml", result, /*bCaseInsenitive*/false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -451,11 +443,6 @@ namespace AzFramework
|
||||
outModules.emplace_back(aznew AzFrameworkModule());
|
||||
}
|
||||
|
||||
const char* Application::GetAssetRoot() const
|
||||
{
|
||||
return m_assetRoot.c_str();
|
||||
}
|
||||
|
||||
const char* Application::GetAppRoot() const
|
||||
{
|
||||
return m_appRoot.c_str();
|
||||
@@ -516,21 +503,15 @@ namespace AzFramework
|
||||
engineRelativePath = fullPath;
|
||||
}
|
||||
|
||||
void Application::CalculateBranchTokenForAppRoot(AZStd::string& token) const
|
||||
void Application::CalculateBranchTokenForEngineRoot(AZStd::string& token) const
|
||||
{
|
||||
AzFramework::StringFunc::AssetPath::CalculateBranchToken(AZStd::string(m_appRoot), token);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void Application::SetAssetRoot(const char* assetRoot)
|
||||
{
|
||||
SetRootPath(RootPathType::AssetRoot, assetRoot);
|
||||
AzFramework::StringFunc::AssetPath::CalculateBranchToken(AZStd::string(m_engineRoot), token);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void Application::MakePathRootRelative(AZStd::string& fullPath)
|
||||
{
|
||||
MakePathRelative(fullPath, m_appRoot.c_str());
|
||||
MakePathRelative(fullPath, m_engineRoot.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -538,7 +519,12 @@ namespace AzFramework
|
||||
{
|
||||
// relative file paths wrt AssetRoot are always lowercase
|
||||
AZStd::to_lower(fullPath.begin(), fullPath.end());
|
||||
MakePathRelative(fullPath, m_assetRoot.c_str());
|
||||
AZStd::string cacheAssetPath;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(cacheAssetPath, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
|
||||
}
|
||||
MakePathRelative(fullPath, cacheAssetPath.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -592,8 +578,8 @@ namespace AzFramework
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
void Application::PumpSystemEventLoopWhileDoingWorkInNewThread(const AZStd::chrono::milliseconds& eventPumpFrequency,
|
||||
const AZStd::function<void()>& workForNewThread,
|
||||
const char* newThreadName)
|
||||
const AZStd::function<void()>& workForNewThread,
|
||||
const char* newThreadName)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
@@ -604,7 +590,7 @@ namespace AzFramework
|
||||
AZStd::thread newThread([&workForNewThread, &binarySemaphore, &newThreadName]
|
||||
{
|
||||
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzFramework,
|
||||
"Application::PumpSystemEventLoopWhileDoingWorkInNewThread:ThreadWorker %s", newThreadName);
|
||||
"Application::PumpSystemEventLoopWhileDoingWorkInNewThread:ThreadWorker %s", newThreadName);
|
||||
|
||||
workForNewThread();
|
||||
binarySemaphore.release();
|
||||
@@ -615,7 +601,7 @@ namespace AzFramework
|
||||
}
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzFramework,
|
||||
"Application::PumpSystemEventLoopWhileDoingWorkInNewThread:WaitOnThread %s", newThreadName);
|
||||
"Application::PumpSystemEventLoopWhileDoingWorkInNewThread:WaitOnThread %s", newThreadName);
|
||||
newThread.join();
|
||||
}
|
||||
|
||||
@@ -678,18 +664,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RootPathType::AssetRoot:
|
||||
{
|
||||
AZ_Assert(sourceLen < m_assetRoot.max_size(), "String overflow for Asset Root: %s", source);
|
||||
m_assetRoot = source;
|
||||
|
||||
AZStd::replace(std::begin(m_assetRoot), std::end(m_assetRoot), AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
if (appendTrailingPathSep)
|
||||
{
|
||||
m_assetRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RootPathType::EngineRoot:
|
||||
{
|
||||
AZ_Assert(sourceLen < m_engineRoot.max_size(), "String overflow for Engine Root: %s", source);
|
||||
@@ -706,95 +680,102 @@ namespace AzFramework
|
||||
AZ_Assert(false, "Invalid RootPathType (%d)", static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
|
||||
{
|
||||
// The number of max attempts ultimately dictates the number of Lumberyard instances that can run
|
||||
// simultaneously. This should be a reasonably high number so that it doesn't artificially limit
|
||||
// the number of instances (ex: parallel level exports via multiple Editor runs). It also shouldn't
|
||||
// be set *infinitely* high - each cache folder is GBs in size, and finding a free directory is a
|
||||
// linear search, so the more instances we allow, the longer the search will take.
|
||||
// 128 seems like a reasonable compromise.
|
||||
constexpr int maxAttempts = 128;
|
||||
|
||||
constexpr const char* userCachePathFilename{ "Cache" };
|
||||
AZ::IO::FixedMaxPath userCachePath = cacheUserPath / userCachePathFilename;
|
||||
#if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
|
||||
int attemptNumber;
|
||||
for (attemptNumber = 0; attemptNumber < maxAttempts; ++attemptNumber)
|
||||
{
|
||||
if (attemptNumber != 0)
|
||||
{
|
||||
userCachePath.ReplaceFilename(AZStd::string_view{ AZ::IO::FixedMaxPathString::format("%s%i", userCachePathFilename, attemptNumber) });
|
||||
}
|
||||
|
||||
// if the directory already exists, check for locked file
|
||||
auto cacheLockFilePath = userCachePath / "lockfile.txt";
|
||||
|
||||
constexpr auto LockFileMode = AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY
|
||||
| AZ::IO::SystemFile::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH;
|
||||
if (AZ::IO::SystemFile lockFileHandle; lockFileHandle.Open(cacheLockFilePath.c_str(), LockFileMode))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attemptNumber >= maxAttempts)
|
||||
{
|
||||
userCachePath.ReplaceFilename(userCachePathFilename);
|
||||
AZ_TracePrintf("Application", "Couldn't find a valid asset cache folder after %i attempts."
|
||||
" Setting cache folder to %s\n", maxAttempts, userCachePath.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
fileIoBase.SetAlias("@usercache@", userCachePath.c_str());
|
||||
}
|
||||
|
||||
void Application::SetFileIOAliases()
|
||||
{
|
||||
if (AZ::IO::FileIOBase::GetInstance() == m_archiveFileIO.get())
|
||||
if (m_archiveFileIO)
|
||||
{
|
||||
auto fileIoBase = m_archiveFileIO.get();
|
||||
// Set up the default file aliases based on the settings registry
|
||||
fileIoBase->SetAlias("@root@", GetAppRoot());
|
||||
fileIoBase->SetAlias("@assets@", GetAssetRoot());
|
||||
fileIoBase->SetAlias("@engroot@", GetAppRoot());
|
||||
fileIoBase->SetAlias("@devroot@", GetAppRoot());
|
||||
fileIoBase->SetAlias("@devassets@", GetAppRoot());
|
||||
fileIoBase->SetAlias("@assets@", "");
|
||||
fileIoBase->SetAlias("@root@", GetEngineRoot());
|
||||
fileIoBase->SetAlias("@engroot@", GetEngineRoot());
|
||||
fileIoBase->SetAlias("@projectroot@", GetEngineRoot());
|
||||
fileIoBase->SetAlias("@exefolder@", GetExecutableFolder());
|
||||
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString pathAliases;
|
||||
if (m_settingsRegistry->Get(pathAliases, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
|
||||
AZ::IO::FixedMaxPath pathAliases;
|
||||
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
|
||||
{
|
||||
fileIoBase->SetAlias("@root@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@projectcache@", pathAliases.c_str());
|
||||
}
|
||||
pathAliases.clear();
|
||||
if (m_settingsRegistry->Get(pathAliases, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheGameFolder))
|
||||
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
|
||||
{
|
||||
fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@assets@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@
|
||||
}
|
||||
pathAliases.clear();
|
||||
if (m_settingsRegistry->Get(pathAliases, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
fileIoBase->SetAlias("@engroot@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@devroot@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@
|
||||
}
|
||||
pathAliases.clear();
|
||||
if (m_settingsRegistry->Get(pathAliases, AZ::SettingsRegistryMergeUtils::FilePathKey_SourceGameFolder))
|
||||
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
|
||||
{
|
||||
fileIoBase->SetAlias("@devassets@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@
|
||||
fileIoBase->SetAlias("@projectroot@", pathAliases.c_str());
|
||||
fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath projectUserPath;
|
||||
if (m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
{
|
||||
auto userPath = AZ::StringFunc::Path::FixedString::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "user", fileIoBase->GetAlias("@root@"));
|
||||
fileIoBase->SetAlias("@user@", userPath.c_str());
|
||||
fileIoBase->SetAlias("@user@", projectUserPath.c_str());
|
||||
AZ::IO::FixedMaxPath projectLogPath = projectUserPath / "log";
|
||||
fileIoBase->SetAlias("@log@", projectLogPath.c_str());
|
||||
fileIoBase->CreatePath(projectLogPath.c_str()); // Create the log directory at this point
|
||||
|
||||
CreateUserCache(projectUserPath, *fileIoBase);
|
||||
}
|
||||
|
||||
{
|
||||
auto logPath = AZ::StringFunc::Path::FixedString::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "log", fileIoBase->GetAlias("@user@"));
|
||||
fileIoBase->SetAlias("@log@", logPath.c_str());
|
||||
|
||||
// Create the Log folder if it doesn't exist
|
||||
fileIoBase->CreatePath("@log@");
|
||||
}
|
||||
|
||||
auto cachePathOriginal = AZ::StringFunc::Path::FixedString::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "cache", fileIoBase->GetAlias("@user@"));
|
||||
// The number of max attempts ultimately dictates the number of Lumberyard instances that can run
|
||||
// simultaneously. This should be a reasonably high number so that it doesn't artificially limit
|
||||
// the number of instances (ex: parallel level exports via multiple Editor runs). It also shouldn't
|
||||
// be set *infinitely* high - each cache folder is GBs in size, and finding a free directory is a
|
||||
// linear search, so the more instances we allow, the longer the search will take.
|
||||
// 128 seems like a reasonable compromise.
|
||||
constexpr int maxAttempts = 128;
|
||||
|
||||
AZ::StringFunc::Path::FixedString cachePath(cachePathOriginal);
|
||||
|
||||
#if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
|
||||
int attemptNumber;
|
||||
for (attemptNumber = 0; attemptNumber < maxAttempts; ++attemptNumber)
|
||||
{
|
||||
if (attemptNumber != 0)
|
||||
{
|
||||
cachePath = AZ::StringFunc::Path::FixedString::format("%s%i", cachePathOriginal.c_str(), attemptNumber);
|
||||
}
|
||||
|
||||
fileIoBase->CreatePath(cachePath.c_str());
|
||||
// if the directory already exists, check for locked file
|
||||
auto cacheLockFilePath = AZ::StringFunc::Path::FixedString::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "lockfile.txt", cachePath.c_str());
|
||||
|
||||
AZ::IO::HandleType lockFileHandle;
|
||||
if (fileIoBase->Open(cacheLockFilePath.c_str(), AZ::IO::OpenMode::ModeWrite, lockFileHandle))
|
||||
{
|
||||
fileIoBase->Close(lockFileHandle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attemptNumber >= maxAttempts)
|
||||
{
|
||||
cachePath = cachePathOriginal;
|
||||
AZ_TracePrintf("Application", "Couldn't find a valid asset cache folder after %i attempts."
|
||||
" Setting cache folder to cachePath %s\n", maxAttempts, cachePath.c_str());
|
||||
}
|
||||
#endif
|
||||
fileIoBase->SetAlias("@cache@", cachePath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,11 +100,10 @@ namespace AzFramework
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! ApplicationRequests::Bus::Handler
|
||||
const char* GetAssetRoot() const override;
|
||||
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
|
||||
const char* GetAppRoot() const override;
|
||||
void ResolveEnginePath(AZStd::string& engineRelativePath) const override;
|
||||
void CalculateBranchTokenForAppRoot(AZStd::string& token) const override;
|
||||
void CalculateBranchTokenForEngineRoot(AZStd::string& token) const override;
|
||||
|
||||
#pragma push_macro("GetCommandLine")
|
||||
#undef GetCommandLine
|
||||
@@ -112,7 +111,6 @@ namespace AzFramework
|
||||
#pragma pop_macro("GetCommandLine")
|
||||
const CommandLine* GetApplicationCommandLine() override { return &m_commandLine; }
|
||||
|
||||
void SetAssetRoot(const char* assetRoot) override;
|
||||
void MakePathRootRelative(AZStd::string& fullPath) override;
|
||||
void MakePathAssetRootRelative(AZStd::string& fullPath) override;
|
||||
void MakePathRelative(AZStd::string& fullPath, const char* rootPath) override;
|
||||
@@ -180,9 +178,6 @@ namespace AzFramework
|
||||
|
||||
AZ::StringFunc::Path::FixedString m_configFilePath;
|
||||
|
||||
AZ::StringFunc::Path::FixedString m_assetRoot;
|
||||
AZ::StringFunc::Path::FixedString m_engineRoot; ///> Location of the engine root folder that this application is based on
|
||||
|
||||
AZStd::unique_ptr<AZ::IO::LocalFileIO> m_directFileIO; ///> The Direct file IO instance is a LocalFileIO.
|
||||
AZStd::unique_ptr<AZ::IO::FileIOBase> m_archiveFileIO; ///> The Default file IO instance is a ArchiveFileIO.
|
||||
AZStd::unique_ptr<AZ::IO::Archive> m_archive; ///> The AZ::IO::Instance
|
||||
@@ -194,7 +189,6 @@ namespace AzFramework
|
||||
enum class RootPathType
|
||||
{
|
||||
AppRoot,
|
||||
AssetRoot,
|
||||
EngineRoot
|
||||
};
|
||||
void SetRootPath(RootPathType type, const char* source);
|
||||
|
||||
@@ -92,7 +92,8 @@ namespace AZ::IO::ArchiveInternal
|
||||
convertedPath->Native().replace(0, aliasToLookFor.size(), aliasToReplaceWith);
|
||||
}
|
||||
// lowercase path if it starts with either the @assets@ or @root@ alias
|
||||
if (convertedPath->Native().starts_with("@assets@") || convertedPath->Native().starts_with("@root@"))
|
||||
if (convertedPath->Native().starts_with("@assets@") || convertedPath->Native().starts_with("@root@")
|
||||
|| convertedPath->Native().starts_with("@projectplatformcache@"))
|
||||
{
|
||||
AZStd::to_lower(convertedPath->Native().begin(), convertedPath->Native().end());
|
||||
}
|
||||
|
||||
@@ -604,4 +604,14 @@ namespace AZ::IO
|
||||
}
|
||||
return realUnderlyingFileIO->ResolvePath(resolvedPath, path);
|
||||
}
|
||||
|
||||
bool ArchiveFileIO::ReplaceAlias(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path) const
|
||||
{
|
||||
FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance();
|
||||
if (!realUnderlyingFileIO)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return realUnderlyingFileIO->ReplaceAlias(replacedAliasPath, path);
|
||||
}
|
||||
}//namespace AZ:IO
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace AZ::IO
|
||||
bool ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const override;
|
||||
bool ResolvePath(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path) const override;
|
||||
using FileIOBase::ResolvePath;
|
||||
bool ReplaceAlias(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path) const override;
|
||||
bool GetFilename(IO::HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override;
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Serialization/ObjectStream.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
@@ -525,7 +526,10 @@ namespace AzFramework
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_registryMutex);
|
||||
|
||||
// Get asset root from application.
|
||||
EBUS_EVENT_RESULT(m_assetRoot, AzFramework::ApplicationRequests::Bus, GetAssetRoot);
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(m_assetRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
|
||||
}
|
||||
|
||||
// Reflect registry for serialization.
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace AzFramework
|
||||
|
||||
//! Name of the game project that is used for negotiating a connection with the AssetProcessor
|
||||
//! The AssetProcessor needs to be processing Assets for the specified game project
|
||||
//! (Can be queried from Settings Registry - "sys_game_folder")
|
||||
//! (Can be queried from Settings Registry - "project_name" under the ProjectSettingsRootKey)
|
||||
AZStd::fixed_string<64> m_projectName;
|
||||
//! The IP address to use either connect to or from the AssetProcessor
|
||||
//! (Can be queried from Settings Registry - "remote_ip")
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace AzFramework
|
||||
namespace AssetSystem
|
||||
{
|
||||
constexpr char BranchToken[] = "assetProcessor_branch_token";
|
||||
constexpr char ProjectName[] = "sys_game_folder";
|
||||
constexpr char Assets[] = "assets";
|
||||
constexpr char AssetProcessorRemoteIp[] = "remote_ip";
|
||||
constexpr char AssetProcessorRemotePort[] = "remote_port";
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace AzFramework::AssetSystem::Platform
|
||||
|
||||
// Declare platform specific LaunchAssetProcessor function
|
||||
bool LaunchAssetProcessor(AZStd::string_view executableDirectory, AZStd::string_view appRoot,
|
||||
AZStd::string_view projectName);
|
||||
AZStd::string_view projectPath);
|
||||
}
|
||||
|
||||
namespace AzFramework
|
||||
@@ -67,23 +67,21 @@ namespace AzFramework
|
||||
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
|
||||
// Add the engine path to the launch command if available from the Settings Registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString engineRootFolder;
|
||||
// Add the app-root to the launch command if available from the Settings Registry
|
||||
if (settingsRegistry)
|
||||
{
|
||||
settingsRegistry->Get(engineRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
|
||||
// Add the active game project to the launch from the Settings Registry
|
||||
const auto gameProjectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/sys_game_folder",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
|
||||
AZ::SettingsRegistryInterface::FixedValueString gameProjectName;
|
||||
// Add the active project's path to the launch command if available from the Settings Registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectPath;
|
||||
if (settingsRegistry)
|
||||
{
|
||||
settingsRegistry->Get(gameProjectName, gameProjectKey);
|
||||
settingsRegistry->Get(projectPath, AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
|
||||
}
|
||||
|
||||
if (!Platform::LaunchAssetProcessor(executableDirectory, engineRootFolder, gameProjectName))
|
||||
if (!Platform::LaunchAssetProcessor(executableDirectory, engineRootFolder, projectPath))
|
||||
{
|
||||
// if we are unable to launch asset processor
|
||||
AzFramework::AssetSystemInfoBus::Broadcast(&AzFramework::AssetSystem::AssetSystemInfoNotifications::OnError, AssetSystemErrors::ASSETSYSTEM_FAILED_TO_LAUNCH_ASSETPROCESSOR);
|
||||
@@ -179,24 +177,22 @@ namespace AzFramework
|
||||
|
||||
{
|
||||
// Read Branch Token from Settings Registry
|
||||
AZ::s64 branchToken64;
|
||||
if (!settingsRegistry->Get(branchToken64, AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZStd::string branchToken;
|
||||
if (!settingsRegistry->Get(branchToken, AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::BranchToken)))
|
||||
{
|
||||
// The first time the AssetProcessor runs within a branch the bootstrap.cfg does not have a branch token set
|
||||
// Therefore it is not an error for the branch token to not be in the bootstrap.cfg file
|
||||
AZStd::string branchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, branchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, branchToken);
|
||||
AZ_TracePrintfOnce("AssetSystemComponent", "Failed to read branch token from bootstrap. Calculating Branch Token: %s\n", branchToken.c_str());
|
||||
outputConnectionSettings.m_branchToken = branchToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputConnectionSettings.m_branchToken = AZStd::fixed_string<32>::format("0x%08X", aznumeric_cast<AZ::u32>(branchToken64));
|
||||
outputConnectionSettings.m_branchToken = AZStd::string_view{ branchToken };
|
||||
if (outputConnectionSettings.m_branchToken.empty())
|
||||
{
|
||||
AZStd::string branchToken;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForAppRoot, branchToken);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, branchToken);
|
||||
AZ_TracePrintfOnce("AssetSystemComponent", "Branch token read from bootstrap is empty. Calculating Branch Token: %s\n", branchToken.c_str());
|
||||
outputConnectionSettings.m_branchToken = branchToken;
|
||||
}
|
||||
@@ -205,19 +201,14 @@ namespace AzFramework
|
||||
|
||||
{
|
||||
// Read Project Name from Settings Registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName;
|
||||
if (!settingsRegistry->Get(projectName, AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, AzFramework::AssetSystem::ProjectName)))
|
||||
AZ::SettingsRegistryInterface::FixedValueString projectName = AZ::Utils::GetProjectName();
|
||||
if (projectName.empty())
|
||||
{
|
||||
AZ_Error("AssetSystemComponent", false, "Failed to read project name from bootstrap");
|
||||
AZ_Error("AssetSystemComponent", false, "Failed to read project name from registry");
|
||||
result = false;
|
||||
}
|
||||
|
||||
outputConnectionSettings.m_projectName = projectName;
|
||||
if (outputConnectionSettings.m_projectName.empty())
|
||||
{
|
||||
AZ_Error("AssetSystemComponent", false, "Project name read from bootstrap is empty");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the direction in which a connection to the Asset Processor should be from using the Settings Registry
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user