Merge remote-tracking branch 'upstream/development' into nvsickle/OutlinerDuplicateEntryFixes

This commit is contained in:
nvsickle
2021-10-13 13:24:24 -07:00
1722 changed files with 154722 additions and 22915 deletions
@@ -173,6 +173,7 @@ namespace AZ
if (assetTracker)
{
assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
@@ -185,7 +186,20 @@ namespace AZ
return context.Report(result, message);
}
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
{
m_serializedAssets.emplace_back(asset);
}
@@ -199,5 +213,6 @@ namespace AZ
{
return m_serializedAssets;
}
} // namespace Data
} // namespace AZ
@@ -39,13 +39,18 @@ namespace AZ
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
using AssetFixUp = AZStd::function<void(Asset<AssetData>& asset)>;
void AddAsset(Asset<AssetData>& asset);
void SetAssetFixUp(AssetFixUp assetFixUpCallback);
void FixUpAsset(Asset<AssetData>& asset);
void AddAsset(Asset<AssetData> asset);
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
private:
AZStd::vector<Asset<AssetData>> m_serializedAssets;
AssetFixUp m_assetFixUpCallback;
};
} // namespace Data
} // namespace AZ
@@ -23,6 +23,7 @@
#include <AzCore/Console/LoggerSystemComponent.h>
#include <AzCore/EBus/EventSchedulerSystemComponent.h>
#include <AzCore/Task/TaskGraphSystemComponent.h>
#include <AzCore/Statistics/StatisticalProfilerProxySystemComponent.h>
namespace AZ
{
@@ -44,6 +45,10 @@ namespace AZ
EventSchedulerSystemComponent::CreateDescriptor(),
TaskGraphSystemComponent::CreateDescriptor(),
#if !defined(_RELEASE)
Statistics::StatisticalProfilerProxySystemComponent::CreateDescriptor(),
#endif
#if !defined(AZCORE_EXCLUDE_LUA)
ScriptSystemComponent::CreateDescriptor(),
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
@@ -58,6 +63,10 @@ namespace AZ
azrtti_typeid<LoggerSystemComponent>(),
azrtti_typeid<EventSchedulerSystemComponent>(),
azrtti_typeid<TaskGraphSystemComponent>(),
#if !defined(_RELEASE)
azrtti_typeid<Statistics::StatisticalProfilerProxySystemComponent>(),
#endif
};
}
}
@@ -1367,9 +1367,6 @@ namespace AZ
#endif
}
//=========================================================================
// Tick
//=========================================================================
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
{
{
@@ -1397,9 +1394,6 @@ namespace AZ
}
}
//=========================================================================
// Tick
//=========================================================================
void ComponentApplication::TickSystem()
{
AZ_PROFILE_SCOPE(System, "Component application tick");
@@ -1547,5 +1541,4 @@ namespace AZ
AZ::SettingsRegistryScriptUtils::ReflectSettingsRegistryToBehaviorContext(*behaviorContext);
}
}
} // namespace AZ
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/ComponentApplicationBus.h>
@@ -476,15 +476,16 @@ namespace AZ
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
// at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
&& inputKey.IsRelativeTo(consoleRootCommandKey))
&& (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
@@ -510,12 +511,24 @@ namespace AZ
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
// The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
// Abuses the IsRelativeToFuncton function of the path class to extract the console
// command from the settings registry objects
FixedValueString command;
if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
{
command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
}
else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
{
command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
}
if (!command.empty())
{
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
@@ -603,9 +616,10 @@ namespace AZ
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
// Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
@@ -31,7 +31,8 @@ namespace AZ
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
@@ -11,6 +11,7 @@
#include <AzCore/Module/Environment.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
AZ_DEFINE_BUDGET(Animation);
AZ_DEFINE_BUDGET(Audio);
@@ -30,8 +31,7 @@ namespace AZ::Debug
};
Budget::Budget(const char* name)
: m_name{ name }
, m_crc{ Crc32(name) }
: Budget( name, Crc32(name) )
{
}
@@ -40,6 +40,10 @@ namespace AZ::Debug
, m_crc{ crc }
{
m_impl = aznew BudgetImpl;
if (auto statsProfiler = Interface<Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
statsProfiler->RegisterProfilerId(m_crc);
}
}
Budget::~Budget()
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/Debug/Budget.h>
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
#ifdef USE_PIX
#include <AzCore/PlatformIncl.h>
@@ -44,7 +45,10 @@
#define AZ_PROFILE_INTERVAL_START(...)
#define AZ_PROFILE_INTERVAL_START_COLORED(...)
#define AZ_PROFILE_INTERVAL_END(...)
#define AZ_PROFILE_INTERVAL_SCOPED(...)
#define AZ_PROFILE_INTERVAL_SCOPED(budget, scopeNameId, ...) \
static constexpr AZ::Crc32 AZ_JOIN(blockId, __LINE__)(scopeNameId); \
AZ::Statistics::StatisticalProfilerProxy::TimedScope AZ_JOIN(scope, __LINE__)(AZ_CRC_CE(#budget), AZ_JOIN(blockId, __LINE__));
#endif
#ifndef AZ_PROFILE_DATAPOINT
@@ -46,5 +46,23 @@ namespace AZ
private:
AZStd::sys_time_t m_timeStamp;
};
//! Utility type that updates the given variable with the lifetime of the object in cycles.
//! Useful for quick scope based timing.
struct ScopedTimer
{
explicit ScopedTimer(AZStd::sys_time_t& variable)
: m_variable(variable)
{
m_timer.Stamp();
}
~ScopedTimer()
{
m_variable = m_timer.GetDeltaTimeInTicks();
}
AZStd::sys_time_t& m_variable;
Timer m_timer;
};
}
}
@@ -224,6 +224,8 @@ namespace AZ
void Debug::Trace::Terminate(int exitCode)
{
AZ_TracePrintf("Exit", "Called Terminate() with exit code: 0x%x", exitCode);
AZ::Debug::Trace::PrintCallstack("Exit");
Platform::Terminate(exitCode);
}
+17 -9
View File
@@ -160,8 +160,8 @@ namespace AZ
/**
* Locking primitive that is used when executing events in the event queue.
*/
using EventQueueMutexType = typename AZStd::Utils::if_c<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>::type;
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>;
/**
* Pointer to an address on the bus.
@@ -180,14 +180,22 @@ namespace AZ
* `<BusName>::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
static const bool EnableEventQueue = Traits::EnableEventQueue;
static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
/**
* True if the EBus supports more than one address. Otherwise, false.
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not the EBus Context Mutex if LocklessDispatch is true
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
};
/**
@@ -460,7 +468,7 @@ namespace AZ
using BusPtr = typename Traits::BusPtr;
/**
* Helper to queue an event by BusIdType only when function queueing is enabled
* Helper to queue an event by BusIdType only when function queueing is enabled
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
@@ -581,7 +589,7 @@ namespace AZ
, public EBusBroadcaster<Bus, Traits>
, public EBusEventer<Bus, Traits>
, public EBusEventEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
{
};
@@ -599,7 +607,7 @@ namespace AZ
: public EventDispatcher<Bus, Traits>
, public EBusBroadcaster<Bus, Traits>
, public EBusBroadcastEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
{
};
+41 -8
View File
@@ -12,7 +12,7 @@
* that Open 3D Engine uses to dispatch notifications and receive requests.
* EBuses are configurable and support many different use cases.
* For more information about %EBuses, see AZ::EBus in this guide and
* [Event Bus](http://docs.aws.amazon.com/lumberyard/latest/developerguide/asset-pipeline-ebus.html)
* [Event Bus](https://o3de.org/docs/user-guide/engine/ebus/)
* in the *Open 3D Engine Developer Guide*.
*/
@@ -62,7 +62,7 @@ namespace AZ
* @endcode
*
* For more information about %EBuses, see EBus in this guide and
* [Event Bus](http://docs.aws.amazon.com/lumberyard/latest/developerguide/asset-pipeline-ebus.html)
* [Event Bus](https://o3de.org/docs/user-guide/engine/ebus/)
* in the *Open 3D Engine Developer Guide*.
*/
struct EBusTraits
@@ -77,9 +77,11 @@ namespace AZ
public:
/**
* Allocator used by the EBus.
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
* The default setting is Internal EBusEnvironmentAllocator
* EBus code stores their Context instances in static memory
* Therfore the configured allocator must last as long as the EBus in a module
*/
using AllocatorType = AZStd::allocator;
using AllocatorType = AZ::Internal::EBusEnvironmentAllocator;
/**
* Defines how many handlers can connect to an address on the EBus
@@ -236,6 +238,17 @@ namespace AZ
* code before or after an event.
*/
using EventProcessingPolicy = EBusEventProcessingPolicy;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus Context uses the LockGuard when dispatching
* (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>)
* The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
* Otherwise the value above in EBusTraits.h is always used and not the value
* that the derived trait class sets.
*/
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = AZStd::conditional_t<IsLocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
};
namespace Internal
@@ -259,8 +272,8 @@ namespace AZ
*
* EBuses are configurable and support many different use cases.
* For more information about EBuses, see
* [Event Bus](http://docs.aws.amazon.com/lumberyard/latest/developerguide/asset-pipeline-ebus.html)
* and [Components and EBuses: Best Practices ](http://docs.aws.amazon.com/lumberyard/latest/developerguide/component-entity-system-pg-components-ebuses-best-practices.html)
* [Event Bus](https://o3de.org/docs/user-guide/engine/ebus/)
* and [Components and EBuses: Best Practices ](https://o3de.org/docs/user-guide/components/development/entity-system-pg-components-ebuses-best-practices/)
* in the *Open 3D Engine Developer Guide*.
*
* ## How Components Use EBuses
@@ -496,6 +509,14 @@ namespace AZ
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not EBus Context Mutex when LocklessDispatch is set
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard<DispatchMutex>;
//////////////////////////////////////////////////////////////////////////
// Check to help identify common mistakes
/// @cond EXCLUDE_DOCS
@@ -620,11 +641,11 @@ namespace AZ
using ContextMutexType = AZStd::conditional_t<BusTraits::LocklessDispatch && AZStd::is_same_v<MutexType, AZ::NullMutex>, AZStd::shared_mutex, MutexType>;
/**
* The scoped lock guard to use (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>
* The scoped lock guard to use
* during broadcast/event dispatch.
* @see EBusTraits::LocklessDispatch
*/
using DispatchLockGuard = AZStd::conditional_t<BusTraits::LocklessDispatch, AZ::Internal::NullLockGuard<ContextMutexType>, AZStd::scoped_lock<ContextMutexType>>;
using DispatchLockGuard = DispatchLockGuard<ContextMutexType>;
/**
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
@@ -704,6 +725,11 @@ namespace AZ
static Context& GetOrCreateContext(bool trackCallstack=true);
static bool IsInDispatch(Context* context = GetContext(false));
/**
* Returns whether the EBus context is in the middle of a dispatch on the current thread
*/
static bool IsInDispatchThisThread(Context* context = GetContext(false));
/// @cond EXCLUDE_DOCS
struct RouterCallstackEntry
: public CallstackEntry
@@ -1208,6 +1234,13 @@ AZ_POP_DISABLE_WARNING
return context != nullptr && context->m_dispatches > 0;
}
template<class Interface, class Traits>
bool EBus<Interface, Traits>::IsInDispatchThisThread(Context* context)
{
return context != nullptr && context->s_callstack != nullptr
&& context->s_callstack->m_prev != nullptr;
}
//=========================================================================
template<class Interface, class Traits>
EBus<Interface, Traits>::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
+10 -5
View File
@@ -148,7 +148,7 @@ namespace AZ
virtual AZ::u64 ModificationTime(HandleType fileHandle) = 0;
virtual AZ::u64 ModificationTime(const char* filePath) = 0;
/// Get the size of the file. Returns Success if we report size.
/// Get the size of the file. Returns Success if we report size.
virtual Result Size(const char* filePath, AZ::u64& size) = 0;
virtual Result Size(HandleType fileHandle, AZ::u64& size) = 0;
@@ -198,7 +198,7 @@ namespace AZ
/// note: the callback will contain the full concatenated path (filePath + slash + fileName)
/// not just the individual file name found.
/// note: if the file path of the found file corresponds to a registered ALIAS, the longest matching alias will be returned
/// so expect return values like @assets@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
/// so expect return values like @products@/textures/mytexture.dds instead of a full path. This is so that fileIO works over remote connections.
/// note: if rootPath is specified the implementation has the option of substituting it for the current directory
/// as would be the case on a file server.
typedef AZStd::function<bool(const char*)> FindFilesCallbackType;
@@ -206,13 +206,18 @@ namespace AZ
// Alias system
/// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @root@, etc.
/// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @products@, etc.
virtual void SetAlias(const char* alias, const char* path) = 0;
/// ClearAlias - Removes an alias from the path resolution system
virtual void ClearAlias(const char* alias) = 0;
/// GetAlias - Returns the destination path for a given alias, or nullptr if the alias does not exist
virtual const char* GetAlias(const char* alias) const = 0;
/// SetDeprecateAlias - Adds a deprecated alias with path resolution which points to a new alias
/// When the DeprecatedAlias is used an Error is logged and the alias is resolved to the path
/// specified by the new alais
virtual void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) = 0;
/// Shorten the given path if it contains an alias. it will always pick the longest alias match.
/// note that it re-uses the buffer, since the data can only get smaller and we don't want to internally allocate memory if we
/// can avoid it.
@@ -230,8 +235,8 @@ namespace AZ
//! 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
//! NOTE: If the path does not start with an alias then the resolved value of the @products@ is used
//! which has the effect of making the path relative to the @products@/ 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
+16 -16
View File
@@ -42,8 +42,8 @@ namespace AZ::IO
// These functions can't be called after a request has been queued.
//
//! Creates a request to read a file.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! Creates a request to read a file.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -62,9 +62,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Sets a request to the read command.
//! Sets a request to the read command.
//! @param request The request that will store the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
//! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
//! @param readSize The number of bytes to read from the file at the relative path.
@@ -84,8 +84,8 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Creates a request to the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! Creates a request to the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -106,9 +106,9 @@ namespace AZ::IO
IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
size_t offset = 0) = 0;
//! Sets a request to the read command.
//! Sets a request to the read command.
//! @param request The request that will store the read command.
//! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
//! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
//! be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
//! to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -138,7 +138,7 @@ namespace AZ::IO
//! @result A smart pointer to the newly created request with the cancel command.
virtual FileRequestPtr Cancel(FileRequestPtr target) = 0;
//! Sets a request to the cancel command.
//! Sets a request to the cancel command.
//! When this request completes it's not guaranteed to have canceled the target request. Not all requests can be canceled and requests
//! that already processing may complete. It's recommended to let the target request handle the completion of the request as normal
//! and handle cancellation by checking the status on the target request is set to IStreamerTypes::RequestStatus::Canceled.
@@ -177,7 +177,7 @@ namespace AZ::IO
//! DestroyDedicatedCache is called. Typical use of a dedicated cache is for files that have their own compression
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to create a dedicated cache.
virtual FileRequestPtr CreateDedicatedCache(AZStd::string_view relativePath) = 0;
@@ -186,25 +186,25 @@ namespace AZ::IO
//! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
//! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
//! @param request The request that will store the command to create a dedicated cache.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& CreateDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to destroy a dedicated cache.
virtual FileRequestPtr DestroyDedicatedCache(AZStd::string_view relativePath) = 0;
//! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
//! @param request The request that will store the command to destroy a dedicated cache.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& DestroyDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
//! Clears a file from all caches in use by Streamer.
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A smart pointer to the newly created request with the command to flush a file from all caches.
virtual FileRequestPtr FlushCache(AZStd::string_view relativePath) = 0;
@@ -212,7 +212,7 @@ namespace AZ::IO
//! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
//! processing. This can result in a noticeable interruption.
//! @param request The request that will store the command to flush a file from all caches.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
//! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
//! @return A reference to the provided request.
virtual FileRequestPtr& FlushCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
@@ -334,7 +334,7 @@ namespace AZ::IO
//
//! Collect statistics from all the components that make up Streamer.
//! This is thread safe in the sense that it won't crash.
//! This is thread safe in the sense that it won't crash.
//! Data is collected lockless from involved threads and might be slightly
//! out of date in some cases.
//! @param statistics The container where statistics will be added to.
+32 -22
View File
@@ -98,6 +98,11 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const noexcept;
// as_posix
//! Replicates the behavior of the Python pathlib as_posix method
//! by replacing the Windows Path Separator with the Posix Path Seperator
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
// decomposition
//! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of
//! "/O3DE/foo/bar/name.txt"
@@ -178,7 +183,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
//! # Paths parts of ".." are removed if there is a preceding directory
//! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -238,7 +243,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
//! according to the following
//! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
@@ -253,24 +258,23 @@ namespace AZ::IO
template <typename StringType>
friend class BasicPath;
friend struct AZStd::hash<PathView>;
template <typename PathResultType>
static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base);
struct PathIterable;
static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept;
//! Returns a structure that provides a view of the path parts which can be used for iteration
//! Only the path parts that correspond to creating an normalized path is returned
//! This function is useful for returning a "view" into a normalized path without the need
//! to allocate memory for the heap
static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept;
// joins the input path to the Path Iterable structure using similiar logic to Path::Append
// If the input path is absolute it will replace the current PathIterable otherwise
// the input path will be appended to the Path Iterable structure
// For example a PathIterable with parts = ['C:', '/', 'foo']
// If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
// If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
// If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
// If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
//! joins the input path to the Path Iterable structure using similiar logic to Path::Append
//! If the input path is absolute it will replace the current PathIterable otherwise
//! the input path will be appended to the Path Iterable structure
//! For example a PathIterable with parts = ['C:', '/', 'foo']
//! If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
//! If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
//! If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
//! If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept;
constexpr int ComparePathView(const PathView& other) const;
@@ -325,32 +329,32 @@ namespace AZ::IO
constexpr BasicPath(BasicPath&& other) = default;
// Conversion constructor for other types of BasicPath instantiations
constexpr BasicPath(const PathView& other);
constexpr BasicPath(const PathView& other) noexcept;
// String constructors
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const string_type& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(const string_type& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by moving the pathString to its internal string
//! The preferred separator is to the OS default path separator
constexpr BasicPath(string_type&& pathString) noexcept;
//! Constructs a Path by copying the pathString to its internal string
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(string_type&& pathString, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
//! The preferred separator is to the OS default path separator
constexpr BasicPath(AZStd::string_view src) noexcept;
//! Constructs a Path by constructing it's internal out of a string_view
//! The preferred separators it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(AZStd::string_view src, const char preferredSeparator) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
//! The preferred separator is to the OS default path separator
constexpr BasicPath(const value_type* pathString) noexcept;
//! Constructs a Path by constructing it's internal out of a value_type*
//! The preferred separator it set to the parameter
//! The preferred separator is set to the parameter
constexpr BasicPath(const value_type* pathString, const char preferredSeparator) noexcept;
//! Constructs a empty Path with the preferred separator set to the parameter
explicit constexpr BasicPath(const char preferredSeparator) noexcept;
@@ -371,7 +375,7 @@ namespace AZ::IO
constexpr BasicPath& operator=(BasicPath&& other) = default;
// conversion assignment operator
constexpr BasicPath& operator=(const PathView& pathView);
constexpr BasicPath& operator=(const PathView& pathView) noexcept;
constexpr BasicPath& operator=(const string_type& str) noexcept;
constexpr BasicPath& operator=(string_type&& str) noexcept;
constexpr BasicPath& operator=(AZStd::string_view str) noexcept;
@@ -477,6 +481,12 @@ namespace AZ::IO
//! made from the internal string
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const;
// as_posix
//! Replicates the behavior of the Python pathlib as_posix method
//! by replacing the Windows Path Separator with the Posix Path Seperator
AZStd::string StringAsPosix() const;
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
// compare
//! Performs a compare of each of the path parts for equivalence
//! Each part of the path is compare using string comparison
@@ -574,7 +584,7 @@ namespace AZ::IO
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
//! # Path parts of "." are collapsed to nothing empty
//! # Paths parts of ".." are removed if there is a preceding directory
//! # Paths parts of ".." are removed if there is a preceding directory
//! The preceding directory is also removed
//! # Runs of Two or more path separators are collapsed into one path separator
//! unless the path begins with two path separators
@@ -616,7 +626,7 @@ namespace AZ::IO
// iterators
//! Returns an iterator to the beginning of the path that can be used to traverse the path
//! according to the following
//! according to the following
//! 1. Root name - (0 or 1)
//! 2. Root directory - (0 or 1)
//! 3. Filename - (0 or more)
+59 -24
View File
@@ -240,6 +240,14 @@ namespace AZ::IO
return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
constexpr AZStd::fixed_string<MaxPathLength> PathView::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), AZ::IO::WindowsPathSeparator, AZ::IO::PosixPathSeparator);
return resultPath;
}
// decomposition
constexpr auto PathView::RootName() const -> PathView
{
@@ -473,8 +481,7 @@ namespace AZ::IO
return lhs.Compare(rhs) >= 0;
}
template <typename PathResultType>
constexpr void PathView::MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base)
constexpr void PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept
{
const bool exactCaseCompare = path.m_preferred_separator == PosixPathSeparator
|| base.m_preferred_separator == PosixPathSeparator;
@@ -492,13 +499,11 @@ namespace AZ::IO
if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, exactCaseCompare);
res != 0)
{
pathResult.m_path = AZStd::string_view{};
return;
}
}
else if (CheckIterMismatchAtBase())
{
pathResult.m_path = AZStd::string_view{};
return;
}
@@ -512,7 +517,6 @@ namespace AZ::IO
}
if (CheckIterMismatchAtBase())
{
pathResult.m_path = AZStd::string_view{};
return;
}
}
@@ -530,7 +534,7 @@ namespace AZ::IO
// If there is no mismatch, return ".".
if (!pathParser && !pathParserBase)
{
pathResult.m_path = AZStd::string_view{ "." };
pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
@@ -539,27 +543,25 @@ namespace AZ::IO
int elemCount = parser::DetermineLexicalElementCount(pathParserBase);
if (elemCount < 0)
{
pathResult.m_path = AZStd::string_view{};
return;
}
// if elemCount == 0 and (pathParser == end() || pathParser->empty()), returns path("."); otherwise
if (elemCount == 0 && (pathParser.AtEnd() || *pathParser == ""))
{
pathResult.m_path = AZStd::string_view{ "." };
pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
return;
}
// return a path constructed with 'n' dot-dot elements, followed by the
// elements of '*this' after the mismatch.
pathResult = PathResultType(path.m_preferred_separator);
while (elemCount--)
{
pathResult /= "..";
pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot);
}
for (; pathParser; ++pathParser)
{
pathResult /= *pathParser;
pathIterable.emplace_back(*pathParser, parser::ClassifyPathPart(pathParser));
}
}
@@ -673,7 +675,7 @@ namespace AZ::IO
// Basic Path implementation
template <typename StringType>
constexpr BasicPath<StringType>::BasicPath(const PathView& other)
constexpr BasicPath<StringType>::BasicPath(const PathView& other) noexcept
: m_path(other.m_path)
, m_preferred_separator(other.m_preferred_separator) {}
@@ -726,6 +728,7 @@ namespace AZ::IO
: m_path(first, last)
, m_preferred_separator(preferredSeparator) {}
template <typename StringType>
constexpr BasicPath<StringType>::operator PathView() const noexcept
{
@@ -733,7 +736,7 @@ namespace AZ::IO
}
template <typename StringType>
constexpr auto BasicPath<StringType>::operator=(const PathView& other) -> BasicPath&
constexpr auto BasicPath<StringType>::operator=(const PathView& other) noexcept -> BasicPath&
{
m_path = other.m_path;
m_preferred_separator = other.m_preferred_separator;
@@ -974,13 +977,13 @@ namespace AZ::IO
template <typename StringType>
constexpr auto BasicPath<StringType>::MakePreferred() -> BasicPath&
{
if (m_preferred_separator != '/')
if (m_preferred_separator != PosixPathSeparator)
{
AZStd::replace(m_path.begin(), m_path.end(), '/', m_preferred_separator);
AZStd::replace(m_path.begin(), m_path.end(), PosixPathSeparator, m_preferred_separator);
}
else
{
AZStd::replace(m_path.begin(), m_path.end(), '\\', m_preferred_separator);
AZStd::replace(m_path.begin(), m_path.end(), WindowsPathSeparator, m_preferred_separator);
}
return *this;
}
@@ -1033,6 +1036,24 @@ namespace AZ::IO
return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
}
// as_posix
// Returns a copy of the path with the path separators converted to PosixPathSeparator
template <typename StringType>
AZStd::string BasicPath<StringType>::StringAsPosix() const
{
AZStd::string resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr AZStd::fixed_string<MaxPathLength> BasicPath<StringType>::FixedMaxPathStringAsPosix() const noexcept
{
AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
constexpr void BasicPath<StringType>::swap(BasicPath& rhs) noexcept
{
@@ -1234,6 +1255,7 @@ namespace AZ::IO
{
pathResult /= pathPartView;
}
return pathResult;
}
@@ -1241,7 +1263,13 @@ namespace AZ::IO
constexpr auto BasicPath<StringType>::LexicallyRelative(const PathView& base) const -> BasicPath
{
BasicPath pathResult(m_preferred_separator);
static_cast<PathView>(*this).MakeRelativeTo(pathResult, *this, base);
PathView::PathIterable pathIterable;
PathView::MakeRelativeTo(pathIterable, *this, base);
for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
{
pathResult /= pathPartView;
}
return pathResult;
}
@@ -1355,7 +1383,7 @@ namespace AZ::IO
return !basePathParts.empty() || !thisPathParts.IsAbsolute();
}
constexpr FixedMaxPath PathView::LexicallyNormal() const
constexpr auto PathView::LexicallyNormal() const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
PathIterable pathIterable = GetNormalPathParts(*this);
@@ -1367,21 +1395,28 @@ namespace AZ::IO
return pathResult;
}
constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const
constexpr auto PathView::LexicallyRelative(const PathView& base) const -> FixedMaxPath
{
FixedMaxPath pathResult(m_preferred_separator);
MakeRelativeTo(pathResult, *this, base);
PathIterable pathIterable;
MakeRelativeTo(pathIterable, *this, base);
for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
{
pathResult /= pathPartView;
}
return pathResult;
}
constexpr FixedMaxPath PathView::LexicallyProximate(const PathView& base) const
constexpr auto PathView::LexicallyProximate(const PathView& base) const -> FixedMaxPath
{
FixedMaxPath result = LexicallyRelative(base);
if (result.empty())
FixedMaxPath pathResult = LexicallyRelative(base);
if (pathResult.empty())
{
return FixedMaxPath(*this);
}
return result;
return pathResult;
}
}
@@ -49,8 +49,8 @@ namespace AZ::IO
constexpr void clear() noexcept;
friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable;
friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void;
friend constexpr auto PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView&, const AZ::IO::PathView&) noexcept -> void;
PartKindArray m_parts{};
size_t m_size{};
};
@@ -35,6 +35,7 @@ namespace Platform
SystemFile::SizeType Length(FileHandleType handle, const SystemFile* systemFile);
bool Exists(const char* fileName);
bool IsDirectory(const char* filePath);
void FindFiles(const char* filter, SystemFile::FindFileCB cb);
AZ::u64 ModificationTime(const char* fileName);
SystemFile::SizeType Length(const char* fileName);
@@ -235,6 +236,11 @@ bool SystemFile::Exists(const char* fileName)
return Platform::Exists(fileName);
}
bool SystemFile::IsDirectory(const char* filePath)
{
return Platform::IsDirectory(filePath);
}
void SystemFile::FindFiles(const char* filter, FindFileCB cb)
{
Platform::FindFiles(filter, cb);
@@ -99,6 +99,8 @@ namespace AZ
// Utility functions
/// Check if a file or directory exists.
static bool Exists(const char* path);
/// Check if path is a directory
static bool IsDirectory(const char* path);
/// FindFiles
typedef AZStd::function<bool /* true to continue to enumerate otherwise false */ (const char* /* fileName*/, bool /* true if file, false if folder*/)> FindFileCB;
static void FindFiles(const char* filter, FindFileCB cb);
@@ -18,6 +18,14 @@
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Threading/ThreadUtils.h>
AZ_CVAR(float, cl_jobThreadsConcurrencyRatio, 0.6f, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system multiplier on the number of hw threads the machine creates at initialization");
AZ_CVAR(uint32_t, cl_jobThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system number of hardware threads that are reserved for O3DE system threads");
AZ_CVAR(uint32_t, cl_jobThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system minimum number of worker threads to create after scaling the number of hw threads");
namespace AZ
{
//=========================================================================
@@ -46,9 +54,10 @@ namespace AZ
JobManagerThreadDesc threadDesc;
int numberOfWorkerThreads = m_numberOfWorkerThreads;
if (numberOfWorkerThreads <= 0)
if (numberOfWorkerThreads <= 0) // spawn default number of threads
{
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency());
uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), scaledHardwareThreads);
#if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS);
#endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
@@ -36,7 +36,7 @@ namespace AZ
*/
int m_stackSize;
JobManagerThreadDesc(int cpuId = -1, int priority = -100000, int stackSize = -1)
JobManagerThreadDesc(int cpuId = -1, int priority = 0, int stackSize = -1)
: m_cpuId(cpuId)
, m_priority(priority)
, m_stackSize(stackSize)
-1
View File
@@ -5,7 +5,6 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
+3 -9
View File
@@ -135,18 +135,12 @@ namespace AZ
{
stringLength = strlen(uuidString);
}
if (stringLength > MaxPermissiveStringSize)
{
if (!skipWarnings)
{
AZ_Warning("Math", false, "Can't create UUID from string length %zu over maximum %zu", stringLength, MaxPermissiveStringSize);
}
return Uuid::CreateNull();
}
size_t newLength{ 0 };
char createString[MaxPermissiveStringSize];
for (size_t curPos = 0; curPos < stringLength; ++curPos)
// Loop until we get to the end of the string OR stop once we've accumulated a full UUID string worth of data
for (size_t curPos = 0; curPos < stringLength && newLength < ValidUuidStringLength; ++curPos)
{
char curChar = uuidString[curPos];
switch (curChar)
+2 -1
View File
@@ -42,8 +42,9 @@ namespace AZ
//VER_AZ_RANDOM_CRC32 = 6, // 0 1 1 0
};
static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string
static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate
Uuid() {}
Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); }
@@ -180,6 +180,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector2& v) const;
//! @}
//! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector2.
//! @{
Vector2 GetFloor() const;
Vector2 GetCeil() const;
Vector2 GetRound() const; // Ties to even (banker's rounding)
//! @}
//! Min/Max functions, operate on each component individually, result will be a new Vector2.
//! @{
Vector2 GetMin(const Vector2& v) const;
@@ -398,6 +398,24 @@ namespace AZ
}
AZ_MATH_INLINE Vector2 Vector2::GetFloor() const
{
return Vector2(Simd::Vec2::Floor(m_value));
}
AZ_MATH_INLINE Vector2 Vector2::GetCeil() const
{
return Vector2(Simd::Vec2::Ceil(m_value));
}
AZ_MATH_INLINE Vector2 Vector2::GetRound() const
{
return Vector2(Simd::Vec2::Round(m_value));
}
AZ_MATH_INLINE Vector2 Vector2::GetMin(const Vector2& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
@@ -211,6 +211,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector3& rhs) const;
//! @}
//! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector3.
//! @{
Vector3 GetFloor() const;
Vector3 GetCeil() const;
Vector3 GetRound() const; // Ties to even (banker's rounding)
//! @}
//! Min/Max functions, operate on each component individually, result will be a new Vector3.
//! @{
Vector3 GetMin(const Vector3& v) const;
@@ -481,6 +481,24 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Vector3::GetFloor() const
{
return Vector3(Simd::Vec3::Floor(m_value));
}
AZ_MATH_INLINE Vector3 Vector3::GetCeil() const
{
return Vector3(Simd::Vec3::Ceil(m_value));
}
AZ_MATH_INLINE Vector3 Vector3::GetRound() const
{
return Vector3(Simd::Vec3::Round(m_value));
}
AZ_MATH_INLINE Vector3 Vector3::GetMin(const Vector3& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
@@ -189,6 +189,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector4& rhs) const;
//! @}
//! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector4.
//! @{
Vector4 GetFloor() const;
Vector4 GetCeil() const;
Vector4 GetRound() const; // Ties to even (banker's rounding)
//! @}
//! Min/Max functions, operate on each component individually, result will be a new Vector4.
//! @{
Vector4 GetMin(const Vector4& v) const;
@@ -464,6 +464,24 @@ namespace AZ
}
AZ_MATH_INLINE Vector4 Vector4::GetFloor() const
{
return Vector4(Simd::Vec4::Floor(m_value));
}
AZ_MATH_INLINE Vector4 Vector4::GetCeil() const
{
return Vector4(Simd::Vec4::Ceil(m_value));
}
AZ_MATH_INLINE Vector4 Vector4::GetRound() const
{
return Vector4(Simd::Vec4::Round(m_value));
}
AZ_MATH_INLINE Vector4 Vector4::GetMin(const Vector4& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
@@ -34,7 +34,9 @@ namespace AZ
friend IAllocator;
friend class AllocatorBase;
friend class Debug::AllocationRecords;
friend class AZ::Internal::EnvironmentVariableHolder<AllocatorManager>;
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
->AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
template<typename T> constexpr friend void AZStd::destroy_at(T*);
public:
typedef AZStd::function<void (IAllocator* allocator, size_t /*byteSize*/, size_t /*alignment*/, int/* flags*/, const char* /*name*/, const char* /*fileName*/, int lineNum /*=0*/)> OutOfMemoryCBType;
@@ -251,16 +251,15 @@ namespace AZ
class EnvironmentVariableHolder
: public EnvironmentVariableHolderBase
{
void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor<T> */)
{
memset(&m_value, 0, sizeof(T));
}
template<class... Args>
void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor<T> */, Args&&... args)
void ConstructImpl(Args&&... args)
{
// Construction of non-trivial types is left up to the type's constructor.
new(&m_value) T(AZStd::forward<Args>(args)...);
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
#if __cpp_lib_launder
AZStd::construct_at(std::launder(reinterpret_cast<T*>(&m_value)), AZStd::forward<Args>(args)...);
#else
AZStd::construct_at(reinterpret_cast<T*>(&m_value), AZStd::forward<Args>(args)...);
#endif
}
static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct)
{
@@ -274,10 +273,12 @@ namespace AZ
AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!");
self->m_isConstructed = false;
self->m_moduleOwner = nullptr;
if constexpr(!AZStd::is_trivially_destructible_v<T>)
{
reinterpret_cast<T*>(&self->m_value)->~T();
}
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
#if __cpp_lib_launder
AZStd::destroy_at(std::launder(reinterpret_cast<T*>(&self->m_value)));
#else
AZStd::destroy_at(reinterpret_cast<T*>(&self->m_value));
#endif
}
public:
EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator)
@@ -303,24 +304,13 @@ namespace AZ
UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease);
}
void Construct()
{
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
if (!m_isConstructed)
{
ConstructImpl(AZStd::is_trivially_constructible<T>{});
m_isConstructed = true;
m_moduleOwner = Environment::GetModuleId();
}
}
template <class... Args>
void Construct(Args&&... args)
{
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
if (!m_isConstructed)
{
ConstructImpl(typename AZStd::false_type(), AZStd::forward<Args>(args)...);
ConstructImpl(AZStd::forward<Args>(args)...);
m_isConstructed = true;
m_moduleOwner = Environment::GetModuleId();
}
@@ -333,7 +323,7 @@ namespace AZ
}
// variable storage
typename AZStd::aligned_storage<sizeof(T), AZStd::alignment_of<T>::value>::type m_value;
AZStd::aligned_storage_for_t<T> m_value;
static int s_moduleUseCount;
};
@@ -468,6 +458,11 @@ namespace AZ
Get() = value;
}
void Set(T&& value)
{
Get() = AZStd::move(value);
}
explicit operator bool() const
{
return IsValid();
@@ -42,7 +42,10 @@ namespace AZ
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
if (m_useCount.fetch_sub(1) == 1)
{
AZ::NameDictionary::Instance().TryReleaseName(hash);
if (AZ::NameDictionary::IsReady())
{
AZ::NameDictionary::Instance().TryReleaseName(hash);
}
}
}
}
@@ -21,23 +21,18 @@ namespace AZ
namespace NameDictionaryInternal
{
static AZ::EnvironmentVariable<NameDictionary*> s_instance = nullptr;
static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
}
void NameDictionary::Create()
{
using namespace NameDictionaryInternal;
AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!");
AZ_Assert(!s_instance, "NameDictionary already created!");
if (!s_instance)
{
s_instance = AZ::Environment::CreateVariable<NameDictionary*>(NameDictionaryInstanceName);
}
if (!s_instance.Get())
{
s_instance.Set(aznew NameDictionary());
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
}
}
@@ -46,8 +41,7 @@ namespace AZ
using namespace NameDictionaryInternal;
AZ_Assert(s_instance, "NameDictionary not created!");
delete (*s_instance);
*s_instance = nullptr;
s_instance.Reset();
}
bool NameDictionary::IsReady()
@@ -56,10 +50,10 @@ namespace AZ
if (!s_instance)
{
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
}
return s_instance && *s_instance;
return s_instance.IsConstructed();
}
NameDictionary& NameDictionary::Instance()
@@ -68,12 +62,12 @@ namespace AZ
if (!s_instance)
{
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
}
AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet.");
AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
return *(*s_instance);
return *s_instance;
}
NameDictionary::NameDictionary()
@@ -16,7 +16,7 @@
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Name/Name.h>
namespace MaterialEditor
namespace MaterialEditor
{
class MaterialEditorCoreComponent;
}
@@ -34,14 +34,14 @@ namespace AZ
{
class NameData;
};
//! Maintains a list of unique strings for Name objects.
//! The main benefit of the Name system is very fast string equality comparison, because every
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
//! collide. It also saves memory by removing duplicate strings.
//!
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
//! that already exist.
class NameDictionary final
{
@@ -51,7 +51,10 @@ namespace AZ
friend Name;
friend Internal::NameData;
friend UnitTest::NameDictionaryTester;
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
-> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
template<typename T> constexpr friend void AZStd::destroy_at(T*);
public:
static void Create();
@@ -62,7 +65,7 @@ namespace AZ
//! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
//! Otherwise, it is added to the internal dictionary.
//!
//!
//! @param name The name to resolve against the dictionary.
//! @return A Name instance holding a dictionary entry associated with the provided raw string.
Name MakeName(AZStd::string_view name);
@@ -84,13 +87,13 @@ namespace AZ
// Attempts to release the name from the dictionary, but checks to make sure
// a reference wasn't taken by another thread.
void TryReleaseName(Name::Hash hash);
//////////////////////////////////////////////////////////////////////////
// Calculates a hash for the provided name string.
// Does not attempt to resolve hash collisions; that is handled elsewhere.
Name::Hash CalcHash(AZStd::string_view name);
AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
mutable AZStd::shared_mutex m_sharedMutex;
};
@@ -204,14 +204,14 @@ namespace AZ
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0;
//! Register a function that will be called before a file is merged.
//! @callback The function to call before a file is merged.
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent (PreMergeEventCallback&& callback) = 0;
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0;
//! Register a function that will be called after a file is merged.
//! @callback The function to call after a file is merged.
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0;
//! Register a function that will be called after a file is merged.
//! @callback The function to call after a file is merged.
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent (PostMergeEventCallback&& callback) = 0;
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
//! Gets the boolean value at the provided path.
//! @param result The target to write the result to.
@@ -276,7 +276,9 @@ namespace AZ::SettingsRegistryMergeUtils
return engineRoot;
}
return {};
// Fall back to using the project root as the engine root if the engine path could not be reconciled
// by checking the project.json "engine" string within o3de_manifest.json "engine_paths" object
return projectRoot;
}
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
@@ -309,7 +311,13 @@ namespace AZ::SettingsRegistryMergeUtils
return projectRoot;
}
return {};
// 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()))
{
projectRoot = AZStd::move(candidateRoot);
}
return projectRoot;
}
AZStd::string_view ConfigParserSettings::DefaultCommentPrefixFilter(AZStd::string_view line)
@@ -538,7 +546,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
// Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
// Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
@@ -562,7 +570,7 @@ namespace AZ::SettingsRegistryMergeUtils
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
// Project path - corresponds to the @devassets@ alias
// 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;
@@ -654,7 +662,7 @@ namespace AZ::SettingsRegistryMergeUtils
}
else
{
// Cache: root - same as the @root@ alias, this is the starting path for cache files.
// 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;
@@ -0,0 +1,136 @@
/*
* 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 <AzCore/Settings/SettingsRegistryVisitorUtils.h>
namespace AZ::SettingsRegistryVisitorUtils
{
// Field Visitor implementation
FieldVisitor::FieldVisitor() = default;
FieldVisitor::FieldVisitor(VisitFieldType visitFieldType)
: m_visitFieldType{ visitFieldType }
{
}
auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) -> VisitResponse
{
// A default response skip prevents visiting grand children(depth 2 or lower)
VisitResponse visitResponse = VisitResponse::Skip;
if (action == VisitAction::Begin)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
// To make sure only the direct children are visited(depth 1)
// set the root path once and set the VisitReponsoe
// to Continue to recurse into is fields
if (!m_rootPath.has_value())
{
bool visitableFieldType{};
switch (m_visitFieldType)
{
case VisitFieldType::Array:
visitableFieldType = type == Type::Array;
break;
case VisitFieldType::Object:
visitableFieldType = type == Type::Object;
break;
case VisitFieldType::ArrayOrObject:
visitableFieldType = type == Type::Array || type ==Type::Object;
break;
default:
AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
break;
}
if (visitableFieldType)
{
m_rootPath = path;
visitResponse = VisitResponse::Continue;
}
}
}
else if (action == VisitAction::Value)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
}
else if (action == VisitAction::End)
{
// Reset m_rootPath back to null when the root path has finished being visited
if (m_rootPath.has_value() && *m_rootPath == path)
{
m_rootPath = AZStd::nullopt;
}
}
return visitResponse;
}
// Array Visitor implementation
ArrayVisitor::ArrayVisitor()
: FieldVisitor(VisitFieldType::Array)
{
}
// Object Visitor implementation
ObjectVisitor::ObjectVisitor()
: FieldVisitor(VisitFieldType::Object)
{
}
// Generic VisitField Callback implemention
template <typename BaseVisitor>
bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
struct VisitFieldVisitor
: BaseVisitor
{
using BaseVisitor::Visit;
VisitFieldVisitor(const VisitorCallback& visitCallback)
: m_visitCallback{ visitCallback }
{}
void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
{
m_visitCallback(path, fieldIndex, type);
}
const VisitorCallback& m_visitCallback;
};
VisitFieldVisitor visitor{ visitCallback };
return settingsRegistry.Visit(visitor, path);
}
// VisitField implementation
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<FieldVisitor>(settingsRegistry, visitCallback, path);
}
// VisitArray implementation
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ArrayVisitor>(settingsRegistry, visitCallback, path);
}
// VisitObject implementation
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ObjectVisitor>(settingsRegistry, visitCallback, path);
}
}
@@ -0,0 +1,83 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ::SettingsRegistryVisitorUtils
{
//! Interface for visiting the fields of an array or object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct FieldVisitor
: public AZ::SettingsRegistryInterface::Visitor
{
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
using Type = AZ::SettingsRegistryInterface::Type;
FieldVisitor();
// Bring the base class visitor functions into scope
using AZ::SettingsRegistryInterface::Visitor::Visit;
virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0;
protected:
// VisitFieldType is used for filtering the type of referenced by the root path
enum class VisitFieldType
{
Array,
Object,
ArrayOrObject
};
FieldVisitor(const VisitFieldType visitFieldType);
private:
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) override;
VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
};
//! Interface for visiting the fields of an array
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ArrayVisitor
: public FieldVisitor
{
ArrayVisitor();
};
//! Interface for visiting the fields of an object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ObjectVisitor
: public FieldVisitor
{
ObjectVisitor();
};
//! Signature of callback funcition invoked when visiting an element of an array or object
using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
AZ::SettingsRegistryInterface::Type)>;
//! Invokes the visitor callback for each element of either the array or object at @path
//! If @path is not an array or object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array or object element found
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the array at @path
//! If @path is not an array, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array element found
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the object at @path
//! If @path is not an object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each object element found
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
}
@@ -1,106 +0,0 @@
/*
* 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 "RunningStatisticsManager.h"
namespace AzFramework
{
namespace Statistics
{
bool RunningStatisticsManager::ContainsStatistic(const AZStd::string& name)
{
auto iterator = m_statisticsNamesToIndexMap.find(name);
return iterator != m_statisticsNamesToIndexMap.end();
}
bool RunningStatisticsManager::AddStatistic(const AZStd::string& name, const AZStd::string& units)
{
if (ContainsStatistic(name))
{
return false;
}
AddStatisticValidated(name, units);
return true;
}
void RunningStatisticsManager::RemoveStatistic(const AZStd::string& name)
{
auto iterator = m_statisticsNamesToIndexMap.find(name);
if (iterator == m_statisticsNamesToIndexMap.end())
{
return;
}
AZ::u32 itemIndex = iterator->second;
m_statistics.erase(m_statistics.begin() + itemIndex);
m_statisticsNamesToIndexMap.erase(iterator);
//Update the indices in m_statisticsNamesToIndexMap.
while (itemIndex < m_statistics.size())
{
const AZStd::string& statName = m_statistics[itemIndex].GetName();
m_statisticsNamesToIndexMap[statName] = itemIndex;
++itemIndex;
}
}
void RunningStatisticsManager::ResetStatistic(const AZStd::string& name)
{
NamedRunningStatistic* stat = GetStatistic(name);
if (!stat)
{
return;
}
stat->Reset();
}
void RunningStatisticsManager::ResetAllStatistics()
{
for (NamedRunningStatistic& stat : m_statistics)
{
stat.Reset();
}
}
void RunningStatisticsManager::PushSampleForStatistic(const AZStd::string& name, double value)
{
NamedRunningStatistic* stat = GetStatistic(name);
if (!stat)
{
return;
}
stat->PushSample(value);
}
NamedRunningStatistic* RunningStatisticsManager::GetStatistic(const AZStd::string& name, AZ::u32* indexOut)
{
auto iterator = m_statisticsNamesToIndexMap.find(name);
if (iterator == m_statisticsNamesToIndexMap.end())
{
return nullptr;
}
const AZ::u32 index = iterator->second;
if (indexOut)
{
*indexOut = index;
}
return &m_statistics[index];
}
const AZStd::vector<NamedRunningStatistic>& RunningStatisticsManager::GetAllStatistics() const
{
return m_statistics;
}
void RunningStatisticsManager::AddStatisticValidated(const AZStd::string& name, const AZStd::string& units)
{
m_statistics.emplace_back(NamedRunningStatistic(name, units));
const AZ::u32 itemIndex = static_cast<AZ::u32>(m_statistics.size() - 1);
m_statisticsNamesToIndexMap[name] = itemIndex;
}
}//namespace Statistics
}//namespace AzFramework
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/EBus/BusImpl.h> //Just to get AZ::NullMutex
#include <AzCore/std/chrono/types.h>
#include <AzCore/Statistics/StatisticsManager.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/parallel/scoped_lock.h>
@@ -37,8 +36,7 @@ namespace AZ
//! are some things to consider when working with the StatisticalProfilerProxy:
//! The StatisticalProfilerProxy OWNS an array of StatisticalProfiler<AZStd::string, AZStd::shared_spin_mutex>.
//! You can "manage" one of those StatisticalProfiler by getting a reference to it and
//! add Running statistics etc. See The TerrainProfilers mentioned above to see concrete use
//! cases on how to work with the StatisticalProfilerProxy.
//! add Running statistics etc.
template <class StatIdType = AZStd::string, class MutexType = AZ::NullMutex>
class StatisticalProfiler
{
@@ -7,28 +7,12 @@
*/
#pragma once
#include <AzCore/std/chrono/types.h>
#include <AzCore/std/parallel/shared_spin_mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/std/containers/bitset.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Statistics/StatisticalProfiler.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/shared_spin_mutex.h>
#if defined(AZ_STATISTICAL_PROFILING_ENABLED)
#if defined(AZ_PROFILE_SCOPE)
#undef AZ_PROFILE_SCOPE
#endif // #if defined(AZ_PROFILE_SCOPE)
#define AZ_PROFILE_SCOPE(profiler, scopeNameId) \
static const AZStd::string AZ_JOIN(blockName, __LINE__)(scopeNameId); \
AZ::Statistics::StatisticalProfilerProxy::TimedScope AZ_JOIN(scope, __LINE__)(profiler, AZ_JOIN(blockName, __LINE__));
#endif //#if defined(AZ_STATISTICAL_PROFILING_ENABLED)
namespace AZ::Statistics
{
using StatisticalProfilerId = uint32_t;
@@ -65,7 +49,7 @@ namespace AZ::Statistics
public:
AZ_TYPE_INFO(StatisticalProfilerProxy, "{1103D0EB-1C32-4854-B9D9-40A2D65BDBD2}");
using StatIdType = AZStd::string;
using StatIdType = AZ::Crc32;
using StatisticalProfilerType = StatisticalProfiler<StatIdType, AZStd::shared_spin_mutex>;
//! A Convenience class used to measure time performance of scopes of code
@@ -94,6 +78,7 @@ namespace AZ::Statistics
}
m_startTime = AZStd::chrono::high_resolution_clock::now();
}
~TimedScope()
{
if (!m_profilerProxy)
@@ -122,7 +107,6 @@ namespace AZ::Statistics
StatisticalProfilerProxy()
{
// TODO:BUDGETS Query available budgets at registration time and create an associated profiler per type
AZ::Interface<StatisticalProfilerProxy>::Register(this);
}
@@ -135,30 +119,54 @@ namespace AZ::Statistics
StatisticalProfilerProxy(StatisticalProfilerProxy&&) = delete;
StatisticalProfilerProxy& operator=(StatisticalProfilerProxy&&) = delete;
void RegisterProfilerId(StatisticalProfilerId id)
{
m_profilers.try_emplace(id, ProfilerInfo());
}
bool IsProfilerActive(StatisticalProfilerId id) const
{
return m_activeProfilersFlag[static_cast<AZStd::size_t>(id)];
auto iter = m_profilers.find(id);
return (iter != m_profilers.end()) ? iter->second.m_enabled : false;
}
StatisticalProfilerType& GetProfiler(StatisticalProfilerId id)
{
return m_profilers[static_cast<AZStd::size_t>(id)];
auto iter = m_profilers.try_emplace(id, ProfilerInfo()).first;
return iter->second.m_profiler;
}
void ActivateProfiler(StatisticalProfilerId id, bool activate)
void ActivateProfiler(StatisticalProfilerId id, bool activate, bool autoCreate = true)
{
m_activeProfilersFlag[static_cast<AZStd::size_t>(id)] = activate;
if (autoCreate)
{
auto iter = m_profilers.try_emplace(id, ProfilerInfo()).first;
iter->second.m_enabled = activate;
}
else if (auto iter = m_profilers.find(id); iter != m_profilers.end())
{
iter->second.m_enabled = activate;
}
}
void PushSample(StatisticalProfilerId id, const StatIdType& statId, double value)
{
m_profilers[static_cast<AZStd::size_t>(id)].PushSample(statId, value);
if (auto iter = m_profilers.find(id); iter != m_profilers.end())
{
iter->second.m_profiler.PushSample(statId, value);
}
}
private:
// TODO:BUDGETS the number of bits allocated here must be based on the number of budgets available at profiler registration time
AZStd::bitset<128> m_activeProfilersFlag;
AZStd::vector<StatisticalProfilerType> m_profilers;
struct ProfilerInfo
{
StatisticalProfilerType m_profiler;
bool m_enabled{ false };
};
using ProfilerMap = AZStd::unordered_map<StatisticalProfilerId, ProfilerInfo>;
ProfilerMap m_profilers;
}; // class StatisticalProfilerProxy
}; // namespace AZ::Statistics
@@ -308,11 +308,11 @@ namespace AZ
void TaskExecutor::SetInstance(TaskExecutor* executor)
{
if (!executor)
if (!executor) // allow unsetting the executor
{
s_executor.Reset();
}
else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
else if (!s_executor) // ignore any extra executors after the first (this happens during unit tests)
{
s_executor = AZ::Environment::CreateVariable<TaskExecutor*>(s_executorName, executor);
}
@@ -11,9 +11,15 @@
#include <AzCore/Task/TaskGraphSystemComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Threading/ThreadUtils.h>
// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
AZ_CVAR(float, cl_taskGraphThreadsConcurrencyRatio, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph calculate the number of worker threads to spawn by scaling the number of hw threads, value is clamped between 0.0f and 1.0f");
AZ_CVAR(uint32_t, cl_taskGraphThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph number of hardware threads that are reserved for O3DE system threads. Value is clamped between 0 and the number of logical cores in the system");
AZ_CVAR(uint32_t, cl_taskGraphThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph minimum number of worker threads to create after scaling the number of hw threads");
static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
namespace AZ
@@ -24,8 +30,8 @@ namespace AZ
if (Interface<TaskGraphActiveInterface>::Get() == nullptr)
{
Interface<TaskGraphActiveInterface>::Register(this);
m_taskExecutor = aznew TaskExecutor();
Interface<TaskGraphActiveInterface>::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance.
m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved));
TaskExecutor::SetInstance(m_taskExecutor);
}
}
@@ -0,0 +1,25 @@
/*
* 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 <AzCore/Threading/ThreadUtils.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
namespace AZ::Threading
{
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads)
{
const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency();
const uint32_t numReservedThreads = AZ::GetMin<uint32_t>(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads
const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads;
const float requestedWorkerThreads = AZ::GetClamp<float>(workerThreadsRatio, 0.0f, 1.0f) * static_cast<float>(maxWorkerThreads);
const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
const uint32_t numWorkerThreads = AZ::GetMax<uint32_t>(minNumWorkerThreads, requestedWorkerThreadsRounded);
return numWorkerThreads;
}
};
@@ -0,0 +1,22 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/base.h>
namespace AZ::Threading
{
//! Calculates the number of worker threads a system should use based on the number of hardware threads a device has.
//! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads))
//! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1.
//! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads.
//! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads.
//! @return number of worker threads for the calling system to allocate
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads);
};
+64 -1
View File
@@ -13,14 +13,26 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/time.h>
#include <AzCore/std/chrono/chrono.h>
namespace AZ
{
//! This is a strong typedef for representing a millisecond value since application start.
AZ_TYPE_SAFE_INTEGRAL(TimeMs, int64_t);
//! This is a strong typedef for representing a microsecond value since application start.
//! Using int64_t as the underlying type, this is good to represent approximately 292,471 years
AZ_TYPE_SAFE_INTEGRAL(TimeUs, int64_t);
//! @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
class ITime
{
public:
@@ -33,6 +45,10 @@ namespace AZ
//! @return the number of milliseconds that have elapsed since application start
virtual TimeMs GetElapsedTimeMs() const = 0;
//! Returns the number of microseconds since application start.
//! @return the number of microseconds that have elapsed since application start
virtual TimeUs GetElapsedTimeUs() const = 0;
AZ_DISABLE_COPY_MOVE(ITime);
};
@@ -51,6 +67,53 @@ namespace AZ
{
return AZ::Interface<ITime>::Get()->GetElapsedTimeMs();
}
}
//! This is a simple convenience wrapper
inline TimeUs GetElapsedTimeUs()
{
return AZ::Interface<ITime>::Get()->GetElapsedTimeUs();
}
//! Converts from milliseconds to microseconds
inline TimeUs TimeMsToUs(TimeMs value)
{
return static_cast<TimeUs>(value * static_cast<TimeMs>(1000));
}
//! Converts from microseconds to milliseconds
inline TimeMs TimeUsToMs(TimeUs value)
{
return static_cast<TimeMs>(value / static_cast<TimeUs>(1000));
}
//! Converts from milliseconds to seconds
inline float TimeMsToSeconds(TimeMs value)
{
return static_cast<float>(value) / 1000.0f;
}
//! Converts from microseconds to seconds
inline float TimeUsToSeconds(TimeUs value)
{
return static_cast<float>(value) / 1000000.0f;
}
//! Converts from milliseconds to AZStd::chrono::time_point
inline auto TimeMsToChrono(TimeMs value)
{
auto epoch = AZStd::chrono::time_point<AZStd::chrono::high_resolution_clock>();
auto chronoValue = AZStd::chrono::milliseconds(aznumeric_cast<int64_t>(value));
return epoch + chronoValue;
}
//! Converts from microseconds to AZStd::chrono::time_point
inline auto TimeUsToChrono(TimeUs value)
{
auto epoch = AZStd::chrono::time_point<AZStd::chrono::high_resolution_clock>();
auto chronoValue = AZStd::chrono::microseconds(aznumeric_cast<int64_t>(value));
return epoch + chronoValue;
}
} // namespace AZ
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeMs);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeUs);
@@ -35,7 +35,7 @@ namespace AZ
TimeSystemComponent::TimeSystemComponent()
{
m_lastInvokedTimeMs = static_cast<TimeMs>(AZStd::GetTimeNowMicroSecond() / 1000);
m_lastInvokedTimeUs = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
AZ::Interface<ITime>::Register(this);
ITimeRequestBus::Handler::BusConnect();
}
@@ -58,18 +58,23 @@ namespace AZ
TimeMs TimeSystemComponent::GetElapsedTimeMs() const
{
TimeMs currentTime = static_cast<TimeMs>(AZStd::GetTimeNowMicroSecond() / 1000);
TimeMs deltaTime = currentTime - m_lastInvokedTimeMs;
return TimeUsToMs(GetElapsedTimeUs());
}
TimeUs TimeSystemComponent::GetElapsedTimeUs() const
{
TimeUs currentTime = static_cast<TimeUs>(AZStd::GetTimeNowMicroSecond());
TimeUs deltaTime = currentTime - m_lastInvokedTimeUs;
if (t_scale != 1.0f)
{
float floatDelta = static_cast<float>(deltaTime) * t_scale;
deltaTime = static_cast<TimeMs>(static_cast<int64_t>(floatDelta));
deltaTime = static_cast<TimeUs>(static_cast<int64_t>(floatDelta));
}
m_accumulatedTimeMs += deltaTime;
m_lastInvokedTimeMs = currentTime;
m_accumulatedTimeUs += deltaTime;
m_lastInvokedTimeUs = currentTime;
return m_accumulatedTimeMs;
return m_accumulatedTimeUs;
}
}
@@ -39,11 +39,12 @@ namespace AZ
//! ITime overrides.
//! @{
TimeMs GetElapsedTimeMs() const override;
TimeUs GetElapsedTimeUs() const override;
//! @}
private:
mutable TimeMs m_lastInvokedTimeMs = TimeMs{0};
mutable TimeMs m_accumulatedTimeMs = TimeMs{0};
mutable TimeUs m_lastInvokedTimeUs = TimeUs{0};
mutable TimeUs m_accumulatedTimeUs = TimeUs{0};
};
}
@@ -52,6 +52,7 @@ namespace AZ
MOCK_METHOD2(SetAlias, void(const char* alias, const char* path));
MOCK_METHOD1(ClearAlias, void(const char* alias));
MOCK_CONST_METHOD1(GetAlias, const char*(const char* alias));
MOCK_METHOD2(SetDeprecatedAlias, void(AZStd::string_view, AZStd::string_view));
MOCK_CONST_METHOD2(ConvertToAlias, AZStd::optional<AZ::u64>(char* inOutBuffer, AZ::u64 bufferLength));
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));
@@ -51,6 +51,20 @@ namespace AZ::Utils
return executableDirectory;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (ConvertToAbsolutePath(srcPath.c_str(), absolutePath.data(), absolutePath.capacity()))
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return srcPath;
}
return AZStd::nullopt;
}
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory();
@@ -104,6 +104,7 @@ namespace AZ
// Attempts the supplied path to an absolute path.
//! Returns nullopt if path cannot be converted to an absolute path
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path);
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 absolutePathMaxSize);
//! 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);
@@ -566,6 +566,8 @@ set(FILES
Settings/SettingsRegistryMergeUtils.h
Settings/SettingsRegistryScriptUtils.cpp
Settings/SettingsRegistryScriptUtils.h
Settings/SettingsRegistryVisitorUtils.cpp
Settings/SettingsRegistryVisitorUtils.h
State/HSM.cpp
State/HSM.h
Statistics/NamedRunningStatistic.h
@@ -639,6 +641,8 @@ set(FILES
Threading/ThreadSafeDeque.inl
Threading/ThreadSafeObject.h
Threading/ThreadSafeObject.inl
Threading/ThreadUtils.h
Threading/ThreadUtils.cpp
Time/ITime.h
Time/TimeSystemComponent.cpp
Time/TimeSystemComponent.h
@@ -0,0 +1,94 @@
/*
* 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 <AzCore/std/allocator_stateless.h>
#include <AzCore/Memory/OSAllocator.h>
namespace AZStd
{
stateless_allocator::stateless_allocator(const char* name)
: m_name(name) {}
const char* stateless_allocator::get_name() const
{
return m_name;
}
void stateless_allocator::set_name(const char* name)
{
m_name = name;
}
auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
{
return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0);
}
auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type
{
pointer_type address = AZ_OS_MALLOC(byteSize, alignment);
if (address == nullptr)
{
AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n");
}
return address;
}
void stateless_allocator::deallocate(pointer_type ptr, size_type)
{
AZ_OS_FREE(ptr);
}
void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type)
{
AZ_OS_FREE(ptr);
}
auto stateless_allocator::max_size() const -> size_type
{
return AZ_CORE_MAX_ALLOCATOR_SIZE;
}
stateless_allocator stateless_allocator::select_on_container_copy_construction() const
{
return *this;
}
auto stateless_allocator::resize(pointer_type, size_type) -> size_type
{
return 0;
}
bool stateless_allocator::is_lock_free()
{
return false;
}
bool stateless_allocator::is_stale_read_allowed()
{
return false;
}
bool stateless_allocator::is_delayed_recycling()
{
return false;
}
// comparison operators
bool operator==(const stateless_allocator&, const stateless_allocator&)
{
return true;
}
bool operator!=(const stateless_allocator&, const stateless_allocator&)
{
return false;
}
}
@@ -0,0 +1,61 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/std/base.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
namespace AZStd
{
class stateless_allocator
{
public:
AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}");
using value_type = uint8_t;
using pointer_type = void*;
using size_type = size_t;
using difference_type = ptrdiff_t;
using allow_memory_leaks = AZStd::true_type;
stateless_allocator(const char* name = "AZStd::stateless_allocator");
stateless_allocator(const stateless_allocator& rhs) = default;
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
const char* get_name() const;
void set_name(const char* name);
pointer_type allocate(size_type byteSize);
pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0);
void deallocate(pointer_type ptr, size_type alignment);
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment);
// max_size actually returns the true maximum size of a single allocation
size_type max_size() const;
// Returns a copy of the allocator
stateless_allocator select_on_container_copy_construction() const;
//! extensions
size_type resize(pointer_type ptr, size_type newSize);
bool is_lock_free();
bool is_stale_read_allowed();
bool is_delayed_recycling();
private:
const char* m_name;
};
bool operator==(const stateless_allocator& left, const stateless_allocator& right);
bool operator!=(const stateless_allocator& left, const stateless_allocator& right);
}
@@ -12,6 +12,8 @@ set(FILES
allocator.h
allocator_ref.h
allocator_stack.h
allocator_stateless.cpp
allocator_stateless.h
allocator_static.h
allocator_traits.h
any.h
@@ -20,7 +20,7 @@
namespace AZStd
{
// alias std::pointer_traits into the AZStd::namespace
// alias std::pointer_traits into the AZStd::namespace
using std::pointer_traits;
//! Bring the names of uninitialized_default_construct and
@@ -229,7 +229,7 @@ namespace AZStd
//! `new (declval<void*>()) T(declval<Args>()...)` is well-formed
template <typename T, typename... Args>
constexpr auto construct_at(T* ptr, Args&&... args)
-> enable_if_t<is_void_v<void_t<decltype(new (declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
-> enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
{
return ::new (ptr) T(AZStd::forward<Args>(args)...);
}
@@ -487,7 +487,7 @@ namespace AZStd
{
//! Implements the C++17 uninitialized_move function
//! The functions accepts two input iterators and an output iterator
//! It performs an AZStd::move on each in in the range of the input iterator
//! It performs an AZStd::move on each in in the range of the input iterator
//! and stores the result in location pointed by the output iterator
template <typename InputIt, typename ForwardIt>
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
+45
View File
@@ -30,4 +30,49 @@ namespace AZStd
using std::sqrt;
using std::tan;
using std::trunc;
} // namespace AZStd
// from c++20 standard
namespace AZStd::Internal
{
template<typename T>
constexpr T lerp(T a, T b, T t) noexcept
{
if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0))
{
return t * b + (1 - t) * a;
}
if (t == 1)
{
return b;
}
const T x = a + t * (b - a);
if ((t > 1) == (b > a))
{
return b < x ? x : b;
}
else
{
return x < b ? x : b;
}
}
} // namespace AZStd::Internal
namespace AZStd
{
constexpr float lerp(float a, float b, float t) noexcept
{
return Internal::lerp(a, b, t);
}
constexpr double lerp(double a, double b, double t) noexcept
{
return Internal::lerp(a, b, t);
}
constexpr long double lerp(long double a, long double b, long double t) noexcept
{
return Internal::lerp(a, b, t);
}
} // namespace AZStd
+1 -1
View File
@@ -39,7 +39,7 @@ ly_add_target(
3rdParty::Lua
3rdParty::RapidJSON
3rdParty::RapidXML
3rdParty::zlib
3rdParty::ZLIB
3rdParty::zstd
3rdParty::cityhash
${AZ_CORE_PIX_BUILD_DEPENDENCIES}
@@ -368,6 +368,21 @@ namespace Platform
return access(fileName, F_OK) == 0;
}
}
bool IsDirectory(const char* filePath)
{
if (AZ::Android::Utils::IsApkPath(filePath))
{
return AZ::Android::APKFileHandler::IsDirectory(AZ::Android::Utils::StripApkPrefix(filePath).c_str());
}
struct stat result;
if (stat(filePath, &result) == 0)
{
return S_ISDIR(result.st_mode);
}
return false;
}
} // namespace AZ::IO::Platform
} // namespace AZ::IO
@@ -60,23 +60,34 @@ namespace AZ
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
if (AZ::Android::Utils::IsApkPath(path))
{
return srcPath;
azstrcpy(absolutePath, maxLength, path);
return true;
}
if(char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
#ifdef PATH_MAX
static constexpr size_t UnixMaxPathLength = PATH_MAX;
#else
// Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
static constexpr size_t UnixMaxPathLength = 4096;
#endif
if (!AZ::IO::PathView(path).IsAbsolute())
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return absolutePath;
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
char absolutePathBuffer[UnixMaxPathLength];
if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
{
azstrcpy(absolutePath, maxLength, absolutePathBuffer);
return true;
}
}
return AZStd::nullopt;
azstrcpy(absolutePath, maxLength, path);
return AZ::IO::PathView(absolutePath).IsAbsolute();
}
}
}
@@ -38,7 +38,7 @@ namespace AZ
{
return false;
}
for (size_t i = tracerPidOffset; i < numRead; ++i)
for (size_t i = tracerPidOffset + tracerPidString.length(); i < numRead; ++i)
{
if (!::isspace(processStatusView[i]))
{
@@ -47,23 +47,32 @@ namespace AZ
AZ::IO::FixedMaxPath path{pass->pw_dir};
return path.Native();
}
return {};
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
#ifdef PATH_MAX
static constexpr size_t UnixMaxPathLength = PATH_MAX;
#else
// Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
static constexpr size_t UnixMaxPathLength = 4096;
#endif
if (!AZ::IO::PathView(path).IsAbsolute())
{
// Fix the size value of the fixed string by calculating the c-string length using char traits
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
return absolutePath;
// note that realpath fails if the path does not exist and actually changes the return value
// to be the actual place that FAILED, which we don't want.
// if we fail, we'd prefer to fall through and at least use the original path.
char absolutePathBuffer[UnixMaxPathLength];
if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
{
azstrcpy(absolutePath, maxLength, absolutePathBuffer);
return true;
}
}
return AZStd::nullopt;
azstrcpy(absolutePath, maxLength, path);
return AZ::IO::PathView(absolutePath).IsAbsolute();
}
} // namespace Utils
} // namespace AZ
@@ -59,6 +59,10 @@ namespace AZStd
{
priority = desc->m_priority;
}
else
{
priority = SCHED_OTHER;
}
if (desc->m_name)
{
name = desc->m_name;
@@ -249,6 +249,16 @@ namespace Platform
{
return access(fileName, F_OK) == 0;
}
bool IsDirectory(const char* filePath)
{
struct stat result;
if (stat(filePath, &result) == 0)
{
return S_ISDIR(result.st_mode);
}
return false;
}
}
} // namespace AZ::IO
@@ -10,6 +10,7 @@
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/PlatformIncl.h>
#include <AzCore/Utils/Utils.h>
@@ -18,7 +19,7 @@
namespace AZ::IO
{
using FixedMaxPathWString = AZStd::fixed_wstring<MaxPathLength>;
namespace
{
//=========================================================================
@@ -28,16 +29,9 @@ namespace
//=========================================================================
DWORD GetAttributes(const char* fileName)
{
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, fileNameW, fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
return GetFileAttributesW(fileNameW);
}
else
{
return INVALID_FILE_ATTRIBUTES;
}
FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, fileName);
return GetFileAttributesW(fileNameW.c_str());
}
//=========================================================================
@@ -47,16 +41,9 @@ namespace
//=========================================================================
BOOL SetAttributes(const char* fileName, DWORD fileAttributes)
{
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, fileNameW, fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
return SetFileAttributesW(fileNameW, fileAttributes);
}
else
{
return FALSE;
}
FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, fileName);
return SetFileAttributesW(fileNameW.c_str(), fileAttributes);
}
//=========================================================================
@@ -68,9 +55,9 @@ namespace
// * GetLastError() on Windows-like platforms
// * errno on Unix platforms
//=========================================================================
bool CreateDirRecursive(wchar_t* dirPath)
bool CreateDirRecursive(AZ::IO::FixedMaxPathWString& dirPath)
{
if (CreateDirectoryW(dirPath, nullptr))
if (CreateDirectoryW(dirPath.c_str(), nullptr))
{
return true; // Created without error
}
@@ -78,28 +65,24 @@ namespace
if (error == ERROR_PATH_NOT_FOUND)
{
// try to create our parent hierarchy
for (size_t i = wcslen(dirPath); i > 0; --i)
if (size_t i = dirPath.find_last_of(LR"(/\)"); i != FixedMaxPathWString::npos)
{
if (dirPath[i] == L'/' || dirPath[i] == L'\\')
wchar_t delimiter = dirPath[i];
dirPath[i] = 0; // null-terminate at the previous slash
const bool ret = CreateDirRecursive(dirPath);
dirPath[i] = delimiter; // restore slash
if (ret)
{
wchar_t delimiter = dirPath[i];
dirPath[i] = 0; // null-terminate at the previous slash
bool ret = CreateDirRecursive(dirPath);
dirPath[i] = delimiter; // restore slash
if (ret)
{
// now that our parent is created, try to create again
return CreateDirectoryW(dirPath, nullptr) != 0;
}
return false;
// now that our parent is created, try to create again
return CreateDirectoryW(dirPath.c_str(), nullptr) != 0;
}
}
// if we reach here then there was no parent folder to create, so we failed for other reasons
}
else if (error == ERROR_ALREADY_EXISTS)
{
DWORD attributes = GetFileAttributesW(dirPath);
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
DWORD attributes = GetFileAttributesW(dirPath.c_str());
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
return false;
}
@@ -152,13 +135,10 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
CreatePath(m_fileName.c_str());
}
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
AZ::IO::FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, m_fileName);
m_handle = INVALID_HANDLE_VALUE;
if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName.c_str(), AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
m_handle = CreateFileW(fileNameW, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
}
m_handle = CreateFileW(fileNameW.c_str(), dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
if (m_handle == INVALID_HANDLE_VALUE)
{
@@ -350,6 +330,12 @@ namespace Platform
return GetAttributes(fileName) != INVALID_FILE_ATTRIBUTES;
}
bool IsDirectory(const char* filePath)
{
DWORD attributes = GetAttributes(filePath);
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
void FindFiles(const char* filter, SystemFile::FindFileCB cb)
{
@@ -357,35 +343,26 @@ namespace Platform
HANDLE hFile;
int lastError;
wchar_t filterW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
AZ::IO::FixedMaxPathWString filterW;
AZStd::to_wstring(filterW, filter);
hFile = INVALID_HANDLE_VALUE;
if (mbstowcs_s(&numCharsConverted, filterW, filter, AZ_ARRAY_SIZE(filterW) - 1) == 0)
{
hFile = FindFirstFile(filterW, &fd);
}
hFile = FindFirstFileW(filterW.c_str(), &fd);
if (hFile != INVALID_HANDLE_VALUE)
{
const char* fileName;
char fileNameA[AZ_MAX_PATH_LEN];
fileName = NULL;
if (wcstombs_s(&numCharsConverted, fileNameA, fd.cFileName, AZ_ARRAY_SIZE(fileNameA) - 1) == 0)
{
fileName = fileNameA;
}
AZ::IO::FixedMaxPathString fileNameUtf8;
AZStd::to_string(fileNameUtf8, fd.cFileName);
fileName = fileNameUtf8.c_str();
cb(fileName, (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
// List all the other files in the directory.
while (FindNextFileW(hFile, &fd) != 0)
{
fileName = NULL;
if (wcstombs_s(&numCharsConverted, fileNameA, fd.cFileName, AZ_ARRAY_SIZE(fileNameA) - 1) == 0)
{
fileName = fileNameA;
}
AZStd::to_string(fileNameUtf8, fd.cFileName);
fileName = fileNameUtf8.c_str();
cb(fileName, (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
}
@@ -411,12 +388,9 @@ namespace Platform
{
HANDLE handle = nullptr;
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, fileNameW, fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
handle = CreateFileW(fileNameW, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
}
AZ::IO::FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, fileName);
handle = CreateFileW(fileNameW.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
{
@@ -448,12 +422,9 @@ namespace Platform
WIN32_FILE_ATTRIBUTE_DATA data = { 0 };
BOOL result = FALSE;
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, fileNameW, fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
result = GetFileAttributesExW(fileNameW, GetFileExInfoStandard, &data);
}
AZ::IO::FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, fileName);
result = GetFileAttributesExW(fileNameW.c_str(), GetFileExInfoStandard, &data);
if (result)
{
@@ -473,18 +444,11 @@ namespace Platform
bool Delete(const char* fileName)
{
wchar_t fileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, fileNameW, fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
{
if (DeleteFileW(fileNameW) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
return false;
}
}
else
AZ::IO::FixedMaxPathWString fileNameW;
AZStd::to_wstring(fileNameW, fileName);
if (DeleteFileW(fileNameW.c_str()) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
return false;
}
@@ -493,20 +457,13 @@ namespace Platform
bool Rename(const char* sourceFileName, const char* targetFileName, bool overwrite)
{
wchar_t sourceFileNameW[AZ_MAX_PATH_LEN];
wchar_t targetFileNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, sourceFileNameW, sourceFileName, AZ_ARRAY_SIZE(sourceFileNameW) - 1) == 0 &&
mbstowcs_s(&numCharsConverted, targetFileNameW, targetFileName, AZ_ARRAY_SIZE(targetFileNameW) - 1) == 0)
{
if (MoveFileExW(sourceFileNameW, targetFileNameW, overwrite ? MOVEFILE_REPLACE_EXISTING : 0) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, sourceFileName, (int)GetLastError());
return false;
}
}
else
AZ::IO::FixedMaxPathWString sourceFileNameW;
AZStd::to_wstring(sourceFileNameW, sourceFileName);
AZ::IO::FixedMaxPathWString targetFileNameW;
AZStd::to_wstring(targetFileNameW, targetFileName);
if (MoveFileExW(sourceFileNameW.c_str(), targetFileNameW.c_str(), overwrite ? MOVEFILE_REPLACE_EXISTING : 0) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, sourceFileName, (int)GetLastError());
return false;
}
@@ -543,17 +500,14 @@ namespace Platform
{
if (dirName)
{
wchar_t dirPath[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, dirPath, dirName, AZ_ARRAY_SIZE(dirPath) - 1) == 0)
AZ::IO::FixedMaxPathWString dirNameW;
AZStd::to_wstring(dirNameW, dirName);
bool success = CreateDirRecursive(dirNameW);
if (!success)
{
bool success = CreateDirRecursive(dirPath);
if (!success)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, dirName, (int)GetLastError());
}
return success;
EBUS_EVENT(FileIOEventBus, OnError, nullptr, dirName, (int)GetLastError());
}
return success;
}
return false;
}
@@ -562,12 +516,9 @@ namespace Platform
{
if (dirName)
{
wchar_t dirNameW[AZ_MAX_PATH_LEN];
size_t numCharsConverted;
if (mbstowcs_s(&numCharsConverted, dirNameW, dirName, AZ_ARRAY_SIZE(dirNameW) - 1) == 0)
{
return RemoveDirectory(dirNameW) != 0;
}
AZ::IO::FixedMaxPathWString dirNameW;
AZStd::to_wstring(dirNameW, dirName);
return RemoveDirectory(dirNameW.c_str()) != 0;
}
return false;
@@ -67,19 +67,12 @@ namespace AZ
return AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
{
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()));
if (result)
{
return absolutePath;
}
return AZStd::nullopt;
char* result = _fullpath(absolutePath, path, maxLength);
return result != nullptr;
}
}
}
@@ -0,0 +1,46 @@
/*
* 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 <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/math.h>
namespace UnitTest
{
template<typename T>
class StdMathTest : public ::testing::Test
{
};
using MathTestConfigs = ::testing::Types<float, double, long double>;
TYPED_TEST_CASE(StdMathTest, MathTestConfigs);
TYPED_TEST(StdMathTest, LerpOperations)
{
using AZStd::lerp;
using ::testing::Eq;
using T = TypeParam;
constexpr T maxNumber = AZStd::numeric_limits<T>::max();
constexpr T eps = AZStd::numeric_limits<T>::epsilon();
constexpr T a{ 42 };
// exactness: lerp(a,b,0)==a && lerp(a,b,1)==b
EXPECT_THAT(lerp(eps, maxNumber, T(0)), Eq(eps));
EXPECT_THAT(lerp(eps, maxNumber, T(1)), Eq(maxNumber));
// consistency: lerp(a,a,t)==a
EXPECT_THAT(lerp(a, a, T(0.5)), Eq(a));
EXPECT_THAT(lerp(eps, eps, T(0.5)), Eq(eps));
// a few generic tests taken from MathUtilTests.cpp
EXPECT_EQ(T(2.5), lerp(T(2), T(4), T(0.25)));
EXPECT_EQ(T(6.0), lerp(T(2), T(4), T(2.0)));
EXPECT_EQ(T(3.5), lerp(T(2), T(4), T(0.75)));
EXPECT_EQ(T(0.0), lerp(T(2), T(4), T(-1.0)));
}
} // namespace UnitTest
@@ -64,6 +64,91 @@ namespace JsonSerializationTests
};
class TestSerializedAssetTracker
: public BaseJsonSerializerFixture
{
public:
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
// Set up the Job Manager with 1 thread so that the Asset Manager is able to load assets.
AZ::JobManagerDesc jobDesc;
AZ::JobManagerThreadDesc threadDesc;
jobDesc.m_workerThreads.push_back(threadDesc);
m_jobManager = aznew AZ::JobManager(jobDesc);
m_jobContext = aznew AZ::JobContext(*m_jobManager);
AZ::JobContext::SetGlobalContext(m_jobContext);
AZ::Data::AssetManager::Descriptor descriptor;
AZ::Data::AssetManager::Create(descriptor);
AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, azrtti_typeid<TestAssetData>());
m_serializeContext->RegisterGenericType<AZ::Data::Asset<TestAssetData>>();
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
}
void TearDown() override
{
m_jsonRegistrationContext->EnableRemoveReflection();
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
m_jsonRegistrationContext->DisableRemoveReflection();
AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler);
AZ::Data::AssetManager::Destroy();
AZ::JobContext::SetGlobalContext(nullptr);
delete m_jobContext;
delete m_jobManager;
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
BaseJsonSerializerFixture::TearDown();
}
private:
TestAssetHandler m_assetHandler;
AZ::JobManager* m_jobManager{ nullptr };
AZ::JobContext* m_jobContext{ nullptr };
};
TEST_F(TestSerializedAssetTracker, AssetTracker_Callback_Works)
{
auto assetCallback = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
{
if (asset.GetHint() == "test/path/foo.asset")
{
asset.SetHint("passed");
}
}
};
auto tracker = AZ::Data::SerializedAssetTracker{};
tracker.SetAssetFixUp(assetCallback);
AZ::JsonDeserializerSettings settings;
settings.m_metadata.Add(tracker);
settings.m_registrationContext = this->m_jsonRegistrationContext.get();
settings.m_serializeContext = this->m_serializeContext.get();
AZStd::string_view assetHintOnlyTestAsset = R"(
{
"assetHint" : "test/path/foo.asset"
})";
rapidjson::Document jsonDom;
jsonDom.Parse(assetHintOnlyTestAsset.data());
AZ::Data::Asset<TestAssetData> instance;
auto result = AZ::JsonSerialization::Load(instance, jsonDom, settings);
EXPECT_NE(result.GetProcessing(), AZ::JsonSerializationResult::Processing::Halted);
EXPECT_STREQ(instance.GetHint().c_str(), "passed");
}
class AssetSerializerTestDescription final
: public JsonSerializerConformityTestDescriptor<AZ::Data::Asset<TestAssetData>>
{
+193 -8
View File
@@ -2088,7 +2088,7 @@ namespace UnitTest
DisconnectNextHandlerByIdImpl multiHandler2;
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
// Set the first handler m_nextHandler field to point to the second handler
multiHandler1.m_nextHandler = &multiHandler2;
@@ -2807,7 +2807,7 @@ namespace UnitTest
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep));
}
}
void DoConnect() override
{
MyEventGroupBus::Handler::BusConnect(m_id);
@@ -2854,7 +2854,7 @@ namespace UnitTest
}
MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
@@ -2891,7 +2891,7 @@ namespace UnitTest
{
MyEventGroupImpl()
{
}
~MyEventGroupImpl() override
@@ -3614,7 +3614,7 @@ namespace UnitTest
{
AZStd::this_thread::yield();
}
EXPECT_GE(AZStd::chrono::system_clock::now(), endTime);
};
AZStd::thread connectThread([&connectHandler, &waitHandler]()
@@ -3813,7 +3813,7 @@ namespace UnitTest
struct LastHandlerDisconnectHandler
: public LastHandlerDisconnectBus::Handler
{
void OnEvent() override
void OnEvent() override
{
++m_numOnEvents;
BusDisconnect();
@@ -3854,7 +3854,7 @@ namespace UnitTest
struct DisconnectAssertHandler
: public DisconnectAssertBus::Handler
{
};
TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
@@ -3995,6 +3995,191 @@ namespace UnitTest
idTestRequest.Disconnect();
}
// IsInDispatchThisThread
struct IsInThreadDispatchRequests
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
};
using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
class IsInThreadDispatchHandler
: public IsInThreadDispatchBus::Handler
{};
TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
{
IsInThreadDispatchHandler handler;
handler.BusConnect();
auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
{
EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
auto PerThreadBusDispatch = []()
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
};
AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
for (AZStd::thread& thread : threads)
{
thread.join();
}
};
static constexpr size_t ThreadDispatcherIterations = 4;
for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
}
}
// Thread Dispatch Policy
struct ThreadDispatchTestBusTraits
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
struct PostThreadDispatchTestInvoker
{
~PostThreadDispatchTestInvoker();
};
template <typename DispatchMutex>
struct ThreadDispatchTestLockGuard
{
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
: m_lock{ contextMutex }
{}
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
: m_lock{ contextMutex, adopt_lock }
{}
ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
private:
PostThreadDispatchTestInvoker m_threadPolicyInvoker;
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
LockType m_lock;
};
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
};
class ThreadDispatchTestRequests
{
public:
virtual void FirstCall() = 0;
virtual void SecondCall() = 0;
virtual void ThirdCall() = 0;
};
using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
{
if (!ThreadDispatchTestBus::IsInDispatchThisThread())
{
++s_threadPostDispatchCalls;
}
}
class ThreadDispatchTestHandler
: public ThreadDispatchTestBus::Handler
{
public:
void Connect()
{
ThreadDispatchTestBus::Handler::BusConnect();
}
void Disconnect()
{
ThreadDispatchTestBus::Handler::BusDisconnect();
}
void FirstCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
}
void SecondCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
}
void ThirdCall() override
{
}
};
template <typename ParamType>
class EBusParamFixture
: public ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<ParamType>
{};
struct ThreadDispatchParams
{
size_t m_threadCount{};
size_t m_handlerCount{};
};
using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
INSTANTIATE_TEST_CASE_P(
ThreadDispatch,
ThreadDispatchParamFixture,
::testing::Values(
ThreadDispatchParams{ 1, 1 },
ThreadDispatchParams{ 2, 1 },
ThreadDispatchParams{ 1, 2 },
ThreadDispatchParams{ 2, 2 },
ThreadDispatchParams{ 16, 8 }
)
);
TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
{
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
ThreadDispatchParams threadDispatchParams = GetParam();
AZStd::vector<AZStd::thread> testThreads;
AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Connect();
}
static constexpr size_t DispatchThreadCalls = 3;
const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
auto DispatchThreadWorker = []()
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
};
for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
{
testThreads.emplace_back(DispatchThreadWorker);
}
for (AZStd::thread& thread : testThreads)
{
thread.join();
}
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Disconnect();
}
EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
}
} // namespace UnitTest
#if defined(HAVE_BENCHMARK)
@@ -4370,7 +4555,7 @@ namespace Benchmark
Bus::ExecuteQueuedEvents();
}
s_benchmarkEBusEnv<Bus>.Disconnect(state);
}
BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
@@ -426,6 +426,10 @@ public:
return nullptr;
}
void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override
{
}
void ClearAlias(const char* ) override { }
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64) const override
@@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING
using PathViewLexicallyProximateFixture = PathLexicallyFixture<PathViewLexicallyProximateParams>;
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot)
TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath)
{
const auto& testParams = GetParam();
AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator);
@@ -0,0 +1,196 @@
/*
* 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 <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace SettingsRegistryVisitorUtilsTests
{
struct VisitCallbackParams
{
AZStd::string_view m_inputJsonDocument;
using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
AZStd::string_view);
static inline constexpr size_t MaxFieldCount = 10;
using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
using ArrayFields = AZStd::fixed_vector<AZStd::string_view, MaxFieldCount>;
ObjectFields m_objectFields;
ArrayFields m_arrayFields;
};
template <typename VisitorParams>
class SettingsRegistryVisitorUtilsParamFixture
: public UnitTest::ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<VisitorParams>
{
public:
void SetUp() override
{
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
}
void TearDown() override
{
m_registry.reset();
}
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
};
using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture<VisitCallbackParams>;
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedArrayFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
EXPECT_TRUE(testArrayFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
EXPECT_TRUE(testObjectFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedObjectFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields));
}
INSTANTIATE_TEST_CASE_P(
VisitField,
SettingsRegistryVisitCallbackFixture,
::testing::Values(
VisitCallbackParams
{
R"({)" "\n"
R"( "Test":)" "\n"
R"( {)" "\n"
R"( "Array": [ "Hello", "World" ],)" "\n"
R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n"
R"( })" "\n"
R"(})" "\n",
VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}},
VisitCallbackParams::ArrayFields{"Hello", "World"}
}
)
);
}
@@ -30,6 +30,8 @@
namespace UnitTest
{
constexpr AZ::u32 ProfilerProxyGroup = AZ_CRC_CE("StatisticalProfilerProxyTests");
class StatisticalProfilerTest
: public AllocatorsFixture
{
@@ -98,10 +100,10 @@ namespace UnitTest
AZ::Statistics::StatisticalProfiler<AZ::Crc32> profiler;
const AZ::Crc32 statIdPerformance = AZ_CRC("PerformanceResult", 0xc1f29a10);
constexpr AZ::Crc32 statIdPerformance = AZ_CRC_CE("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Crc32 statIdBlock = AZ_CRC("Block", 0x831b9722);
constexpr AZ::Crc32 statIdBlock = AZ_CRC_CE("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
@@ -175,10 +177,10 @@ namespace UnitTest
AZ::Statistics::StatisticalProfiler<AZ::Crc32, AZStd::shared_spin_mutex> profiler;
const AZ::Crc32 statIdPerformance = AZ_CRC("PerformanceResult", 0xc1f29a10);
constexpr AZ::Crc32 statIdPerformance = AZ_CRC_CE("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Crc32 statIdBlock = AZ_CRC("Block", 0x831b9722);
constexpr AZ::Crc32 statIdBlock = AZ_CRC_CE("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
@@ -317,26 +319,26 @@ namespace UnitTest
AZ::Statistics::StatisticalProfilerProxy::TimedScope::ClearCachedProxy();
AZ::Statistics::StatisticalProfilerProxy profilerProxy;
AZ::Statistics::StatisticalProfilerProxy* proxy = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get();
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(AZ::Debug::ProfileCategory::Terrain);
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdPerformance = "PerformanceResult";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdPerformance("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdBlock = "Block";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdBlock("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdBlock, statNameBlock, "us") != nullptr);
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, true);
proxy->ActivateProfiler(ProfilerProxyGroup, true);
const int iter_count = 10;
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, statIdPerformance)
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, statIdPerformance)
int counter = 0;
for (int i = 0; i < iter_count; i++)
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, statIdBlock)
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, statIdBlock)
counter++;
}
}
@@ -348,7 +350,7 @@ namespace UnitTest
EXPECT_EQ(profiler.GetStatistic(statIdBlock)->GetNumSamples(), iter_count);
//Clean Up
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, false);
proxy->ActivateProfiler(ProfilerProxyGroup, false);
#undef CODE_PROFILER_PROXY_PUSH_TIME
@@ -362,12 +364,12 @@ namespace UnitTest
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread1("simple_thread1");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread1_loop("simple_thread1_loop");
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread1);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread1);
static int counter = 0;
for (int i = 0; i < loop_cnt; i++)
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread1_loop);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread1_loop);
counter++;
}
}
@@ -377,12 +379,12 @@ namespace UnitTest
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread2("simple_thread2");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread2_loop("simple_thread2_loop");
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread2);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread2);
static int counter = 0;
for (int i = 0; i < loop_cnt; i++)
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread2_loop);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread2_loop);
counter++;
}
}
@@ -392,12 +394,13 @@ namespace UnitTest
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread3("simple_thread3");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType simple_thread3_loop("simple_thread3_loop");
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread3);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread3);
static int counter = 0;
for (int i = 0; i < loop_cnt; i++)
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, simple_thread3_loop);
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, simple_thread3_loop);
counter++;
}
}
@@ -408,21 +411,21 @@ namespace UnitTest
AZ::Statistics::StatisticalProfilerProxy::TimedScope::ClearCachedProxy();
AZ::Statistics::StatisticalProfilerProxy profilerProxy;
AZ::Statistics::StatisticalProfilerProxy* proxy = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get();
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(AZ::Debug::ProfileCategory::Terrain);
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1 = "simple_thread1";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1("simple_thread1");
const AZStd::string statNameThread1("simple_thread1");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1Loop = "simple_thread1_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1Loop("simple_thread1_loop");
const AZStd::string statNameThread1Loop("simple_thread1_loop");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2 = "simple_thread2";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2("simple_thread2");
const AZStd::string statNameThread2("simple_thread2");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2Loop = "simple_thread2_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2Loop("simple_thread2_loop");
const AZStd::string statNameThread2Loop("simple_thread2_loop");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3 = "simple_thread3";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3("simple_thread3");
const AZStd::string statNameThread3("simple_thread3");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3Loop = "simple_thread3_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3Loop("simple_thread3_loop");
const AZStd::string statNameThread3Loop("simple_thread3_loop");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1, statNameThread1, "us"));
@@ -432,7 +435,7 @@ namespace UnitTest
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3, statNameThread3, "us"));
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3Loop, statNameThread3Loop, "us"));
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, true);
proxy->ActivateProfiler(ProfilerProxyGroup, true);
//Let's kickoff the threads to see how much contention affects the profiler's performance.
const int iter_count = 10;
@@ -459,7 +462,7 @@ namespace UnitTest
EXPECT_EQ(profiler.GetStatistic(statIdThread3Loop)->GetNumSamples(), iter_count);
//Clean Up
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, false);
proxy->ActivateProfiler(ProfilerProxyGroup, false);
}
/** Trace message handler to track messages during tests
@@ -566,10 +569,10 @@ namespace UnitTest
AZ::Statistics::StatisticalProfiler<AZ::Crc32> profiler;
const AZ::Crc32 statIdPerformance = AZ_CRC("PerformanceResult", 0xc1f29a10);
constexpr AZ::Crc32 statIdPerformance = AZ_CRC_CE("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Crc32 statIdBlock = AZ_CRC("Block", 0x831b9722);
constexpr AZ::Crc32 statIdBlock = AZ_CRC_CE("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
@@ -647,10 +650,10 @@ namespace UnitTest
AZ::Statistics::StatisticalProfiler<AZ::Crc32, AZStd::shared_spin_mutex> profiler;
const AZ::Crc32 statIdPerformance = AZ_CRC("PerformanceResult", 0xc1f29a10);
constexpr AZ::Crc32 statIdPerformance = AZ_CRC_CE("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Crc32 statIdBlock = AZ_CRC("Block", 0x831b9722);
constexpr AZ::Crc32 statIdBlock = AZ_CRC_CE("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
@@ -745,26 +748,26 @@ namespace UnitTest
AZ::Statistics::StatisticalProfilerProxy::TimedScope::ClearCachedProxy();
AZ::Statistics::StatisticalProfilerProxy profilerProxy;
AZ::Statistics::StatisticalProfilerProxy* proxy = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get();
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(AZ::Debug::ProfileCategory::Terrain);
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdPerformance = "PerformanceResult";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdPerformance("PerformanceResult");
const AZStd::string statNamePerformance("PerformanceResult");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdBlock = "Block";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdBlock("Block");
const AZStd::string statNameBlock("Block");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdBlock, statNameBlock, "us") != nullptr);
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, true);
proxy->ActivateProfiler(ProfilerProxyGroup, true);
const int iter_count = 1000000;
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, statIdPerformance)
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, statIdPerformance)
int counter = 0;
for (int i = 0; i < iter_count; i++)
{
CODE_PROFILER_PROXY_PUSH_TIME(AZ::Debug::ProfileCategory::Terrain, statIdBlock)
CODE_PROFILER_PROXY_PUSH_TIME(ProfilerProxyGroup, statIdBlock)
counter++;
}
}
@@ -778,7 +781,7 @@ namespace UnitTest
profiler.LogAndResetStats("StatisticalProfilerProxy");
//Clean Up
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, false);
proxy->ActivateProfiler(ProfilerProxyGroup, false);
}
#undef CODE_PROFILER_PROXY_PUSH_TIME
@@ -788,21 +791,21 @@ namespace UnitTest
AZ::Statistics::StatisticalProfilerProxy::TimedScope::ClearCachedProxy();
AZ::Statistics::StatisticalProfilerProxy profilerProxy;
AZ::Statistics::StatisticalProfilerProxy* proxy = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get();
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(AZ::Debug::ProfileCategory::Terrain);
AZ::Statistics::StatisticalProfilerProxy::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1 = "simple_thread1";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1("simple_thread1");
const AZStd::string statNameThread1("simple_thread1");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1Loop = "simple_thread1_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread1Loop("simple_thread1_loop");
const AZStd::string statNameThread1Loop("simple_thread1_loop");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2 = "simple_thread2";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2("simple_thread2");
const AZStd::string statNameThread2("simple_thread2");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2Loop = "simple_thread2_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread2Loop("simple_thread2_loop");
const AZStd::string statNameThread2Loop("simple_thread2_loop");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3 = "simple_thread3";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3("simple_thread3");
const AZStd::string statNameThread3("simple_thread3");
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3Loop = "simple_thread3_loop";
const AZ::Statistics::StatisticalProfilerProxy::StatIdType statIdThread3Loop("simple_thread3_loop");
const AZStd::string statNameThread3Loop("simple_thread3_loop");
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1, statNameThread1, "us"));
@@ -812,7 +815,7 @@ namespace UnitTest
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3, statNameThread3, "us"));
ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3Loop, statNameThread3Loop, "us"));
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, true);
proxy->ActivateProfiler(ProfilerProxyGroup, true);
//Let's kickoff the threads to see how much contention affects the profiler's performance.
const int iter_count = 1000000;
@@ -841,7 +844,7 @@ namespace UnitTest
profiler.LogAndResetStats("3_Threads_StatisticalProfilerProxy");
//Clean Up
proxy->ActivateProfiler(AZ::Debug::ProfileCategory::Terrain, false);
proxy->ActivateProfiler(ProfilerProxyGroup, false);
}
}//namespace UnitTest
@@ -0,0 +1,56 @@
/*
* 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 <AzCore/Time/TimeSystemComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
class TimeTests
: public AllocatorsFixture
{
public:
void SetUp() override
{
SetupAllocator();
m_timeComponent = new AZ::TimeSystemComponent;
}
void TearDown() override
{
delete m_timeComponent;
TeardownAllocator();
}
AZ::TimeSystemComponent* m_timeComponent = nullptr;
};
TEST_F(TimeTests, TestConversionUsToMs)
{
AZ::TimeUs timeUs = AZ::TimeUs{ 1000 };
AZ::TimeMs timeMs = AZ::TimeUsToMs(timeUs);
EXPECT_EQ(timeMs, AZ::TimeMs{ 1 });
}
TEST_F(TimeTests, TestConversionMsToUs)
{
AZ::TimeMs timeMs = AZ::TimeMs{ 1000 };
AZ::TimeUs timeUs = AZ::TimeMsToUs(timeMs);
EXPECT_EQ(timeUs, AZ::TimeUs{ 1000000 });
}
TEST_F(TimeTests, TestClocks)
{
AZ::TimeUs timeUs = AZ::GetElapsedTimeUs();
AZ::TimeMs timeMs = AZ::GetElapsedTimeMs();
AZ::TimeMs timeUsToMs = AZ::TimeUsToMs(timeUs);
int64_t delta = static_cast<int64_t>(timeMs) - static_cast<int64_t>(timeUsToMs);
EXPECT_LT(abs(delta), 1);
}
}
+23
View File
@@ -247,4 +247,27 @@ namespace UnitTest
Uuid right = Uuid::CreateStringPermissive(permissiveStr1);
EXPECT_EQ(left, right);
}
TEST_F(UuidTests, CreateStringPermissive_StringWithExtraData_Succeeds)
{
const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
Uuid left = Uuid::CreateString(uuidStr);
const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World";
Uuid right = Uuid::CreateStringPermissive(permissiveStr);
EXPECT_EQ(left, right);
}
TEST_F(UuidTests, CreateStringPermissive_StringWithLotsOfExtraData_Succeeds)
{
const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
Uuid left = Uuid::CreateString(uuidStr);
const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World this is a really long string "
"with lots of extra data to make sure we can parse a long string without failing as long as the uuid is in "
"the beginning of the string then we should succeed";
Uuid right = Uuid::CreateStringPermissive(permissiveStr);
EXPECT_EQ(left, right);
}
}
@@ -61,6 +61,7 @@ set(FILES
Slice.cpp
State.cpp
Statistics.cpp
StatisticalProfiler.cpp
StreamerTests.cpp
StringFunc.cpp
SystemFile.cpp
@@ -74,11 +75,12 @@ set(FILES
Name/NameJsonSerializerTests.cpp
Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp
SettingsRegistryTests.cpp
SettingsRegistryMergeUtilsTests.cpp
Settings/CommandLineTests.cpp
Settings/SettingsRegistryTests.cpp
Settings/SettingsRegistryConsoleUtilsTests.cpp
Settings/SettingsRegistryMergeUtilsTests.cpp
Settings/SettingsRegistryScriptUtilsTests.cpp
Settings/SettingsRegistryVisitorUtilsTests.cpp
Streamer/BlockCacheTests.cpp
Streamer/DedicatedCacheTests.cpp
Streamer/FullDecompressorTests.cpp
@@ -127,6 +129,7 @@ set(FILES
Serialization/Json/UnorderedSetSerializerTests.cpp
Serialization/Json/UnsupportedTypesSerializerTests.cpp
Serialization/Json/UuidSerializerTests.cpp
Time/TimeTests.cpp
Math/AabbTests.cpp
Math/ColorTests.cpp
Math/CrcTests.cpp
@@ -192,6 +195,7 @@ set(FILES
AZStd/LockFreeQueues.cpp
AZStd/LockFreeStacks.cpp
AZStd/LockTests.cpp
AZStd/Math.cpp
AZStd/Numeric.cpp
AZStd/Ordered.cpp
AZStd/Optional.cpp
@@ -77,11 +77,15 @@
namespace AzFramework
{
namespace ApplicationInternal
{
static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem";
static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures";
static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage";
static constexpr const char* DeprecatedFileIOAliasesRoot = "/O3DE/AzCore/FileIO/DeprecatedAliases";
static constexpr const char* DeprecatedFileIOAliasesOldAliasKey = "OldAlias";
static constexpr const char* DeprecatedFileIOAliasesNewAliasKey = "NewAlias";
}
Application::Application()
@@ -563,6 +567,68 @@ namespace AzFramework
}
}
struct DeprecatedAliasesKeyVisitor
: AZ::SettingsRegistryInterface::Visitor
{
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
using Type = AZ::SettingsRegistryInterface::Type;
using AZ::SettingsRegistryInterface::Visitor::Visit;
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view,
VisitAction action, Type type) override
{
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
{
if (type == AZ::SettingsRegistryInterface::Type::Array)
{
m_parentArrayPath = path;
}
// Strip off last path segment from json path and check if is a child element of the array
if (AZ::StringFunc::TokenizeLast(path, '/');
m_parentArrayPath == path)
{
m_aliases.emplace_back();
}
}
else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
{
if (type == AZ::SettingsRegistryInterface::Type::Array)
{
m_parentArrayPath = AZStd::string{};
}
}
return AZ::SettingsRegistryInterface::VisitResponse::Continue;
}
void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override
{
if (!m_aliases.empty())
{
if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey)
{
m_aliases.back().m_oldAlias = value;
}
else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey)
{
m_aliases.back().m_newAlias = value;
}
}
}
struct AliasPair
{
AZStd::string m_oldAlias;
AZStd::string m_newAlias;
};
AZStd::vector<AliasPair> m_aliases;
private:
AZStd::string m_parentArrayPath;
};
static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
{
@@ -610,9 +676,8 @@ namespace AzFramework
void Application::SetFileIOAliases()
{
if (m_archiveFileIO)
if (auto fileIoBase = m_archiveFileIO.get(); fileIoBase)
{
auto fileIoBase = m_archiveFileIO.get();
// Set up the default file aliases based on the settings registry
fileIoBase->SetAlias("@engroot@", GetEngineRoot());
fileIoBase->SetAlias("@projectroot@", GetEngineRoot());
@@ -620,29 +685,20 @@ namespace AzFramework
{
AZ::IO::FixedMaxPath pathAliases;
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
{
fileIoBase->SetAlias("@projectcache@", pathAliases.c_str());
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
fileIoBase->SetAlias("@assets@", pathAliases.c_str());
fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str());
fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@
fileIoBase->SetAlias("@products@", pathAliases.c_str());
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
fileIoBase->SetAlias("@engroot@", pathAliases.c_str());
fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@
}
pathAliases.clear();
if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@
fileIoBase->SetAlias("@projectroot@", pathAliases.c_str());
fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str());
}
}
@@ -663,6 +719,15 @@ namespace AzFramework
}
fileIoBase->SetAlias("@log@", projectLogPath.c_str());
fileIoBase->CreatePath(projectLogPath.c_str());
DeprecatedAliasesKeyVisitor visitor;
if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
{
for (const auto& [oldAlias, newAlias] : visitor.m_aliases)
{
fileIoBase->SetDeprecatedAlias(oldAlias, newAlias);
}
}
}
}
@@ -1121,7 +1121,7 @@ namespace AZ::IO
if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot))
{
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@");
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@products@");
desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String();
}
else
@@ -1807,9 +1807,9 @@ namespace AZ::IO
if (m_eRecordFileOpenList != IArchive::RFOM_Disabled)
{
// we only want to record ASSET access
// assets are identified as files that are relative to the resolved @assets@ alias path
// assets are identified as files that are relative to the resolved @products@ alias path
auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
const char* aliasValue = fileIoBase->GetAlias("@assets@");
const char* aliasValue = fileIoBase->GetAlias("@products@");
if (AZ::IO::FixedMaxPath resolvedFilePath;
fileIoBase->ResolvePath(resolvedFilePath, szFilename)
@@ -546,6 +546,16 @@ namespace AZ::IO
realUnderlyingFileIO->GetAlias(alias);
}
void ArchiveFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance();
if (!realUnderlyingFileIO)
{
return;
}
realUnderlyingFileIO->SetDeprecatedAlias(oldAlias, newAlias);
}
AZStd::optional<AZ::u64> ArchiveFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
if ((!inOutBuffer) || (bufferLength == 0))
@@ -63,6 +63,7 @@ namespace AZ::IO
IO::Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -186,8 +186,8 @@ namespace AZ::IO
{
// filter out the stuff which does not match.
// the problem here is that szDir might be something like "@assets@/levels/*"
// but our archive might be mounted at the root, or at some other folder at like "@assets@" or "@assets@/levels/mylevel"
// the problem here is that szDir might be something like "@products@/levels/*"
// but our archive might be mounted at the root, or at some other folder at like "@products@" or "@products@/levels/mylevel"
// so there's really no way to filter out opening the pack and looking at the files inside.
// however, the bind root is not part of the inner zip entry name either
// and the ZipDir::FindFile actually expects just the chopped off piece.
@@ -202,22 +202,22 @@ namespace AZ::IO
// Example:
// "@assets@\\levels\\*" <--- szDir
// "@assets@\\" <--- mount point
// "@products@\\levels\\*" <--- szDir
// "@products@\\" <--- mount point
// ~~~~~~~~~~~ Common part
// "levels\\*" <---- remainder that is not in common
// "" <--- mount point remainder. In this case, we should scan the contents of the pak for the remainder
// Example:
// "@assets@\\levels\\*" <--- szDir
// "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "@products@\\levels\\*" <--- szDir
// "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// ~~~~~~~~~~~~~~~~~~ common part
// "*" <---- remainder that is not in common
// "mylevel\\" <--- mount point remainder.
// example:
// "@assets@\\levels\\otherlevel\\*" <--- szDir
// "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "@products@\\levels\\otherlevel\\*" <--- szDir
// "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
// "otherlevel\\*" <---- remainder
// "mylevel\\" <--- mount point remainder.
@@ -249,7 +249,7 @@ namespace AZ::IO
// which means we may search inside the pack.
ScanInZip(it->pZip.get(), sourcePathRemainder.Native());
}
}
}
@@ -94,7 +94,7 @@ namespace AZ::IO::Internal
}
AZStd::smatch matches;
const AZStd::regex lodRegex("@assets@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
const AZStd::regex lodRegex("@products@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
if (!AZStd::regex_match(szPath, matches, lodRegex) || matches.size() != 3)
{
// The current file is not a valid LOD file
@@ -725,7 +725,7 @@ namespace AzFramework
if (!info.m_relativePath.empty())
{
const char* devAssetRoot = fileIO->GetAlias("@devassets@");
const char* devAssetRoot = fileIO->GetAlias("@projectroot@");
if (devAssetRoot)
{
AZ::Data::AssetStreamInfo streamInfo;
@@ -133,6 +133,8 @@ namespace AzFramework
behaviorContext->Class<BehaviorEntity>("Entity")
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "entity")
->Constructor()
->Constructor<AZ::EntityId>()
->Constructor<AZ::Entity*>()
@@ -61,7 +61,7 @@ namespace AzFramework
AZ::IO::Path& gemAbsPath = gemInfo.m_absoluteSourcePaths.emplace_back(value);
// Resolve any file aliases first - Do not use ResolvePath() as that assumes
// any relative path is underneath the @assets@ alias
// any relative path is underneath the @products@ alias
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
AZ::IO::FixedMaxPath replacedAliasPath;
@@ -12,10 +12,12 @@
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/std/containers/fixed_unordered_set.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <cctype>
namespace AZ
@@ -281,10 +283,18 @@ namespace AZ
return SystemFile::Exists(resolvedPath);
}
bool LocalFileIO::IsDirectory(const char* filePath)
{
char resolvedPath[AZ_MAX_PATH_LEN];
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
return SystemFile::IsDirectory(resolvedPath);
}
void LocalFileIO::CheckInvalidWrite([[maybe_unused]] const char* path)
{
#if defined(AZ_ENABLE_TRACING)
const char* assetAliasPath = GetAlias("@assets@");
const char* assetAliasPath = GetAlias("@products@");
if (path && assetAliasPath)
{
const AZ::IO::PathView pathView(path);
@@ -470,17 +480,15 @@ namespace AZ
return false;
}
if (IsAbsolutePath(path))
if (AZ::IO::PathView(path).HasRootPath())
{
size_t pathLen = strlen(path);
if (pathLen + 1 < resolvedPathSize)
{
azstrncpy(resolvedPath, resolvedPathSize, path, pathLen + 1);
//see if the absolute path uses @assets@ or @root@, if it does lowercase the relative part
[[maybe_unused]] bool lowercasePath = LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@assets@"))
|| LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@root@"))
|| LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@projectplatformcache@"));
//see if the absolute path matches the resolved value of @products@, if it does lowercase the relative part
LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@products@"));
ToUnixSlashes(resolvedPath, resolvedPathSize);
return true;
@@ -491,34 +499,39 @@ namespace AZ
}
}
char rootedPathBuffer[AZ_MAX_PATH_LEN] = {0};
constexpr AZStd::string_view productAssetAlias = "@products@";
// Add plus one for the path separator: <alias>/<path>
constexpr size_t MaxPathSizeWithProductAssetAlias = AZ::IO::MaxPathLength + productAssetAlias.size() + 1;
using RootedPathString = AZStd::fixed_string<MaxPathSizeWithProductAssetAlias>;
RootedPathString rootedPathBuffer;
const char* rootedPath = path;
// if the path does not begin with an alias, then it is assumed to begin with @assets@
// if the path does not begin with an alias, then it is assumed to begin with @products@
if (path[0] != '@')
{
if (GetAlias("@assets@"))
if (GetAlias("@products@"))
{
const int rootLength = 9;// strlen("@assets@/")
azstrncpy(rootedPathBuffer, AZ_MAX_PATH_LEN, "@assets@/", rootLength);
size_t pathLen = strlen(path);
size_t rootedPathBufferlength = rootLength + pathLen + 1;// +1 for null terminator
if (rootedPathBufferlength > resolvedPathSize)
if (const size_t requiredSize = productAssetAlias.size() + strlen(path) + 1;
requiredSize > rootedPathBuffer.capacity())
{
AZ_Assert(rootedPathBufferlength < resolvedPathSize, "Constructed path length is wrong:%s", rootedPathBuffer);//path constructed is wrong
size_t remainingSize = resolvedPathSize - rootLength - 1;// - 1 for null terminator
azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN, path, remainingSize);
rootedPathBuffer[resolvedPathSize - 1] = '\0';
AZ_Error("FileIO", false, "Prepending the %.*s alias to the input path results in a path longer than the"
" AZ::IO::MaxPathLength + the alias size of %zu. The size of the potential failed path is %zu",
AZ_STRING_ARG(productAssetAlias), rootedPathBuffer.capacity(), requiredSize)
}
else
{
azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN - rootLength, path, pathLen + 1);
rootedPathBuffer = RootedPathString::format("%.*s/%s", AZ_STRING_ARG(productAssetAlias), path);
}
}
else
{
ConvertToAbsolutePath(path, rootedPathBuffer, AZ_MAX_PATH_LEN);
if (ConvertToAbsolutePath(path, rootedPathBuffer.data(), rootedPathBuffer.capacity()))
{
// Recalculate the internal string length
rootedPathBuffer.resize_no_construct(AZStd::char_traits<char>::length(rootedPathBuffer.data()));
}
}
rootedPath = rootedPathBuffer;
rootedPath = rootedPathBuffer.c_str();
}
if (ResolveAliases(rootedPath, resolvedPath, resolvedPathSize))
@@ -553,11 +566,57 @@ namespace AZ
const char* LocalFileIO::GetAlias(const char* key) const
{
const auto it = m_aliases.find(key);
if (it != m_aliases.end())
if (const auto it = m_aliases.find(key); it != m_aliases.end())
{
return it->second.c_str();
}
else if (const auto deprecatedIt = m_deprecatedAliases.find(key);
deprecatedIt != m_deprecatedAliases.end())
{
AZ_Error("FileIO", false, R"(Alias "%s" is deprecated. Please use alias "%s" instead)",
key, deprecatedIt->second.c_str());
AZStd::string_view aliasValue = deprecatedIt->second;
// Contains the list of aliases resolved so far
// If max_size is hit, than an error is logged and nullptr is returned
using VisitedAliasSet = AZStd::fixed_unordered_set<AZStd::string_view, 8, 8>;
VisitedAliasSet visitedAliasSet;
while (aliasValue.starts_with("@"))
{
if (visitedAliasSet.contains(aliasValue))
{
AZ_Error("FileIO", false, "Cycle found with for alias %.*s when trying to resolve deprecated alias %s",
AZ_STRING_ARG(aliasValue), key);
return nullptr;
}
if(visitedAliasSet.size() == visitedAliasSet.max_size())
{
AZ_Error("FileIO", false, "Unable to resolve path to deprecated alias %s within %zu steps",
key, visitedAliasSet.max_size());
return nullptr;
}
// Add the current alias value to the visited set
visitedAliasSet.emplace(aliasValue);
// Check if the alias value corresponds to another alias
if (auto resolvedIter = m_aliases.find(aliasValue); resolvedIter != m_aliases.end())
{
aliasValue = resolvedIter->second;
}
else if (resolvedIter = m_deprecatedAliases.find(aliasValue);
resolvedIter != m_deprecatedAliases.end())
{
aliasValue = resolvedIter->second;
}
else
{
return nullptr;
}
}
return aliasValue.data();
}
return nullptr;
}
@@ -566,6 +625,11 @@ namespace AZ
m_aliases.erase(key);
}
void LocalFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
m_deprecatedAliases[oldAlias] = newAlias;
}
AZStd::optional<AZ::u64> LocalFileIO::ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const
{
size_t longestMatch = 0;
@@ -667,7 +731,9 @@ namespace AZ
: string_view_pair{};
size_t requiredResolvedPathSize = pathView.size() - aliasKey.size() + aliasValue.size() + 1;
AZ_Assert(path != resolvedPath && resolvedPathSize >= requiredResolvedPathSize, "Resolved path is incorrect");
AZ_Assert(path != resolvedPath, "ResolveAliases does not support inplace update of the path");
AZ_Assert(resolvedPathSize >= requiredResolvedPathSize, "Resolved path size %llu not large enough. It needs to be %zu",
resolvedPathSize, requiredResolvedPathSize);
// we assert above, but we also need to properly handle the case when the resolvedPath buffer size
// is too small to copy the source into.
if (path == resolvedPath || (resolvedPathSize < requiredResolvedPathSize))
@@ -691,13 +757,9 @@ namespace AZ
resolvedPath[resolvedPathLen] = '\0';
// If the path started with one of the "asset cache" path aliases, lowercase the path
const char* assetAliasPath = GetAlias("@assets@");
const char* rootAliasPath = GetAlias("@root@");
const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@");
const char* projectPlatformCacheAliasPath = GetAlias("@products@");
const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) ||
(rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) ||
(projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath));
const bool lowercasePath = projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath);
if (lowercasePath)
{
@@ -814,5 +876,10 @@ namespace AZ
return pathStr + "/";
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
return AZ::Utils::ConvertToAbsolutePath(path, absolutePath, maxLength);
}
} // namespace IO
} // namespace AZ
@@ -61,6 +61,8 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
const char* GetAlias(const char* alias) const override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -71,7 +73,7 @@ namespace AZ
bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override;
bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const;
private:
SystemFile* GetFilePointerFromHandle(HandleType fileHandle);
@@ -79,7 +81,6 @@ namespace AZ
AZStd::optional<AZ::u64> ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const;
bool ResolveAliases(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const;
bool IsAbsolutePath(const char* path) const;
bool LowerIfBeginsWith(char* inOutBuffer, AZ::u64 bufferLen, const char* alias) const;
@@ -91,6 +92,7 @@ namespace AZ
AZStd::atomic<HandleType> m_nextHandle;
AZStd::unordered_map<HandleType, SystemFile> m_openFiles;
AZStd::unordered_map<AZStd::string, AZStd::string> m_aliases;
AZStd::unordered_map<AZStd::string, AZStd::string> m_deprecatedAliases;
void CheckInvalidWrite(const char* path);
};
@@ -49,14 +49,14 @@ namespace AZ
s_IOLog.append(m_name);
s_IOLog.append("\r\n");
}
void Append(const char* line)
{
s_IOLog.append(AZStd::string::format("%u ", m_fileOperation));
s_IOLog.append(line);
s_IOLog.append("\r\n");
}
~LogCall()
{
s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation));
@@ -251,7 +251,7 @@ namespace AZ
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str());
return ResultCode::Error;
}
size = response.m_size;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str());
return ResultCode::Success;
@@ -793,6 +793,12 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str());
}
void NetworkFileIO::SetDeprecatedAlias([[maybe_unused]] AZStd::string_view oldAlias, [[maybe_unused]] AZStd::string_view newAlias)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetDeprecatedAlias(oldAlias=%.*s, newAlias=%.*s)",
AZ_STRING_ARG(oldAlias), AZ_STRING_ARG(newAlias)).c_str());
}
AZStd::optional<AZ::u64> NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str());
@@ -927,7 +933,7 @@ namespace AZ
{
m_cacheLookaheadPos = filePosition - CacheStartFilePosition();
}
void RemoteFileCache::SyncCheck()
{
#ifdef REMOTEFILEIO_SYNC_CHECK
@@ -955,7 +961,7 @@ namespace AZ
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str());
}
if (responce.m_offset != m_filePosition)
{
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset);
@@ -1028,7 +1034,7 @@ namespace AZ
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
Result returnValue = NetworkFileIO::Close(fileHandle);
if (returnValue == ResultCode::Success)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
@@ -1160,7 +1166,7 @@ namespace AZ
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
AZ::u64 remainingBytesToRead = size;
AZ::u64 bytesReadFromCache = 0;
AZ::u64 remainingBytesInCache = cache.RemainingBytes();
@@ -1263,7 +1269,7 @@ namespace AZ
RemoteFileCache& cache = GetCache(fileHandle);
if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes())
{
// find out where we are
// find out where we are
AZ::u64 seekPosition = cache.CacheFilePosition();
// note, seeks are predicted, and do not ask for a response.
@@ -1361,6 +1367,14 @@ namespace AZ
}
}
void RemoteFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
if (m_excludedFileIO)
{
m_excludedFileIO->SetDeprecatedAlias(oldAlias, newAlias);
}
}
AZStd::optional<AZ::u64> RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer);
@@ -102,6 +102,7 @@ namespace AZ
Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -194,6 +195,7 @@ namespace AZ
void SetAlias(const char* alias, const char* path) override;
const char* GetAlias(const char* alias) const override;
void ClearAlias(const char* alias) override;
void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
@@ -35,6 +35,7 @@ namespace AzFramework
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingAnd::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
@@ -35,6 +35,7 @@ namespace AzFramework
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingOr::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
@@ -0,0 +1,63 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Matchmaking/MatchmakingRequests.h>
namespace AzFramework
{
//! IMatchmakingRequests
//! Pure virtual session interface class to abstract the details of session handling from application code.
class IMatchmakingRequests
{
public:
AZ_RTTI(IMatchmakingRequests, "{BC0B74DA-A448-4F40-9B50-9D73142829D5}");
IMatchmakingRequests() = default;
virtual ~IMatchmakingRequests() = default;
// Registers a player's acceptance or rejection of a proposed matchmaking.
// @param acceptMatchRequest The request of AcceptMatch operation
virtual void AcceptMatch(const AcceptMatchRequest& acceptMatchRequest) = 0;
// Create a game match for a group of players.
// @param startMatchmakingRequest The request of StartMatchmaking operation
// @return A unique identifier for a matchmaking ticket
virtual AZStd::string StartMatchmaking(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
// Cancels a matchmaking ticket that is currently being processed.
// @param stopMatchmakingRequest The request of StopMatchmaking operation
virtual void StopMatchmaking(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
};
//! IMatchmakingAsyncRequests
//! Async version of IMatchmakingRequests
class IMatchmakingAsyncRequests
{
public:
AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
IMatchmakingAsyncRequests() = default;
virtual ~IMatchmakingAsyncRequests() = default;
// AcceptMatch Async
// @param acceptMatchRequest The request of AcceptMatch operation
virtual void AcceptMatchAsync(const AcceptMatchRequest& acceptMatchRequest) = 0;
// StartMatchmaking Async
// @param startMatchmakingRequest The request of StartMatchmaking operation
virtual void StartMatchmakingAsync(const StartMatchmakingRequest& startMatchmakingRequest) = 0;
// StopMatchmaking Async
// @param stopMatchmakingRequest The request of StopMatchmaking operation
virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
};
} // namespace AzFramework
@@ -0,0 +1,62 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AzFramework
{
//! MatchmakingAsyncRequestNotifications
//! The notifications correspond to matchmaking async requests
class MatchmakingAsyncRequestNotifications
: public AZ::EBusTraits
{
public:
// Safeguard handler for multi-threaded use case
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
virtual void OnAcceptMatchAsyncComplete() = 0;
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
virtual void OnStopMatchmakingAsyncComplete() = 0;
};
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
//! MatchmakingNotifications
//! The matchmaking notifications to listen for performing required operations
class MatchAcceptanceNotifications
: public AZ::EBusTraits
{
public:
// Safeguard handler for multi-threaded use case
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE
virtual void OnMatchAcceptance() = 0;
};
using MatchAcceptanceNotificationBus = AZ::EBus<MatchAcceptanceNotifications>;
} // namespace AzFramework
@@ -0,0 +1,78 @@
/*
* 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 <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Matchmaking/MatchmakingRequests.h>
namespace AzFramework
{
void AcceptMatchRequest::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AcceptMatchRequest>()
->Version(0)
->Field("acceptMatch", &AcceptMatchRequest::m_acceptMatch)
->Field("playerIds", &AcceptMatchRequest::m_playerIds)
->Field("ticketId", &AcceptMatchRequest::m_ticketId);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<AcceptMatchRequest>("AcceptMatchRequest", "The container for AcceptMatch request parameters")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_acceptMatch, "AcceptMatch",
"Player response to accept or reject match")
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_playerIds, "PlayerIds",
"A list of unique identifiers for players delivering the response")
->DataElement(AZ::Edit::UIHandlers::Default, &AcceptMatchRequest::m_ticketId, "TicketId",
"A unique identifier for a matchmaking ticket");
}
}
}
void StartMatchmakingRequest::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<StartMatchmakingRequest>()
->Version(0)
->Field("ticketId", &StartMatchmakingRequest::m_ticketId);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<StartMatchmakingRequest>("StartMatchmakingRequest", "The container for StartMatchmaking request parameters")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &StartMatchmakingRequest::m_ticketId, "TicketId",
"A unique identifier for a matchmaking ticket");
}
}
}
void StopMatchmakingRequest::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<StopMatchmakingRequest>()
->Version(0)
->Field("ticketId", &StopMatchmakingRequest::m_ticketId);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<StopMatchmakingRequest>("StopMatchmakingRequest", "The container for StopMatchmaking request parameters")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &StopMatchmakingRequest::m_ticketId, "TicketId",
"A unique identifier for a matchmaking ticket");
}
}
}
} // namespace AzFramework
@@ -0,0 +1,67 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
class ReflectContext;
}
namespace AzFramework
{
//! AcceptMatchRequest
//! The container for AcceptMatch request parameters.
struct AcceptMatchRequest
{
AZ_RTTI(AcceptMatchRequest, "{AD289D76-CEE2-424F-847E-E62AA83B7D79}");
static void Reflect(AZ::ReflectContext* context);
AcceptMatchRequest() = default;
virtual ~AcceptMatchRequest() = default;
// Player response to accept or reject match
bool m_acceptMatch;
// A list of unique identifiers for players delivering the response
AZStd::vector<AZStd::string> m_playerIds;
// A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
//! StartMatchmakingRequest
//! The container for StartMatchmaking request parameters.
struct StartMatchmakingRequest
{
AZ_RTTI(StartMatchmakingRequest, "{70B47776-E8E7-4993-BEC3-5CAEC3D48E47}");
static void Reflect(AZ::ReflectContext* context);
StartMatchmakingRequest() = default;
virtual ~StartMatchmakingRequest() = default;
// A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
//! StopMatchmakingRequest
//! The container for StopMatchmaking request parameters.
struct StopMatchmakingRequest
{
AZ_RTTI(StopMatchmakingRequest, "{6132E293-65EF-4DC2-A8A0-00269697229D}");
static void Reflect(AZ::ReflectContext* context);
StopMatchmakingRequest() = default;
virtual ~StopMatchmakingRequest() = default;
// A unique identifier for a matchmaking ticket
AZStd::string m_ticketId;
};
} // namespace AzFramework
@@ -10,98 +10,11 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzFramework/Session/SessionRequests.h>
namespace AzFramework
{
struct SessionConfig;
//! CreateSessionRequest
//! The container for CreateSession request parameters.
struct CreateSessionRequest
{
AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}");
static void Reflect(AZ::ReflectContext* context);
CreateSessionRequest() = default;
virtual ~CreateSessionRequest() = default;
// A unique identifier for a player or entity creating the session.
AZStd::string m_creatorId;
// A collection of custom properties for a session.
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
// A descriptive label that is associated with a session.
AZStd::string m_sessionName;
// The maximum number of players that can be connected simultaneously to the session.
uint64_t m_maxPlayer = 0;
};
//! SearchSessionsRequest
//! The container for SearchSessions request parameters.
struct SearchSessionsRequest
{
AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}");
static void Reflect(AZ::ReflectContext* context);
SearchSessionsRequest() = default;
virtual ~SearchSessionsRequest() = default;
// String containing the search criteria for the session search. If no filter expression is included, the request returns results
// for all active sessions.
AZStd::string m_filterExpression;
// Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order.
AZStd::string m_sortExpression;
// The maximum number of results to return.
uint8_t m_maxResult = 0;
// A token that indicates the start of the next sequential page of results.
AZStd::string m_nextToken;
};
//! SearchSessionsResponse
//! The container for SearchSession request results.
struct SearchSessionsResponse
{
AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}");
static void Reflect(AZ::ReflectContext* context);
SearchSessionsResponse() = default;
virtual ~SearchSessionsResponse() = default;
// A collection of sessions that match the search criteria and sorted in specific order.
AZStd::vector<SessionConfig> m_sessionConfigs;
// A token that indicates the start of the next sequential page of results.
AZStd::string m_nextToken;
};
//! JoinSessionRequest
//! The container for JoinSession request parameters.
struct JoinSessionRequest
{
AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}");
static void Reflect(AZ::ReflectContext* context);
JoinSessionRequest() = default;
virtual ~JoinSessionRequest() = default;
// A unique identifier for the session.
AZStd::string m_sessionId;
// A unique identifier for a player. Player IDs are developer-defined.
AZStd::string m_playerId;
// Developer-defined information related to a player.
AZStd::string m_playerData;
};
//! ISessionRequests
//! Pure virtual session interface class to abstract the details of session handling from application code.
class ISessionRequests

Some files were not shown because too many files have changed in this diff Show More