(engineRoot.size()));
}
//////////////////////////////////////////////////////////////////////////
diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp
index c8f2e268d9..37c46f07dd 100644
--- a/Code/Editor/Viewport.cpp
+++ b/Code/Editor/Viewport.cpp
@@ -1407,6 +1407,69 @@ void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
}
//////////////////////////////////////////////////////////////////////////
#if defined(AZ_PLATFORM_WINDOWS)
+// Note: Both CreateAnglesYPR and CreateOrientationYPR were copied verbatim from Cry_Camera.h which has been removed.
+//
+// Description
+//
+// x-YAW
+// y-PITCH (negative=looking down / positive=looking up)
+// z-ROLL
+//
+// Note: If we are looking along the z-axis, its not possible to specify the x and z-angle
+inline Ang3 CreateAnglesYPR(const Matrix33& m)
+{
+ assert(m.IsOrthonormal());
+ float l = Vec3(m.m01, m.m11, 0.0f).GetLength();
+ if (l > 0.0001)
+ {
+ return Ang3(atan2f(-m.m01 / l, m.m11 / l), atan2f(m.m21, l), atan2f(-m.m20 / l, m.m22 / l));
+ }
+ else
+ {
+ return Ang3(0, atan2f(m.m21, l), 0);
+ }
+}
+
+// Description
+// This function builds a 3x3 orientation matrix using YPR-angles
+// Rotation order for the orientation-matrix is Z-X-Y. (Zaxis=YAW / Xaxis=PITCH / Yaxis=ROLL)
+//
+//
+// COORDINATE-SYSTEM
+//
+// z-axis
+// ^
+// |
+// | y-axis
+// | /
+// | /
+// |/
+// +---------------> x-axis
+//
+//
+// Example:
+// Matrix33 orientation=CreateOrientationYPR( Ang3(1,2,3) );
+inline Matrix33 CreateOrientationYPR(const Ang3& ypr)
+{
+ f32 sz, cz;
+ sincos_tpl(ypr.x, &sz, &cz); //Zaxis = YAW
+ f32 sx, cx;
+ sincos_tpl(ypr.y, &sx, &cx); //Xaxis = PITCH
+ f32 sy, cy;
+ sincos_tpl(ypr.z, &sy, &cy); //Yaxis = ROLL
+ Matrix33 c;
+ c.m00 = cy * cz - sy * sz * sx;
+ c.m01 = -sz * cx;
+ c.m02 = sy * cz + cy * sz * sx;
+ c.m10 = cy * sz + sy * sx * cz;
+ c.m11 = cz * cx;
+ c.m12 = sy * sz - cy * sx * cz;
+ c.m20 = -sy * cx;
+ c.m21 = sx;
+ c.m22 = cy * cx;
+ return c;
+}
+
void QtViewport::OnRawInput([[maybe_unused]] UINT wParam, HRAWINPUT lParam)
{
static C3DConnexionDriver* p3DConnexionDriver = 0;
@@ -1450,12 +1513,12 @@ void QtViewport::OnRawInput([[maybe_unused]] UINT wParam, HRAWINPUT lParam)
t *= sys_scale3DMouseTranslation->GetFVal();
float as = 0.001f * gSettings.cameraMoveSpeed;
- Ang3 ypr = CCamera::CreateAnglesYPR(Matrix33(viewTM));
+ Ang3 ypr = CreateAnglesYPR(Matrix33(viewTM));
ypr.x += -all6DOFs[5] * as * fScaleYPR;
ypr.y = AZStd::clamp(ypr.y + all6DOFs[3] * as * fScaleYPR, -1.5f, 1.5f); // to keep rotation in reasonable range
ypr.z = 0; // to have camera always upward
- viewTM = Matrix34(CCamera::CreateOrientationYPR(ypr), viewTM.GetTranslation());
+ viewTM = Matrix34(CreateOrientationYPR(ypr), viewTM.GetTranslation());
viewTM = viewTM * Matrix34::CreateTranslationMat(t);
SetViewTM(viewTM);
diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp
index 75d16e9a40..49459fad0f 100644
--- a/Code/Editor/ViewportTitleDlg.cpp
+++ b/Code/Editor/ViewportTitleDlg.cpp
@@ -138,14 +138,11 @@ CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
connect(this, &CViewportTitleDlg::ActionTriggered, MainWindow::instance()->GetActionManager(), &ActionManager::ActionTriggered);
- AZ::VR::VREventBus::Handler::BusConnect();
-
OnInitDialog();
}
CViewportTitleDlg::~CViewportTitleDlg()
{
- AZ::VR::VREventBus::Handler::BusDisconnect();
GetISystem()->GetISystemEventDispatcher()->RemoveListener(this);
GetIEditor()->UnregisterNotifyListener(this);
@@ -236,10 +233,6 @@ void CViewportTitleDlg::SetupOverflowMenu()
connect(m_audioMuteAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedMuteAudio);
overFlowMenu->addAction(m_audioMuteAction);
- m_enableVRAction = new QAction("Enable VR Preview", overFlowMenu);
- connect(m_enableVRAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedEnableVR);
- overFlowMenu->addAction(m_enableVRAction);
-
overFlowMenu->addSeparator();
m_enableGridSnappingAction = new QAction("Enable Grid Snapping", overFlowMenu);
@@ -305,16 +298,6 @@ void CViewportTitleDlg::OnInitDialog()
connect(displayInfoHelper, &CViewportTitleDlgDisplayInfoHelper::ViewportInfoStatusUpdated, this, &CViewportTitleDlg::UpdateDisplayInfo);
UpdateDisplayInfo();
- // This is here just in case this class hasn't been created before
- // a VR headset was initialized
- m_enableVRAction->setEnabled(false);
- if (AZ::VR::HMDDeviceRequestBus::GetTotalNumOfEventHandlers() != 0)
- {
- m_enableVRAction->setEnabled(true);
- }
-
- AZ::VR::VREventBus::Handler::BusConnect();
-
QFontMetrics metrics({});
int width = static_cast(metrics.boundingRect("-9999.99").width() * m_fieldWidthMultiplier);
@@ -931,23 +914,6 @@ void CViewportTitleDlg::UpdateMuteActionText()
}
}
-void CViewportTitleDlg::OnHMDInitialized()
-{
- m_enableVRAction->setEnabled(true);
-}
-
-void CViewportTitleDlg::OnHMDShutdown()
-{
- m_enableVRAction->setEnabled(false);
-}
-
-void CViewportTitleDlg::OnBnClickedEnableVR()
-{
- gSettings.bEnableGameModeVR = !gSettings.bEnableGameModeVR;
-
- m_enableVRAction->setText(gSettings.bEnableGameModeVR ? tr("Disable VR Preview") : tr("Enable VR Preview"));
-}
-
inline double Round(double fVal, double fStep)
{
if (fStep > 0.f)
diff --git a/Code/Editor/ViewportTitleDlg.h b/Code/Editor/ViewportTitleDlg.h
index 6996fe7750..a3e19837b4 100644
--- a/Code/Editor/ViewportTitleDlg.h
+++ b/Code/Editor/ViewportTitleDlg.h
@@ -22,7 +22,6 @@
#include
#include
-#include
#endif
// CViewportTitleDlg dialog
@@ -44,7 +43,6 @@ class CViewportTitleDlg
: public QWidget
, public IEditorNotifyListener
, public ISystemEventListener
- , public AZ::VR::VREventBus::Handler
{
Q_OBJECT
public:
@@ -85,13 +83,6 @@ protected:
void OnToggleHelpers();
void UpdateDisplayInfo();
- //////////////////////////////////////////////////////////////////////////
- /// VR Event Bus Implementation
- //////////////////////////////////////////////////////////////////////////
- void OnHMDInitialized() override;
- void OnHMDShutdown() override;
- //////////////////////////////////////////////////////////////////////////
-
void SetupCameraDropdownMenu();
void SetupResolutionDropdownMenu();
void SetupViewportInformationMenu();
@@ -140,7 +131,6 @@ protected:
void OnBnClickedGotoPosition();
void OnBnClickedMuteAudio();
- void OnBnClickedEnableVR();
void UpdateMuteActionText();
@@ -168,7 +158,6 @@ protected:
QAction* m_fullInformationAction = nullptr;
QAction* m_compactInformationAction = nullptr;
QAction* m_audioMuteAction = nullptr;
- QAction* m_enableVRAction = nullptr;
QAction* m_enableGridSnappingAction = nullptr;
QAction* m_enableAngleSnappingAction = nullptr;
QComboBox* m_cameraSpeed = nullptr;
diff --git a/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp b/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp
index 17f576b5ee..89dfcaffd1 100644
--- a/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp
+++ b/Code/Editor/WelcomeScreen/WelcomeScreenDialog.cpp
@@ -25,8 +25,6 @@
#include
-// AzFramework
-#include
// AzToolsFramework
#include
@@ -173,9 +171,6 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
m_pRecentList = pList;
- const char* engineRoot;
- EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
-
auto projectPath = AZ::Utils::GetProjectPath();
QString gamePath{projectPath.c_str()};
Path::ConvertSlashToBackSlash(gamePath);
diff --git a/Code/Editor/editor_lib_test_files.cmake b/Code/Editor/editor_lib_test_files.cmake
index 2ae3d22c19..17f36228db 100644
--- a/Code/Editor/editor_lib_test_files.cmake
+++ b/Code/Editor/editor_lib_test_files.cmake
@@ -22,6 +22,7 @@ set(FILES
Lib/Tests/test_DisplaySettingsPythonBindings.cpp
Lib/Tests/test_ViewportManipulatorController.cpp
Lib/Tests/test_ModularViewportCameraController.cpp
+ Lib/Tests/Camera/test_EditorCamera.cpp
DisplaySettingsPythonFuncs.cpp
DisplaySettingsPythonFuncs.h
)
diff --git a/Code/Framework/AtomCore/Tests/Main.cpp b/Code/Framework/AtomCore/Tests/Main.cpp
index 29ef408551..eb4c6bc835 100644
--- a/Code/Framework/AtomCore/Tests/Main.cpp
+++ b/Code/Framework/AtomCore/Tests/Main.cpp
@@ -7,7 +7,6 @@
*/
-#include
#include
#include
#include
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h
index 0c8e5209ca..c45bb21c6d 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h
@@ -556,16 +556,24 @@ namespace AZ
Asset assetData(AssetInternal::GetAssetData(actualId, AZ::Data::AssetLoadBehavior::Default));
if (assetData)
{
- auto curStatus = assetData->GetStatus();
+ auto isReady = assetData->GetStatus() == AssetData::AssetStatus::Ready;
bool isError = assetData->IsError();
- connectLock.unlock();
- if (curStatus == AssetData::AssetStatus::Ready)
+
+ if (isReady || isError)
{
- handler->OnAssetReady(assetData);
- }
- else if (isError)
- {
- handler->OnAssetError(assetData);
+ connectLock.unlock();
+
+ if (isReady)
+ {
+ handler->OnAssetReady(assetData);
+ }
+ else if (isError)
+ {
+ handler->OnAssetError(assetData);
+ }
+
+ // Lock the mutex again since some destructors will be modifying the context afterwards
+ connectLock.lock();
}
}
}
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h
index 62f5808207..d2b069b55b 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h
@@ -70,6 +70,9 @@ namespace AZ::Data
const char* GetFilename() const override { return m_filePath.c_str(); }
+ AZStd::chrono::milliseconds GetStreamingDeadline() const { return m_curDeadline; }
+ AZ::IO::IStreamerTypes::Priority GetStreamingPriority() const { return m_curPriority; }
+
// AssetDataStream specific APIs
//! Whether or not all data has been loaded.
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
index 06bb0b0cac..b03e1affdc 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
@@ -1677,9 +1677,13 @@ namespace AZ
// they will trigger a ReleaseAsset call sometime after the AssetManager has begun to shut down, which can lead to
// race conditions.
+ // Make sure the streamer request is removed first before the asset is released
+ // If the asset is released first it could lead to a race condition where another thread starts loading the asset
+ // again and attempts to add a new streamer request with the same ID before the old one has been removed, causing
+ // that load request to fail
+ RemoveActiveStreamerRequest(assetId);
weakAsset = {};
loadingAsset.Reset();
- RemoveActiveStreamerRequest(assetId);
};
auto&& [deadline, priority] = GetEffectiveDeadlineAndPriority(*handler, asset.GetType(), loadParams);
diff --git a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
index 67123ed826..e7e87559b0 100644
--- a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
+++ b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
@@ -19,7 +19,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -40,7 +39,6 @@ namespace AZ
SliceComponent::CreateDescriptor(),
SliceSystemComponent::CreateDescriptor(),
SliceMetadataInfoComponent::CreateDescriptor(),
- TimeSystemComponent::CreateDescriptor(),
LoggerSystemComponent::CreateDescriptor(),
EventSchedulerSystemComponent::CreateDescriptor(),
TaskGraphSystemComponent::CreateDescriptor(),
@@ -59,7 +57,6 @@ namespace AZ
{
return AZ::ComponentTypeList
{
- azrtti_typeid(),
azrtti_typeid(),
azrtti_typeid(),
azrtti_typeid(),
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
index df8db79db0..cf23d2b4d7 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
@@ -72,6 +72,7 @@
#include
#include
+#include
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
{
@@ -214,7 +215,6 @@ namespace AZ
// Update old Project path before attempting to merge in new Settings Registry values in order to prevent recursive calls
m_oldProjectPath = newProjectPath;
- // Merge the project.json file into settings registry under ProjectSettingsRootKey path.
// Update all the runtime file paths based on the new "project_path" value.
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
@@ -417,6 +417,7 @@ namespace AZ
ComponentApplication::ComponentApplication(int argC, char** argV)
: m_eventLogger{}
+ , m_timeSystem(AZStd::make_unique())
{
if (Interface::Get() == nullptr)
{
@@ -486,16 +487,6 @@ namespace AZ
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.
- CalculateAppRoot();
-
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
@@ -585,7 +576,6 @@ namespace AZ
DestroyAllocator();
}
-
void ReportBadEngineRoot()
{
AZStd::string errorMessage = {"Unable to determine a valid path to the engine.\n"
@@ -615,7 +605,8 @@ namespace AZ
{
AZ_Assert(!m_isStarted, "Component application already started!");
- if (m_engineRoot.empty())
+ using Type = AZ::SettingsRegistryInterface::Type;
+ if (m_settingsRegistry->GetType(SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder) == Type::NoType)
{
ReportBadEngineRoot();
return nullptr;
@@ -687,7 +678,6 @@ namespace AZ
ComponentApplicationBus::Handler::BusConnect();
- m_currentTime = AZStd::chrono::system_clock::now();
TickRequestBus::Handler::BusConnect();
#if defined(AZ_ENABLE_DEBUG_TOOLS)
@@ -1181,6 +1171,24 @@ namespace AZ
return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext() : nullptr;
}
+ /// Returns the path to the engine.
+
+ const char* ComponentApplication::GetEngineRoot() const
+ {
+ static IO::FixedMaxPathString engineRoot;
+ engineRoot.clear();
+ m_settingsRegistry->Get(engineRoot, SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ return engineRoot.c_str();
+ }
+
+ const char* ComponentApplication::GetExecutableFolder() const
+ {
+ static IO::FixedMaxPathString exeFolder;
+ exeFolder.clear();
+ m_settingsRegistry->Get(exeFolder, SettingsRegistryMergeUtils::FilePathKey_BinaryFolder);
+ return exeFolder.c_str();
+ }
+
//=========================================================================
// CreateReflectionManager
//=========================================================================
@@ -1405,31 +1413,23 @@ namespace AZ
#endif
}
- void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
+ void ComponentApplication::Tick()
{
+ AZ_PROFILE_SCOPE(System, "Component application simulation tick");
+
{
- AZ_PROFILE_SCOPE(System, "Component application simulation tick");
-
- AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
-
- m_deltaTime = 0.0f;
-
- if (now >= m_currentTime)
- {
- AZStd::chrono::duration delta = now - m_currentTime;
- m_deltaTime = deltaOverride >= 0.f ? deltaOverride : delta.count();
- }
-
- {
- AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
- TickBus::ExecuteQueuedEvents();
- }
- m_currentTime = now;
- {
- AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
- EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
- }
+ AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
+ TickBus::ExecuteQueuedEvents();
}
+
+ {
+ AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
+ const AZ::TimeUs deltaTimeUs = m_timeSystem->AdvanceTickDeltaTimes();
+ const float deltaTimeSeconds = AZ::TimeUsToSeconds(deltaTimeUs);
+ AZ::TickBus::Broadcast(&TickEvents::OnTick, deltaTimeSeconds, GetTimeAtCurrentTick());
+ }
+
+ m_timeSystem->ApplyTickRateLimiterIfNeeded();
}
void ComponentApplication::TickSystem()
@@ -1486,27 +1486,6 @@ namespace AZ
}
}
- //=========================================================================
- // CalculateExecutablePath
- //=========================================================================
- void ComponentApplication::CalculateExecutablePath()
- {
- m_exeDirectory = Utils::GetExecutableDirectory();
- }
-
- void ComponentApplication::CalculateAppRoot()
- {
- if (AZStd::optional appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
- {
- m_appRoot = AZStd::move(*appRootPath);
- }
- }
-
- void ComponentApplication::CalculateEngineRoot()
- {
- m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native();
- }
-
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)
{
// No special parsing of the Module Path is done by the Component Application anymore
@@ -1532,13 +1511,10 @@ namespace AZ
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Invalid;
}
- //=========================================================================
- // GetFrameTime
- // [1/22/2016]
- //=========================================================================
float ComponentApplication::GetTickDeltaTime()
{
- return m_deltaTime;
+ const AZ::TimeUs gameTickTime = m_timeSystem->GetSimulationTickDeltaTimeUs();
+ return AZ::TimeUsToSeconds(gameTickTime);
}
//=========================================================================
@@ -1547,7 +1523,8 @@ namespace AZ
//=========================================================================
ScriptTimePoint ComponentApplication::GetTimeAtCurrentTick()
{
- return ScriptTimePoint(m_currentTime);
+ const AZ::TimeUs lastGameTickTime = m_timeSystem->GetLastSimulationTickTime();
+ return ScriptTimePoint(AZ::TimeUsToChrono(lastGameTickTime));
}
//=========================================================================
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
index 6df93aff4e..f245b5f5da 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
@@ -30,12 +30,14 @@
#include
#include
+
namespace AZ
{
class BehaviorContext;
class IConsole;
class Module;
class ModuleManager;
+ class TimeSystem;
}
namespace AZ::Debug
{
@@ -221,13 +223,10 @@ namespace AZ
BehaviorContext* GetBehaviorContext() override;
/// Returns the json registration context that has been registered with the app, if there is one.
JsonRegistrationContext* GetJsonRegistrationContext() override;
- /// 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(); }
+ const char* GetEngineRoot() const override;
/// Returns the path to the folder the executable is in.
- const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
+ const char* GetExecutableFolder() const override;
//////////////////////////////////////////////////////////////////////////
/// TickRequestBus
@@ -240,7 +239,7 @@ namespace AZ
/**
* Ticks all components using the \ref AZ::TickBus during simulation time. May not tick if the application is not active (i.e. not in focus)
*/
- virtual void Tick(float deltaOverride = -1.f);
+ virtual void Tick();
/**
* Ticks all using the \ref AZ::SystemTickBus at all times. Should always tick even if the application is not active.
@@ -352,15 +351,6 @@ namespace AZ
/// Adds system components requested by modules and the application to the system entity.
void AddRequiredSystemComponents(AZ::Entity* systemEntity);
- /// Calculates the directory the application executable comes from.
- void CalculateExecutablePath();
-
- /// Calculates the root directory of the engine.
- void CalculateEngineRoot();
-
- /// Deprecated: The term "AppRoot" has no meaning
- void CalculateAppRoot();
-
template
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
{
@@ -371,8 +361,6 @@ namespace AZ
}
}
- AZStd::chrono::system_clock::time_point m_currentTime{ AZStd::chrono::system_clock::time_point::max() };
- float m_deltaTime{ 0.0f };
AZStd::unique_ptr m_moduleManager;
AZStd::unique_ptr m_settingsRegistry;
EntityAddedEvent m_entityAddedEvent;
@@ -388,14 +376,13 @@ 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::IO::FixedMaxPath m_exeDirectory;
- AZ::IO::FixedMaxPath m_engineRoot;
- AZ::IO::FixedMaxPath m_appRoot;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler;
AZ::SettingsRegistryInterface::NotifyEventHandler m_commandLineUpdatedHandler;
+ AZStd::unique_ptr m_timeSystem;
+
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
// from the m_console member when it goes out of scope
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h
index 0c0977384a..feefa95973 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationBus.h
@@ -175,10 +175,6 @@ namespace AZ
//! the serializers used by the best-effort json serialization.
virtual class JsonRegistrationContext* GetJsonRegistrationContext() = 0;
- //! Gets the name of the working root folder that was registered with the app.
- //! @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;
diff --git a/Code/Framework/AzCore/AzCore/EBus/EventSchedulerSystemComponent.cpp b/Code/Framework/AzCore/AzCore/EBus/EventSchedulerSystemComponent.cpp
index 3cdc3fc461..dd92b340c5 100644
--- a/Code/Framework/AzCore/AzCore/EBus/EventSchedulerSystemComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/EBus/EventSchedulerSystemComponent.cpp
@@ -60,7 +60,7 @@ namespace AZ
void EventSchedulerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
- TimeMs startTime = GetElapsedTimeMs();
+ TimeMs startTime = AZ::GetElapsedTimeMs();
bool usingTimeslice = bg_maxScheduledEventProcessTimeMs != TimeMs{ 0 };
while (!m_queue.empty())
@@ -76,7 +76,7 @@ namespace AZ
while (!m_pendingQueue.empty())
{
- if (usingTimeslice && (GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
+ if (usingTimeslice && (AZ::GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
{
AZLOG_WARN("Failed to trigger all pending scheduled events, %u events remain on the pending queue", aznumeric_cast(m_pendingQueue.size()));
break;
@@ -103,7 +103,7 @@ namespace AZ
durationMs = TimeMs{ 0 };
}
- TimeMs currentMilliseconds = GetElapsedTimeMs();
+ TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
if (timedEvent->m_handle == nullptr)
{
timedEvent->m_handle = AllocateHandle();
@@ -122,7 +122,7 @@ namespace AZ
durationMs = TimeMs{ 0 };
}
- TimeMs currentMilliseconds = GetElapsedTimeMs();
+ TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
ScheduledEvent* timedEvent = AllocateManagedEvent(callback, eventName);
const bool ownsScheduledEvent = true;
*(timedEvent->m_handle) = ScheduledEventHandle(TimeMs(currentMilliseconds + durationMs), durationMs, timedEvent, ownsScheduledEvent);
diff --git a/Code/Framework/AzCore/AzCore/EBus/ScheduledEvent.cpp b/Code/Framework/AzCore/AzCore/EBus/ScheduledEvent.cpp
index 8e496c3dbc..41230958e4 100644
--- a/Code/Framework/AzCore/AzCore/EBus/ScheduledEvent.cpp
+++ b/Code/Framework/AzCore/AzCore/EBus/ScheduledEvent.cpp
@@ -76,7 +76,7 @@ namespace AZ
TimeMs ScheduledEvent::TimeInQueueMs() const
{
- return GetElapsedTimeMs() - m_timeInserted;
+ return AZ::GetElapsedTimeMs() - m_timeInserted;
}
TimeMs ScheduledEvent::RemainingTimeInQueueMs() const
diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp
index 6427571f10..797f96e53f 100644
--- a/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp
+++ b/Code/Framework/AzCore/AzCore/IO/Streamer/FullFileDecompressor.cpp
@@ -62,6 +62,7 @@ namespace AZ
, m_alignment(alignment)
{
JobManagerDesc jobDesc;
+ jobDesc.m_jobManagerName = "Full File Decompressor";
u32 numThreads = AZ::GetMin(maxNumJobs, AZStd::thread::hardware_concurrency());
for (u32 i = 0; i < numThreads; ++i)
{
diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
index 230bf959f6..ce8455d9a1 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
+++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
@@ -83,7 +84,7 @@ AZ_THREAD_LOCAL JobManagerWorkStealing::ThreadInfo* JobManagerWorkStealing::m_cu
JobManagerWorkStealing::JobManagerWorkStealing(const JobManagerDesc& desc)
: m_isAsynchronous(!desc.m_workerThreads.empty())
- , m_workerThreads(AZStd::move(CreateWorkerThreads(desc.m_workerThreads)))
+ , m_workerThreads(AZStd::move(CreateWorkerThreads(desc)))
{
//allow workers to begin processing after they have all been created, needed to wait since they may access each others queues
m_initSemaphore.release(static_cast(desc.m_workerThreads.size()));
@@ -457,8 +458,6 @@ void JobManagerWorkStealing::ProcessJobsInternal(ThreadInfo* info, Job* suspende
else
{
//attempt to steal a job from another thread's queue
- AZ_PROFILE_SCOPE(AzCore, "JobManagerWorkStealing::ProcessJobsInternal:WorkStealing");
-
unsigned int numStealAttempts = 0;
const unsigned int maxStealAttempts = (unsigned int)m_workerThreads.size() * 3; //try every thread a few times before giving up
while (!job)
@@ -620,8 +619,9 @@ JobManagerWorkStealing::ThreadInfo* JobManagerWorkStealing::FindCurrentThreadInf
return info;
}
-JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(const JobManagerDesc::DescList& workerDescList)
+JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(const JobManagerDesc& jmDesc)
{
+ const JobManagerDesc::DescList& workerDescList = jmDesc.m_workerThreads;
ThreadList workerThreads(workerDescList.size());
m_threads.reserve(workerDescList.size());
@@ -634,8 +634,12 @@ JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(c
info->m_owningManager = this;
info->m_workerId = iThread;
+ AZStd::fixed_string<128> threadName = AZStd::fixed_string<128>::format(
+ "%s worker thread %d",
+ jmDesc.m_jobManagerName[0] != '\0' ? jmDesc.m_jobManagerName : "AZ JobManager",
+ iThread);
AZStd::thread_desc threadDesc;
- threadDesc.m_name = "AZ JobManager worker thread";
+ threadDesc.m_name = threadName.c_str();
threadDesc.m_cpuId = desc.m_cpuId;
threadDesc.m_priority = desc.m_priority;
if (desc.m_stackSize != 0)
diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.h
index 55de872d86..734c166d6a 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.h
+++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.h
@@ -115,7 +115,7 @@ namespace AZ
void ProcessJobsAssist(ThreadInfo* info, Job* suspendedJob, AZStd::atomic* notifyFlag);
void ProcessJobsSynchronous(ThreadInfo* info, Job* suspendedJob, AZStd::atomic* notifyFlag);
void ProcessJobsInternal(ThreadInfo* info, Job* suspendedJob, AZStd::atomic* notifyFlag);
- ThreadList CreateWorkerThreads(const JobManagerDesc::DescList& workerDescList);
+ ThreadList CreateWorkerThreads(const JobManagerDesc& jmDesc);
#ifndef AZ_MONOLITHIC_BUILD
ThreadInfo* CrossModuleFindAndSetWorkerThreadInfo() const;
#endif
diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
index 6f5ccc93e4..883fa15991 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerComponent.cpp
@@ -51,6 +51,7 @@ namespace AZ
JobManagerBus::Handler::BusConnect();
JobManagerDesc desc;
+ desc.m_jobManagerName = "Default JobManager";
JobManagerThreadDesc threadDesc;
int numberOfWorkerThreads = m_numberOfWorkerThreads;
diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
index 94f84f27b3..b2156291bc 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
+++ b/Code/Framework/AzCore/AzCore/Jobs/JobManagerDesc.h
@@ -51,6 +51,8 @@ namespace AZ
{
JobManagerDesc() {}
+ const char* m_jobManagerName = "";
+
using DescList = AZStd::fixed_vector;
DescList m_workerThreads; ///< List of worker threads to create
};
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector3.h b/Code/Framework/AzCore/AzCore/Math/Vector3.h
index 821dc8292c..6b7c53266d 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector3.h
+++ b/Code/Framework/AzCore/AzCore/Math/Vector3.h
@@ -100,7 +100,7 @@ namespace AZ
void Set(float x, float y, float z);
//! Sets components from an array of 3 floats in xyz order.
- void Set(float values[]);
+ void Set(const float values[]);
//! Indexed access using operator(), just for convenience.
float operator()(int32_t index) const;
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector3.inl b/Code/Framework/AzCore/AzCore/Math/Vector3.inl
index 879ade38cf..6371c688b8 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector3.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Vector3.inl
@@ -186,7 +186,7 @@ namespace AZ
}
- AZ_MATH_INLINE void Vector3::Set(float values[])
+ AZ_MATH_INLINE void Vector3::Set(const float values[])
{
m_value = Simd::Vec3::LoadImmediate(values[0], values[1], values[2]);
}
diff --git a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp
index 3047a2894e..6cba54a17f 100644
--- a/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp
+++ b/Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp
@@ -50,7 +50,12 @@ namespace AZ
if (!s_instance)
{
- s_instance = Environment::FindVariable(NameDictionaryInstanceName);
+ // Because the NameDictionary allocates memory using the AZ::Allocator and it is created
+ // in the executable memory space, it's ownership cannot be transferred to other module memory spaces
+ // Otherwise this could cause the the NameDictionary to be destroyed in static de-init
+ // after the AZ::Allocators have been destroyed
+ // Therefore we supply the isTransferOwnership value of false using CreateVariableEx
+ s_instance = AZ::Environment::CreateVariableEx(NameDictionaryInstanceName, true, false);
}
return s_instance.IsConstructed();
diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp
index b03d413507..45f7876993 100644
--- a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp
+++ b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp
@@ -3408,7 +3408,14 @@ LUA_API const Node* lua_getDummyNode()
const BehaviorParameter* arg = method->GetArgument(iArg);
BehaviorClass* argClass = nullptr;
LuaLoadFromStack fromStack = FromLuaStack(context, arg, argClass);
- AZ_Assert(fromStack, "Argument %s for Method %s doesn't have support to be converted to Lua!", arg->m_name, method->m_name.c_str());
+ AZ_Assert(fromStack,
+ "The argument type: %s for method: %s is not serialized and/or reflected for scripting.\n"
+ "Make sure %s is added to the SerializeContext and reflected to the BehaviorContext\n"
+ "For example, verify these two exist and are being called in a Reflect function:\n"
+ "serializeContext->Class<%s>();\n"
+ "behaviorContext->Class<%s>();\n"
+ "%s will not be available for scripting unless these requirements are met."
+ , arg->m_name, method->m_name.c_str(), arg->m_name, arg->m_name, arg->m_name, method->m_name.c_str());
m_fromLua.push_back(AZStd::make_pair(fromStack, argClass));
}
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 5458a3fadf..975217a131 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -29,6 +29,8 @@
namespace AZ::Internal
{
+ static constexpr const char* ProductCacheDirectoryName = "Cache";
+
AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject(
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectJsonPath)
{
@@ -228,19 +230,20 @@ namespace AZ::Internal
namespace AZ::SettingsRegistryMergeUtils
{
- constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Settings/Internal/engine_root_scan_up_path" };
- constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Settings/Internal/project_root_scan_up_path" };
-
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry)
{
+ static constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Runtime/Internal/engine_root_scan_up_path" };
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
+
AZ::IO::FixedMaxPath engineRoot;
// 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);
+ constexpr auto engineRootKey = FixedValueString(BootstrapSettingsRootKey) + "/engine_path";
// Step 1 Run the scan upwards logic once to find the location of the engine.json if it exist
// Once this step is run the {InternalScanUpEngineRootKey} is set in the Settings Registry
// to have this scan logic only run once InternalScanUpEngineRootKey the supplied registry
- if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == SettingsRegistryInterface::Type::NoType)
+ if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == Type::NoType)
{
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
engineRoot = Internal::ScanUpRootLocator("engine.json");
@@ -263,7 +266,8 @@ namespace AZ::SettingsRegistryMergeUtils
// Step 3 locate the project root and attempt to find the engine root using the registered engine
// for the project in the project.json file
- AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry);
+ AZ::IO::FixedMaxPath projectRoot;
+ settingsRegistry.Get(projectRoot.Native(), FilePathKey_ProjectPath);
if (projectRoot.empty())
{
return {};
@@ -283,14 +287,18 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
{
- AZ::IO::FixedMaxPath projectRoot;
- const auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
+ static constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Runtime/Internal/project_root_scan_up_path" };
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
- // Step 1 Run the scan upwards logic once to find the location of the project.json if it exist
+ AZ::IO::FixedMaxPath projectRoot;
+ constexpr auto projectRootKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
+
+ // Step 1 Run the scan upwards logic once to find the location of the closest ancestor project.json
// Once this step is run the {InternalScanUpProjectRootKey} is set in the Settings Registry
// to have this scan logic only run once for the supplied registry
// SettingsRegistryInterface::GetType is used to check if a key is set
- if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == SettingsRegistryInterface::Type::NoType)
+ if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType)
{
projectRoot = Internal::ScanUpRootLocator("project.json");
// Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry
@@ -305,19 +313,129 @@ namespace AZ::SettingsRegistryMergeUtils
}
// Step 2 Check the project-path key
- // This is the project path root key, as in passed from command-line or .setreg files.
- if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
+ // This is the project path root key, as passed from command-line or *.setreg files.
+ settingsRegistry.Get(projectRoot.Native(), projectRootKey);
+ return projectRoot;
+ }
+
+ //! The algorithm that is used to find the project cache is as follows
+ //! 1. The "{BootstrapSettingsRootKey}/project_cache_path" is checked for the path
+ //! 2. Otherwise append the ProductCacheDirectoryName constant to the
+ static AZ::IO::FixedMaxPath FindProjectCachePath(SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path";
+
+ // Step 1 Check the project-cache-path key
+ if (AZ::IO::FixedMaxPath projectCachePath; settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey))
{
- return projectRoot;
+ return projectCachePath;
}
- // Step 3 Check for a "Cache" directory by scanning upwards from the executable directory
- if (auto candidateRoot = Internal::ScanUpRootLocator("Cache");
- !candidateRoot.empty() && AZ::IO::SystemFile::IsDirectory(candidateRoot.c_str()))
+ // Step 2 Append the "Cache" directory to the project-path
+ return projectPath / Internal::ProductCacheDirectoryName;
+ }
+
+ //! Set the user directory with the provided path or using /user as default
+ static AZ::IO::FixedMaxPath FindProjectUserPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ // User: root - same as the @user@ alias, this is the starting path for transient data and log files.
+ constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path";
+
+ // Step 1 Check the project-user-path key
+ if (AZ::IO::FixedMaxPath projectUserPath; settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey))
{
- projectRoot = AZStd::move(candidateRoot);
+ return projectUserPath;
+ }
+
+ // Step 2 Append the "User" directory to the project-path
+ return projectPath / "user";
+ }
+
+ //! Set the log directory using the settings registry path or using /log as default
+ static AZ::IO::FixedMaxPath FindProjectLogPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectUserPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ // User: root - same as the @log@ alias, this is the starting path for transient data and log files.
+ constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path";
+
+ // Step 1 Check the project-user-path key
+ if (AZ::IO::FixedMaxPath projectLogPath; settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey))
+ {
+ return projectLogPath;
+ }
+
+ // Step 2 Append the "Log" directory to the project-user-path
+ return projectUserPath / "log";
+ }
+
+ // check for a default write storage path, fall back to the if not
+ static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath)
+ {
+ AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
+ return devWriteStorage.has_value() ? *devWriteStorage : projectUserPath;
+ }
+
+ // check for the project build path, which is a relative path from the project root
+ // that specifies where the build directory is located
+ static void SetProjectBuildPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ if (AZ::IO::FixedMaxPath projectBuildPath; settingsRegistry.Get(projectBuildPath.Native(), ProjectBuildPath))
+ {
+ settingsRegistry.Remove(FilePathKey_ProjectBuildPath);
+ settingsRegistry.Remove(FilePathKey_ProjectConfigurationBinPath);
+ AZ::IO::FixedMaxPath buildConfigurationPath = (projectPath / projectBuildPath).LexicallyNormal();
+ if (IO::SystemFile::Exists(buildConfigurationPath.c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.Native());
+ }
+
+ // Add the specific build configuration paths to the Settings Registry
+ // First try /bin/$ and if that path doesn't exist
+ // try /bin/$/$
+ buildConfigurationPath /= "bin";
+ if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath,
+ (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).Native());
+ }
+ else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath,
+ (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).Native());
+ }
+ }
+ }
+
+ // Sets the project name within the Settings Registry by looking up the "project_name"
+ // within the project.json file
+ static void SetProjectName(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
+ constexpr auto projectNameKey = FixedValueString(ProjectSettingsRootKey) + "/project_name";
+
+ // Read the project name from the project.json file if it exists
+ if (AZ::IO::FixedMaxPath projectJsonPath = projectPath / "project.json";
+ AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
+ {
+ settingsRegistry.MergeSettingsFile(projectJsonPath.Native(),
+ AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
+ }
+ // If a project name isn't set the default will be set to the final path segment of the project path
+ if (FixedValueString projectName; !settingsRegistry.Get(projectName, projectNameKey))
+ {
+ projectName = projectPath.Filename().Native();
+ settingsRegistry.Set(projectNameKey, projectName);
}
- return projectRoot;
}
AZStd::string_view ConfigParserSettings::DefaultCommentPrefixFilter(AZStd::string_view line)
@@ -397,7 +515,7 @@ namespace AZ::SettingsRegistryMergeUtils
bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath,
const ConfigParserSettings& configParserSettings)
{
- auto configPath = FindEngineRoot(registry) / filePath;
+ auto configPath = FindProjectRoot(registry) / filePath;
IO::FileReader configFile;
bool configFileOpened{};
switch (configParserSettings.m_fileReaderClass)
@@ -542,19 +660,78 @@ namespace AZ::SettingsRegistryMergeUtils
void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
- // Binary folder
- AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
- registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
- // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
- AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
- registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
+ // Binary folder - corresponds to the @exefolder@ alias
+ AZ::IO::FixedMaxPath exePath = AZ::Utils::GetExecutableDirectory();
+ registry.Set(FilePathKey_BinaryFolder, exePath.LexicallyNormal().Native());
- auto projectPathKey = FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
- SettingsRegistryInterface::FixedValueString projectPathValue;
- if (registry.Get(projectPathValue, projectPathKey))
+ // Project path - corresponds to the @projectroot@ alias
+ // NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first
+
+ AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry);
+ if ([[maybe_unused]] constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
+ !projectPath.empty())
{
- // Cache folder
+ if (projectPath.IsRelative())
+ {
+ if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectPath.Native());
+ projectAbsPath.has_value())
+ {
+ projectPath = AZStd::move(*projectAbsPath);
+ }
+ }
+
+ projectPath = projectPath.LexicallyNormal();
+ AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(projectPath.c_str()),
+ R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to a valid absolute path?)"
+ , projectPath.c_str(), AZ_STRING_ARG(projectPathKey));
+
+ registry.Set(FilePathKey_ProjectPath, projectPath.Native());
+ }
+ else
+ {
+ AZ_TracePrintf("SettingsRegistryMergeUtils",
+ R"(Project path isn't set in the Settings Registry at "%.*s".)"
+ " Project-related filepaths will be set relative to the executable directory\n",
+ AZ_STRING_ARG(projectPathKey));
+ projectPath = exePath;
+ registry.Set(FilePathKey_ProjectPath, exePath.Native());
+ }
+
+ // Engine root folder - corresponds to the @engroot@ alias
+ AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
+ if (!engineRoot.empty())
+ {
+ if (engineRoot.IsRelative())
+ {
+ if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
+ engineRootAbsPath.has_value())
+ {
+ engineRoot = AZStd::move(*engineRootAbsPath);
+ }
+ }
+
+ engineRoot = engineRoot.LexicallyNormal();
+ registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native());
+ }
+
+ // Cache folder
+ AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal();
+ if (!projectCachePath.empty())
+ {
+ if (projectCachePath.IsRelative())
+ {
+ if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native());
+ projectCacheAbsPath.has_value())
+ {
+ projectCachePath = AZStd::move(*projectCacheAbsPath);
+ }
+ }
+
+ projectCachePath = projectCachePath.LexicallyNormal();
+ registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native());
+
+ // 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 "assets".
FixedValueString assetPlatform;
@@ -570,124 +747,67 @@ namespace AZ::SettingsRegistryMergeUtils
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
- // Project path - corresponds to the @projectroot@ 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 "%.*s" registry setting set to valid absolute path?)"
- , path.c_str(), aznumeric_cast(projectPathKey.size()), projectPathKey.data());
-
- AZ::IO::FixedMaxPath normalizedProjectPath = path.LexicallyNormal();
- registry.Set(FilePathKey_ProjectPath, normalizedProjectPath.Native());
-
- // Set the user directory with the provided path or using project/user as default
- auto projectUserPathKey = FixedValueString::format("%s/project_user_path", BootstrapSettingsRootKey);
- AZ::IO::FixedMaxPath projectUserPath;
- if (!registry.Get(projectUserPath.Native(), projectUserPathKey))
- {
- projectUserPath = (normalizedProjectPath / "user").LexicallyNormal();
- }
- registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
-
- // Set the log directory with the provided path or using project/user/log as default
- auto projectLogPathKey = FixedValueString::format("%s/project_log_path", BootstrapSettingsRootKey);
- AZ::IO::FixedMaxPath projectLogPath;
- if (!registry.Get(projectLogPath.Native(), projectLogPathKey))
- {
- projectLogPath = (projectUserPath / "log").LexicallyNormal();
- }
- registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
-
- // check for a default write storage path, fall back to the project's user/ directory if not
- AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
- registry.Set(FilePathKey_DevWriteStorage, devWriteStorage.has_value()
- ? devWriteStorage.value()
- : projectUserPath.Native());
-
- // Set the project in-memory build path if the ProjectBuildPath key has been supplied
- if (AZ::IO::FixedMaxPath projectBuildPath; registry.Get(projectBuildPath.Native(), ProjectBuildPath))
- {
- registry.Remove(FilePathKey_ProjectBuildPath);
- registry.Remove(FilePathKey_ProjectConfigurationBinPath);
- AZ::IO::FixedMaxPath buildConfigurationPath = normalizedProjectPath / projectBuildPath;
- if (IO::SystemFile::Exists(buildConfigurationPath.c_str()))
- {
- registry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.LexicallyNormal().Native());
- }
-
- // Add the specific build configuration paths to the Settings Registry
- // First try /bin/$ and if that path doesn't exist
- // try /bin/$/$
- buildConfigurationPath /= "bin";
- if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
- {
- registry.Set(FilePathKey_ProjectConfigurationBinPath,
- (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
- }
- else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
- {
- registry.Set(FilePathKey_ProjectConfigurationBinPath,
- (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
- }
-
- }
-
- // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
- constexpr auto projectNameKey =
- FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
- + "/project_name";
-
- // Read the project name from the project.json file if it exists
- if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json";
- AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
- {
- registry.MergeSettingsFile(projectJsonPath.Native(),
- AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
- }
- if (FixedValueString projectName; !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.
+ // Make sure the asset platform is set before setting cache path for the asset platform.
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.
- auto projectCacheRootOverrideKey = FixedValueString::format("%s/project_cache_path", BootstrapSettingsRootKey);
- // 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 @products@ 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());
- }
+ registry.Set(FilePathKey_CacheRootFolder, (projectCachePath / assetPlatform).Native());
}
}
- else
+
+ // User folder
+ AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath);
+ if (!projectUserPath.empty())
{
- // Set the default ProjectUserPath to the /user directory
- registry.Set(FilePathKey_ProjectUserPath, (engineRoot / "user").LexicallyNormal().Native());
- AZ_TracePrintf("SettingsRegistryMergeUtils",
- R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n",
- aznumeric_cast(projectPathKey.size()), projectPathKey.data());
+ if (projectUserPath.IsRelative())
+ {
+ if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native());
+ projectUserAbsPath.has_value())
+ {
+ projectUserPath = AZStd::move(*projectUserAbsPath);
+ }
+ }
+
+ projectUserPath = projectUserPath.LexicallyNormal();
+ registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
}
+ // Log folder
+ if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty())
+ {
+ if (projectLogPath.IsRelative())
+ {
+ if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native()))
+ {
+ projectLogPath = AZStd::move(*projectLogAbsPath);
+ }
+ }
+
+ projectLogPath = projectLogPath.LexicallyNormal();
+ registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
+ }
+
+ // Developer Write Storage folder
+ if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty())
+ {
+ if (devWriteStoragePath.IsRelative())
+ {
+ if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native()))
+ {
+ devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath);
+ }
+ }
+
+ devWriteStoragePath = devWriteStoragePath.LexicallyNormal();
+ registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native());
+ }
+
+ // Set the project in-memory build path if the ProjectBuildPath key has been supplied
+ SetProjectBuildPath(registry, projectPath);
+ // Set the project name using the "project_name" key
+ SetProjectName(registry, projectPath);
+
#if !AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
// Setup the cache, user, and log paths to platform specific locations when running on non-host platforms
- path = engineRoot;
if (AZStd::optional nonHostCacheRoot = Utils::GetDefaultAppRootPath();
nonHostCacheRoot)
{
@@ -696,25 +816,25 @@ namespace AZ::SettingsRegistryMergeUtils
}
else
{
- registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
- registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
+ registry.Set(FilePathKey_CacheProjectRootFolder, projectPath.Native());
+ registry.Set(FilePathKey_CacheRootFolder, projectPath.Native());
}
if (AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
devWriteStorage)
{
- const AZ::IO::FixedMaxPath devWriteStoragePath(*devWriteStorage);
- registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user/log").LexicallyNormal().Native());
+ const auto devWriteStoragePath = AZ::IO::PathView(*devWriteStorage).LexicallyNormal();
+ registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native());
+ registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").Native());
+ registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user" / "log").Native());
}
else
{
- registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectLogPath, (path / "user/log").LexicallyNormal().Native());
- }
-#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
+ registry.Set(FilePathKey_DevWriteStorage, projectPath.Native());
+ registry.Set(FilePathKey_ProjectUserPath, (projectPath / "user").Native());
+ registry.Set(FilePathKey_ProjectLogPath, (projectPath / "user" / "log").Native());
}
+#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
+}
void MergeSettingsToRegistry_TargetBuildDependencyRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector* scratchBuffer)
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
index daa64c0343..56eec91813 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
@@ -87,9 +87,9 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry);
//! The algorithm that is used to find the project root is as follows
- //! 1. The first time this function is it performs a upward scan for a project.json file from
- //! the executable directory and if found stores that path to an internal key.
- //! In the same step it injects the path into the front of list of command line parameters
+ //! 1. The first time this function runs it performs an upward scan for a "project.json" file from
+ //! the executable directory and stores that path into an internal key.
+ //! In the same step it injects the path into the back of the command line parameters
//! using the --regset="{BootstrapSettingsRootKey}/project_path=" value
//! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set
//!
diff --git a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp
index ff30291a70..a953d53c33 100644
--- a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp
+++ b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.cpp
@@ -1427,26 +1427,36 @@ namespace AZ
namespace AssetPath
{
- void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token)
+ namespace Internal
{
- // Normalize the token to prepare for CRC32 calculation
- AZStd::string normalized = appRootPath;
+ AZ::u32 CalculateBranchTokenHash(AZStd::string_view engineRootPath)
+ {
+ // Normalize the token to prepare for CRC32 calculation
+ auto NormalizeEnginePath = [](const char element) -> char
+ {
+ // Substitute path separators with '_' and lower case
+ return element == AZ::IO::WindowsPathSeparator || element == AZ::IO::PosixPathSeparator
+ ? '_' : static_cast(std::tolower(element));
+ };
- // Strip out any trailing path separators
- AZ::StringFunc::Strip(normalized, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING AZ_WRONG_FILESYSTEM_SEPARATOR_STRING,false, false, true);
+ // Trim off trailing path separators
+ engineRootPath = RStrip(engineRootPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
+ AZ::IO::FixedMaxPathString enginePath;
+ AZStd::transform(engineRootPath.begin(), engineRootPath.end(),
+ AZStd::back_inserter(enginePath), AZStd::move(NormalizeEnginePath));
- // Lower case always
- AZStd::to_lower(normalized.begin(), normalized.end());
-
- // Substitute path separators with '_'
- AZStd::replace(normalized.begin(), normalized.end(), '\\', '_');
- AZStd::replace(normalized.begin(), normalized.end(), '/', '_');
-
- // Perform the CRC32 calculation
- const AZ::Crc32 branchTokenCrc(normalized.c_str(), normalized.size(), true);
- char branchToken[12];
- azsnprintf(branchToken, AZ_ARRAY_SIZE(branchToken), "0x%08X", static_cast(branchTokenCrc));
- token = AZStd::string(branchToken);
+ // Perform the CRC32 calculation
+ constexpr bool forceLowercase = true;
+ return static_cast(AZ::Crc32(enginePath.c_str(), enginePath.size(), forceLowercase));
+ }
+ }
+ void CalculateBranchToken(AZStd::string_view engineRootPath, AZStd::string& token)
+ {
+ token = AZStd::string::format("0x%08X", Internal::CalculateBranchTokenHash(engineRootPath));
+ }
+ void CalculateBranchToken(AZStd::string_view engineRootPath, AZ::IO::FixedMaxPathString& token)
+ {
+ token = AZ::IO::FixedMaxPathString::format("0x%08X", Internal::CalculateBranchTokenHash(engineRootPath));
}
}
diff --git a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h
index 1e651afc93..55236a0fff 100644
--- a/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h
+++ b/Code/Framework/AzCore/AzCore/StringFunc/StringFunc.h
@@ -485,10 +485,11 @@ namespace AZ
//! CalculateBranchToken
/*! Calculate the branch token that is used for asset processor connection negotiations
*
- * \param appRootPath - The absolute path of the app root to base the token calculation on
+ * \param engineRootPath - The absolute path to the engine root to base the token calculation on
* \param token - The result of the branch token calculation
*/
- void CalculateBranchToken(const AZStd::string& appRootPath, AZStd::string& token);
+ void CalculateBranchToken(AZStd::string_view engineRootPath, AZStd::string& token);
+ void CalculateBranchToken(AZStd::string_view engineRootPath, AZ::IO::FixedMaxPathString& token);
}
//////////////////////////////////////////////////////////////////////////
diff --git a/Code/Framework/AzCore/AzCore/Time/ITime.h b/Code/Framework/AzCore/AzCore/Time/ITime.h
index a97ba2319a..a3dcca09ea 100644
--- a/Code/Framework/AzCore/AzCore/Time/ITime.h
+++ b/Code/Framework/AzCore/AzCore/Time/ITime.h
@@ -12,8 +12,8 @@
#include
#include
#include
-#include
#include
+#include
namespace AZ
{
@@ -24,15 +24,21 @@ namespace AZ
//! Using int64_t as the underlying type, this is good to represent approximately 292,471 years
AZ_TYPE_SAFE_INTEGRAL(TimeUs, int64_t);
+ namespace Time
+ {
+ static const AZ::TimeMs ZeroTimeMs = AZ::TimeMs{ 0 };
+ static const AZ::TimeUs ZeroTimeUs = AZ::TimeUs{ 0 };
+ }
+
//! @class ITime
//! @brief This is an AZ::Interface<> for managing time related operations.
//! AZ::ITime and associated types may not operate in realtime. These abstractions are to allow our application
//! simulation to operate both slower and faster than realtime in a well defined and user controllable manner
- //! The rate at which time passes for AZ::ITime is controlled by the cvar t_scale
- //! t_scale == 0 means simulation time should halt
- //! 0 < t_scale < 1 will cause time to pass slower than realtime, with t_scale 0.1 being roughly 1/10th realtime
- //! t_scale == 1 will cause time to pass at roughly realtime
- //! t_scale > 1 will cause time to pass faster than normal, with t_scale 10 being roughly 10x realtime
+ //! The rate at which time passes for AZ::ITime is controlled by the cvar t_simulationTickScale
+ //! t_simulationTickScale == 0 means simulation time should halt
+ //! 0 < t_simulationTickScale < 1 will cause time to pass slower than realtime, with t_simulationTickScale 0.1 being roughly 1/10th realtime
+ //! t_simulationTickScale == 1 will cause time to pass at roughly realtime
+ //! t_simulationTickScale > 1 will cause time to pass faster than normal, with t_simulationTickScale 10 being roughly 10x realtime
class ITime
{
public:
@@ -41,15 +47,72 @@ namespace AZ
ITime() = default;
virtual ~ITime() = default;
- //! Returns the number of milliseconds since application start.
- //! @return the number of milliseconds that have elapsed since application start
+ //! Returns the number of milliseconds since application start scaled by t_simulationTickScale.
+ //! @return The number of milliseconds that have elapsed since application start.
virtual TimeMs GetElapsedTimeMs() const = 0;
- //! Returns the number of microseconds since application start.
+ //! Returns the number of microseconds since application start scaled by t_simulationTickScale.
//! @return the number of microseconds that have elapsed since application start
virtual TimeUs GetElapsedTimeUs() const = 0;
+
+ //! Returns the number of milliseconds since application start.
+ //! This value is not affected by the t_simulationTickScale cvar.
+ //! @return The number of milliseconds that have elapsed since application start.
+ virtual TimeMs GetRealElapsedTimeMs() const = 0;
+
+ //! Returns the number of microseconds since application start.
+ //! This value is not affected by the t_simulationTickScale cvar.
+ //! @return The number of microseconds that have elapsed since application start.
+ virtual TimeUs GetRealElapsedTimeUs() const = 0;
+
+ //! Returns the current simulation tick delta time.
+ //! This is affected by the cvars t_simulationTickScale, t_simulationTickDeltaOverride, and t_maxGameTickDelta.
+ //! @return The number of microseconds elapsed since the last game tick.
+ virtual TimeUs GetSimulationTickDeltaTimeUs() const = 0;
+
+ //! Returns the non-manipulated tick time.
+ //! @return The number of microseconds elapsed since the last game tick.
+ virtual TimeUs GetRealTickDeltaTimeUs() const = 0;
+
+ //! Returns the time since application start of when the last simulation tick was updated.
+ virtual TimeUs GetLastSimulationTickTime() const = 0;
+
+ //! If > 0 this will override the simulation tick delta time with the provided value.
+ //! When enabled this will ignore any set simulation tick scale.
+ //! Setting to 0 disables the override.
+ //! @param timeMs The time in milliseconds to use for the tick delta.
+ virtual void SetSimulationTickDeltaOverride(TimeMs timeMs) = 0;
+
+ //! Returns the current simulation tick override.
+ //! 0 means disabled.
+ //! @returns The current simulation tick override in milliseconds.
+ virtual TimeMs GetSimulationTickDeltaOverride() const = 0;
+
+ //! A scalar amount to adjust the passage of time by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime.
+ //! @param scale The scalar value to apply to the simulation time.
+ virtual void SetSimulationTickScale(float scale) = 0;
+
+ //! Returns the current simulation tick scale.
+ //! 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime.
+ //! @returns The simulation tick scale value.
+ virtual float GetSimulationTickScale() const = 0;
+
+ //! The minimum rate to force the simulation tick to run.
+ //! 0 for as fast as possible. 30 = ~33ms, 60 = ~16ms.
+ //! Setting to 0 will disable rate limiting.
+ //! @note It is not guaranteed to hit the requested tick rate exactly.
+ //! @param rate The rate in frames per second.
+ virtual void SetSimulationTickRate(int rate) = 0;
+
+ //! Return the current simulation tick rate.
+ //! 0 means disabled.
+ //! @return The rate in frames per second.
+ virtual int32_t GetSimulationTickRate() const = 0;
AZ_DISABLE_COPY_MOVE(ITime);
+
+ static const AZ::TimeMs ZeroTimeMs = AZ::TimeMs{ 0 };
+ static const AZ::TimeUs ZeroTimeUs = AZ::TimeUs{ 0 };
};
// EBus wrapper for ScriptCanvas
@@ -74,6 +137,29 @@ namespace AZ
return AZ::Interface::Get()->GetElapsedTimeUs();
}
+ inline TimeMs GetRealElapsedTimeMs()
+ {
+ return AZ::Interface::Get()->GetRealElapsedTimeMs();
+ }
+
+ //! This is a simple convenience wrapper
+ inline TimeUs GetSimulationTickDeltaTimeUs()
+ {
+ return AZ::Interface::Get()->GetSimulationTickDeltaTimeUs();
+ }
+
+ //! This is a simple convenience wrapper
+ inline TimeUs GetRealTickDeltaTimeUs()
+ {
+ return AZ::Interface::Get()->GetRealTickDeltaTimeUs();
+ }
+
+ //! This is a simple convenience wrapper
+ inline TimeUs GetLastSimulationTickTime()
+ {
+ return AZ::Interface::Get()->GetLastSimulationTickTime();
+ }
+
//! Converts from milliseconds to microseconds
inline TimeUs TimeMsToUs(TimeMs value)
{
@@ -92,12 +178,24 @@ namespace AZ
return static_cast(value) / 1000.0f;
}
+ //! Converts from milliseconds to seconds
+ inline double TimeMsToSecondsDouble(TimeMs value)
+ {
+ return static_cast(value) / 1000.0;
+ }
+
//! Converts from microseconds to seconds
inline float TimeUsToSeconds(TimeUs value)
{
return static_cast(value) / 1000000.0f;
}
+ //! Converts from microseconds to seconds
+ inline double TimeUsToSecondsDouble(TimeUs value)
+ {
+ return static_cast(value) / 1000000.0;
+ }
+
//! Converts from milliseconds to AZStd::chrono::time_point
inline auto TimeMsToChrono(TimeMs value)
{
@@ -113,6 +211,20 @@ namespace AZ
auto chronoValue = AZStd::chrono::microseconds(aznumeric_cast(value));
return epoch + chronoValue;
}
+
+ //! A utility function to convert from seconds to TimeMs
+ inline TimeMs SecondsToTimeMs(const double value)
+ {
+ const double valueMs = value * 1000.0;
+ return static_cast(static_cast(valueMs));
+ }
+
+ //! A utility function to convert from seconds to TimeUs
+ inline TimeUs SecondsToTimeUs(const double value)
+ {
+ const double valueMs = value * 1000000.0;
+ return static_cast(static_cast(valueMs));
+ }
} // namespace AZ
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeMs);
diff --git a/Code/Framework/AzCore/AzCore/Time/TimeSystem.cpp b/Code/Framework/AzCore/AzCore/Time/TimeSystem.cpp
new file mode 100644
index 0000000000..cd0170749f
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Time/TimeSystem.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+#include