Merge branch 'main' into LoadPipelineFromGitHub

This commit is contained in:
Brian Herrera
2021-03-26 17:05:57 -07:00
765 changed files with 11513 additions and 20246 deletions
@@ -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)
+1 -1
View File
@@ -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)
+2 -12
View File
@@ -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;
+2 -1
View File
@@ -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;
+1 -14
View File
@@ -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;
+1 -14
View File
@@ -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;
+1 -9
View File
@@ -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
}
//////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -1064,7 +1064,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////
// Interface methods
virtual bool Initialize(const SSystemInitParams* initParams) = 0;
virtual bool Initialize() = 0;
virtual void Release() = 0;
};
+1 -6
View File
@@ -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;
+7 -189
View File
@@ -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.
+7 -8
View File
@@ -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());
}
}
};
+1 -1
View File
@@ -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
-630
View File
@@ -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("&", "&amp;");
label.replace("<", "&lt;");
label.replace(">", "&gt;");
label.replace("\"", "&quot;");
label.replace("'", "&apos;");
if (m_args.size() > 0)
{
m_args.replace("&", "&amp;");
m_args.replace("<", "&lt;");
m_args.replace(">", "&gt;");
m_args.replace("\"", "&quot;");
m_args.replace("'", "&apos;");
m_args.replace("%", "&#37;");
}
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
-67
View File
@@ -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
+1 -12
View File
@@ -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]);
}
}
}
+7 -11
View File
@@ -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");
+12 -28
View File
@@ -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;
+3 -33
View File
@@ -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;
+3 -9
View File
@@ -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__);
}
+44 -297
View File
@@ -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);
-15
View File
@@ -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;
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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);
}
}
+14 -25
View File
@@ -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;
+4 -4
View File
@@ -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, &currentPosIndex](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(&registry) / 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(&registry);
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(&registry);
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 -5
View File
@@ -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
@@ -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;
}
@@ -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