Merge branch 'development' of https://github.com/o3de/o3de into mp_deltaserializer_perf

This commit is contained in:
puvvadar
2021-10-13 16:28:40 -07:00
1418 changed files with 29698 additions and 21814 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
@@ -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;
@@ -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{};
};
@@ -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)
+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); }
@@ -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);
}
@@ -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);
};
@@ -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}
@@ -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();
}
}
}
@@ -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;
@@ -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"}
}
)
);
}
+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);
}
}
@@ -75,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
@@ -194,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;
@@ -29,6 +29,10 @@ namespace AzFramework
AZStd::vector<AZ::IO::Path> m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path)
static constexpr const char* GetGemAssetFolder() { return "Assets"; }
static constexpr const char* GetGemRegistryFolder()
{
return "Registry";
}
};
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.
@@ -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
@@ -292,7 +294,7 @@ namespace AZ
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);
@@ -478,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;
@@ -499,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))
@@ -561,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;
}
@@ -574,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;
@@ -675,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))
@@ -699,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)
{
@@ -822,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
@@ -6,9 +6,10 @@
*
*/
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Session/ISessionRequests.h>
#include <AzFramework/Session/SessionRequests.h>
#include <AzFramework/Session/SessionConfig.h>
namespace AzFramework
@@ -0,0 +1,107 @@
/*
* 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/unordered_map.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
class ReflectContext;
}
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;
};
} // namespace AzFramework
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
@@ -17,16 +18,38 @@ namespace AzFramework
{
namespace SurfaceData
{
namespace Constants
{
static const char* s_unassignedTagName = "(unassigned)";
}
struct SurfaceTagWeight
{
AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}");
AZ::Crc32 m_surfaceType;
float m_weight; //! A Value in the range [0.0f .. 1.0f]
AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName);
float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f]
//! Don't call this directly. TerrainDataRequests::Reflect is doing it already.
static void Reflect(AZ::ReflectContext* context);
};
struct SurfaceTagWeightComparator
{
bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const
{
if (!AZ::IsClose(tagWeight1.m_weight, tagWeight2.m_weight))
{
return tagWeight1.m_weight > tagWeight2.m_weight;
}
else
{
return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType;
}
}
};
using OrderedSurfaceTagWeightSet = AZStd::set<SurfaceTagWeight, SurfaceTagWeightComparator>;
} //namespace SurfaceData
namespace Terrain
@@ -75,8 +98,28 @@ namespace AzFramework
//! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false,
//! otherwise *terrainExistsPtr will be set to true.
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0;
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
//! Given an XY coordinate, return the set of surface types and weights. The Vector3 input position version is defined to ignore
//! the input Z value.
virtual void GetSurfaceWeights(
const AZ::Vector3& inPosition,
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
Sampler sampleFilter = Sampler::DEFAULT,
bool* terrainExistsPtr = nullptr) const = 0;
virtual void GetSurfaceWeightsFromVector2(
const AZ::Vector2& inPosition,
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
Sampler sampleFilter = Sampler::DEFAULT,
bool* terrainExistsPtr = nullptr) const = 0;
virtual void GetSurfaceWeightsFromFloats(
float x,
float y,
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
Sampler sampleFilter = Sampler::DEFAULT,
bool* terrainExistsPtr = nullptr) const = 0;
//! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats.
//! Not available in the behavior context.
//! Returns nullptr if the position is inside a hole or outside of the terrain boundaries.
@@ -533,8 +533,8 @@ namespace AzFramework
m_translateCameraInputChannelIds = translateCameraInputChannelIds;
}
PivotCameraInput::PivotCameraInput(const InputChannelId& pivotChannelId)
: m_pivotChannelId(pivotChannelId)
OrbitCameraInput::OrbitCameraInput(const InputChannelId& orbitChannelId)
: m_orbitChannelId(orbitChannelId)
{
m_pivotFn = []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
@@ -542,11 +542,11 @@ namespace AzFramework
};
}
bool PivotCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_pivotChannelId)
if (input->m_channelId == m_orbitChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -561,13 +561,13 @@ namespace AzFramework
if (Active())
{
return m_pivotCameras.HandleEvents(event, cursorDelta, scrollDelta);
return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
}
return !Idle();
}
Camera PivotCameraInput::StepCamera(
Camera OrbitCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -581,12 +581,12 @@ namespace AzFramework
if (Active())
{
MovePivotDetached(nextCamera, m_pivotFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()));
nextCamera = m_pivotCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
if (Ending())
{
m_pivotCameras.Reset();
m_orbitCameras.Reset();
nextCamera.m_pivot = nextCamera.Translation();
nextCamera.m_offset = AZ::Vector3::CreateZero();
@@ -595,12 +595,12 @@ namespace AzFramework
return nextCamera;
}
void PivotCameraInput::SetPivotInputChannelId(const InputChannelId& pivotChanneId)
void OrbitCameraInput::SetOrbitInputChannelId(const InputChannelId& orbitChanneId)
{
m_pivotChannelId = pivotChanneId;
m_orbitChannelId = orbitChanneId;
}
PivotDollyScrollCameraInput::PivotDollyScrollCameraInput()
OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput()
{
m_scrollSpeedFn = []() constexpr
{
@@ -608,7 +608,7 @@ namespace AzFramework
};
}
bool PivotDollyScrollCameraInput::HandleEvents(
bool OrbitDollyScrollCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@@ -619,36 +619,45 @@ namespace AzFramework
return !Idle();
}
static Camera PivotDolly(const Camera& targetCamera, const float delta)
static Camera OrbitDolly(const Camera& targetCamera, const float delta)
{
Camera nextCamera = targetCamera;
const auto pivotDirection = targetCamera.m_offset.GetNormalized();
nextCamera.m_offset -= pivotDirection * delta;
const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset);
const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot);
const auto minDistance = 0.01f;
if (distance < minDistance || pivotDot < 0.0f)
// handle case where pivot and offset may be the same to begin with
// choose negative y-axis for offset to default to moving the camera backwards from the pivot (standard centered pivot behavior)
const auto pivotDirection = [&targetCamera]
{
nextCamera.m_offset = pivotDirection * minDistance;
if (const auto offsetLength = targetCamera.m_offset.GetLength(); AZ::IsCloseMag(offsetLength, 0.0f))
{
return -AZ::Vector3::CreateAxisY();
}
else
{
return targetCamera.m_offset / offsetLength;
}
}();
nextCamera.m_offset -= pivotDirection * delta;
if (pivotDirection.Dot(nextCamera.m_offset) < 0.0f)
{
nextCamera.m_offset = pivotDirection * 0.001f;
}
return nextCamera;
}
Camera PivotDollyScrollCameraInput::StepCamera(
Camera OrbitDollyScrollCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
const auto nextCamera = PivotDolly(targetCamera, aznumeric_cast<float>(scrollDelta) * m_scrollSpeedFn());
const auto nextCamera = OrbitDolly(targetCamera, aznumeric_cast<float>(scrollDelta) * m_scrollSpeedFn());
EndActivation();
return nextCamera;
}
PivotDollyMotionCameraInput::PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId)
OrbitDollyMotionCameraInput::OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
m_motionSpeedFn = []() constexpr
@@ -657,28 +666,28 @@ namespace AzFramework
};
}
bool PivotDollyMotionCameraInput::HandleEvents(
bool OrbitDollyMotionCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this);
return CameraInputUpdatingAfterMotion(*this);
}
Camera PivotDollyMotionCameraInput::StepCamera(
Camera OrbitDollyMotionCameraInput::StepCamera(
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
return PivotDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
return OrbitDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
}
void PivotDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
void OrbitDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
{
m_dollyChannelId = dollyChannelId;
}
ScrollTranslationCameraInput::ScrollTranslationCameraInput()
LookScrollTranslationCameraInput::LookScrollTranslationCameraInput()
{
m_scrollSpeedFn = []() constexpr
{
@@ -686,7 +695,7 @@ namespace AzFramework
};
}
bool ScrollTranslationCameraInput::HandleEvents(
bool LookScrollTranslationCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@@ -697,7 +706,7 @@ namespace AzFramework
return !Idle();
}
Camera ScrollTranslationCameraInput::StepCamera(
Camera LookScrollTranslationCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
@@ -771,6 +780,73 @@ namespace AzFramework
return camera;
}
FocusCameraInput::FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn)
: m_focusChannelId(focusChannelId)
, m_offsetFn(offsetFn)
{
}
bool FocusCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_focusChannelId && input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
}
return !Idle();
}
Camera FocusCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
[[maybe_unused]] float scrollDelta,
[[maybe_unused]] float deltaTime)
{
if (Beginning())
{
// as the camera starts, record the camera we would like to end up as
m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation()));
const auto angles =
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn())));
m_nextCamera.m_pitch = angles.GetX();
m_nextCamera.m_yaw = angles.GetZ();
m_nextCamera.m_pivot = targetCamera.m_pivot;
}
// end the behavior when the camera is in alignment
if (AZ::IsCloseMag(targetCamera.m_pitch, m_nextCamera.m_pitch) && AZ::IsCloseMag(targetCamera.m_yaw, m_nextCamera.m_yaw))
{
EndActivation();
}
return m_nextCamera;
}
void FocusCameraInput::SetPivotFn(PivotFn pivotFn)
{
m_pivotFn = AZStd::move(pivotFn);
}
void FocusCameraInput::SetFocusInputChannelId(const InputChannelId& focusChannelId)
{
m_focusChannelId = focusChannelId;
}
bool CustomCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
{
return m_handleEventsFn(*this, event, cursorDelta, scrollDelta);
}
Camera CustomCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
return m_stepCameraFn(*this, targetCamera, cursorDelta, scrollDelta, deltaTime);
}
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize)
{
const auto& inputChannelId = inputChannel.GetInputChannelId();
@@ -30,8 +30,11 @@ namespace AzFramework
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
//! A simple camera representation using spherical coordinates as input (pitch, yaw, pivot and offset).
//! The cameras transform and view can be obtained through accessor functions that use the internal
//! The camera's transform and view can be obtained through accessor functions that use the internal
//! spherical coordinates to calculate the position and orientation.
//! @note Modifying m_pivot directly and leaving m_offset as zero will produce a free look camera effect, giving
//! m_offset a value (e.g. in negative Y only) will produce an orbit camera effect, modifying X and Z of m_offset
//! will further alter the camera translation in relation to m_pivot so it appears off center.
struct Camera
{
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero(); //!< Pivot point to rotate about (modified in world space).
@@ -291,7 +294,7 @@ namespace AzFramework
Cameras m_cameras; //!< Represents a collection of camera inputs that together provide a camera controller.
private:
ScreenVector m_motionDelta; //!< The delta used for look/pivot/pan (rotation + translation) - two dimensional.
ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional.
CursorState m_cursorState; //!< The current and previous position of the cursor (used to calculate movement delta).
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
bool m_handlingEvents = false; //!< Is the camera system currently handling events (events are consumed and not propagated).
@@ -316,7 +319,7 @@ namespace AzFramework
return AZStd::fmod(yaw + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
}
//! A camera input to handle motion deltas that can rotate or pivot the camera.
//! A camera input to handle motion deltas that can change the orientation of the camera (update pitch and yaw).
class RotateCameraInput : public CameraInput
{
public:
@@ -348,15 +351,16 @@ namespace AzFramework
//! PanAxes build function that will return a pair of pan axes depending on the camera orientation.
using PanAxesFn = AZStd::function<PanAxes(const Camera& camera)>;
//! PanAxes to use while in 'look' camera behavior (free look).
//! PanAxes to use while in 'look' or 'orbit' camera behavior.
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return { orientation.GetBasisX(), orientation.GetBasisZ() };
}
//! PanAxes to use while in 'pivot' camera behavior.
inline PanAxes PivotPan(const Camera& camera)
//! Optional PanAxes to use while in 'orbit' camera behavior.
//! @note This will move the camera in the local X/Y plane instead of usual X/Z plane.
inline PanAxes OrbitPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -370,14 +374,23 @@ namespace AzFramework
return { basisX, basisY };
}
//! TranslationDeltaFn is used by PanCameraInput and TranslateCameraInput
//! @note Choose the appropriate function if the behavior should be operating as a free look camera (TranslatePivotLook)
//! or an orbit camera (TranslateOffsetOrbit).
using TranslationDeltaFn = AZStd::function<void(Camera& camera, const AZ::Vector3& delta)>;
inline void TranslatePivot(Camera& camera, const AZ::Vector3& delta)
//! Update the pivot camera position.
//! @note delta will need to have been transformed to world space, e.g. To move the camera right, (1, 0, 0) must
//! first be transformed by the orientation of the camera before being applied to m_pivot.
inline void TranslatePivotLook(Camera& camera, const AZ::Vector3& delta)
{
camera.m_pivot += delta;
}
inline void TranslateOffset(Camera& camera, const AZ::Vector3& delta)
//! Update the offset camera position.
//! @note delta still needs to be transformed to world space (as with TranslatePivotLook) but internally this is undone
//! to be performed in local space when being applied to m_offset.
inline void TranslateOffsetOrbit(Camera& camera, const AZ::Vector3& delta)
{
camera.m_offset += camera.View().TransformVector(delta);
}
@@ -409,7 +422,7 @@ namespace AzFramework
//! Axes to use while translating the camera.
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
//! TranslationAxes to use while in 'look' camera behavior (free look).
//! TranslationAxes to use while in 'look' or 'orbit' camera behavior.
inline AZ::Matrix3x3 LookTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -421,8 +434,8 @@ namespace AzFramework
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
//! TranslationAxes to use while in 'pivot' camera behavior.
inline AZ::Matrix3x3 PivotTranslation(const Camera& camera)
//! Optional TranslationAxes to use while in 'orbit' camera behavior.
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -535,11 +548,11 @@ namespace AzFramework
bool m_boost = false; //!< Is the translation speed currently being multiplied/scaled upwards.
};
//! A camera input to handle discrete scroll events that can modify the camera pivot distance.
class PivotDollyScrollCameraInput : public CameraInput
//! A camera input to handle discrete scroll events that can modify the camera offset.
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
PivotDollyScrollCameraInput();
OrbitDollyScrollCameraInput();
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -548,11 +561,11 @@ namespace AzFramework
AZStd::function<float()> m_scrollSpeedFn;
};
//! A camera input to handle motion deltas that can modify the camera pivot distance.
class PivotDollyMotionCameraInput : public CameraInput
//! A camera input to handle motion deltas that can modify the camera offset.
class OrbitDollyMotionCameraInput : public CameraInput
{
public:
explicit PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId);
explicit OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -569,10 +582,10 @@ namespace AzFramework
};
//! A camera input to handle discrete scroll events that can scroll (translate) the camera along its forward axis.
class ScrollTranslationCameraInput : public CameraInput
class LookScrollTranslationCameraInput : public CameraInput
{
public:
ScrollTranslationCameraInput();
LookScrollTranslationCameraInput();
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -583,40 +596,96 @@ namespace AzFramework
//! A camera input that doubles as its own set of camera inputs.
//! It is 'exclusive', so does not overlap with other sibling camera inputs - it runs its own set of camera inputs as 'children'.
class PivotCameraInput : public CameraInput
class OrbitCameraInput : public CameraInput
{
public:
using PivotFn = AZStd::function<AZ::Vector3(const AZ::Vector3& position, const AZ::Vector3& direction)>;
explicit PivotCameraInput(const InputChannelId& pivotChannelId);
explicit OrbitCameraInput(const InputChannelId& orbitChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override;
void SetPivotInputChannelId(const InputChannelId& pivotChanneId);
void SetOrbitInputChannelId(const InputChannelId& orbitChanneId);
Cameras m_pivotCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
Cameras m_orbitCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
//! Override the default behavior for how a pivot point is calculated.
void SetPivotFn(PivotFn pivotFn);
private:
InputChannelId m_pivotChannelId; //!< Input channel to begin the pivot camera input.
PivotFn m_pivotFn; //!< The pivot position to use for this pivot camera (how is the pivot point calculated/retrieved).
InputChannelId m_orbitChannelId; //!< Input channel to begin the orbit camera input.
PivotFn m_pivotFn; //!< The pivot position to use for this orbit camera (how is the pivot point calculated/retrieved).
};
inline void PivotCameraInput::SetPivotFn(PivotFn pivotFn)
inline void OrbitCameraInput::SetPivotFn(PivotFn pivotFn)
{
m_pivotFn = AZStd::move(pivotFn);
}
inline bool PivotCameraInput::Exclusive() const
inline bool OrbitCameraInput::Exclusive() const
{
return true;
}
//! Callback to use for FocusCameraInput when a free look camera is being used.
//! @note This is when offset is zero.
inline AZ::Vector3 FocusLook(float)
{
return AZ::Vector3::CreateZero();
}
//! Callback to use for FocusCameraInput when a orbit camera is being used.
//! @note This is when offset is non zero.
inline AZ::Vector3 FocusOrbit(const float length)
{
return AZ::Vector3::CreateAxisY(-length);
}
using FocusOffsetFn = AZStd::function<AZ::Vector3(float)>;
//! A focus behavior to align the camera view to the position returned by the pivot function.
//! @note This only alters the camera orientation, the translation is unaffected.
class FocusCameraInput : public CameraInput
{
public:
using PivotFn = AZStd::function<AZ::Vector3()>;
FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
//! Override the default behavior for how a pivot point is calculated.
void SetPivotFn(PivotFn pivotFn);
void SetFocusInputChannelId(const InputChannelId& focusChannelId);
private:
InputChannelId m_focusChannelId; //!< Input channel to begin the focus camera input.
Camera m_nextCamera;
PivotFn m_pivotFn;
FocusOffsetFn m_offsetFn;
};
//! Provides a CameraInput type that can be implemented without needing to create a new type deriving from CameraInput.
//! This can be very useful for specific use cases that are less generally applicable.
class CustomCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
//! HandleEvents delegates directly to m_handleEventsFn.
AZStd::function<bool(CameraInput&, const InputEvent&, const ScreenVector&, float)> m_handleEventsFn;
//! StepCamera delegates directly to m_stepCameraFn.
AZStd::function<Camera(CameraInput&, const Camera&, const ScreenVector&, float, float)> m_stepCameraFn;
};
//! Map from a generic InputChannel event to a camera specific InputEvent.
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework
@@ -167,6 +167,10 @@ set(FILES
Logging/MissingAssetLogger.cpp
Logging/MissingAssetLogger.h
Logging/MissingAssetNotificationBus.h
Matchmaking/IMatchmakingRequests.h
Matchmaking/MatchmakingRequests.cpp
Matchmaking/MatchmakingRequests.h
Matchmaking/MatchmakingNotifications.h
Scene/Scene.h
Scene/Scene.inl
Scene/Scene.cpp
@@ -181,8 +185,9 @@ set(FILES
Script/ScriptRemoteDebugging.cpp
Script/ScriptRemoteDebugging.h
Session/ISessionHandlingRequests.h
Session/ISessionRequests.cpp
Session/ISessionRequests.h
Session/SessionRequests.cpp
Session/SessionRequests.h
Session/SessionConfig.cpp
Session/SessionConfig.h
Session/SessionNotifications.h
@@ -29,7 +29,6 @@ ly_add_target(
AZ::AzCore
PUBLIC
AZ::GridMate
3rdParty::zlib
3rdParty::zstd
3rdParty::lz4
)
@@ -13,7 +13,6 @@
#include <AzCore/Android/Utils.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/functional.h>
#include <android/api-level.h>
@@ -42,10 +41,10 @@ namespace AZ
{
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourcePath[AZ_MAX_PATH_LEN];
char resolvedDestPath[AZ_MAX_PATH_LEN];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
char resolvedSourcePath[AZ::IO::MaxPathLength];
char resolvedDestPath[AZ::IO::MaxPathLength];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ::IO::MaxPathLength);
ResolvePath(destinationFilePath, resolvedDestPath, AZ::IO::MaxPathLength);
if (AZ::Android::Utils::IsApkPath(sourceFilePath) || AZ::Android::Utils::IsApkPath(destinationFilePath))
{
@@ -77,18 +76,17 @@ namespace AZ
{
ANDROID_IO_PROFILE_SECTION_ARGS("FindFiles:%s", filePath);
char resolvedPath[AZ_MAX_PATH_LEN];
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength];
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
AZStd::string pathWithoutSlash = RemoveTrailingSlash(resolvedPath);
bool isInAPK = AZ::Android::Utils::IsApkPath(pathWithoutSlash.c_str());
AZ::IO::FixedMaxPath tempBuffer;
if (isInAPK)
{
AZ::IO::FixedMaxPath strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str());
char tempBuffer[AZ_MAX_PATH_LEN] = {0};
AZ::Android::APKFileHandler::ParseDirectory(strippedPath.c_str(), [&](const char* name)
{
AZStd::string_view filenameView = name;
@@ -98,10 +96,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += name;
// if aliased, de-alias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
return false;
}
@@ -115,10 +112,6 @@ namespace AZ
if (dir != nullptr)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
// clear the errno state so we can distinguish between errors and end of stream
errno = 0;
struct dirent* entry = readdir(dir);
@@ -133,10 +126,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += entry->d_name;
// if aliased, de-alias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
break;
}
@@ -163,8 +155,8 @@ namespace AZ
Result LocalFileIO::CreatePath(const char* filePath)
{
char resolvedPath[AZ_MAX_PATH_LEN];
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength];
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
if (AZ::Android::Utils::IsApkPath(resolvedPath))
{
@@ -201,33 +193,5 @@ namespace AZ
mkdir(pathBuffer.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
return path && path[0] == '/';
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
if (AZ::Android::Utils::IsApkPath(path))
{
azstrncpy(absolutePath, maxLength, path, maxLength);
return true;
}
AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
if (!IsAbsolutePath(path))
{
// 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.
const char* result = realpath(path, absolutePath);
if (result)
{
return true;
}
}
azstrcpy(absolutePath, maxLength, path);
return IsAbsolutePath(absolutePath);
}
} // namespace IO
}//namespace AZ
@@ -10,7 +10,7 @@
#include <dirent.h>
#include <unistd.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/functional.h>
namespace AZ
@@ -19,11 +19,11 @@ namespace AZ
{
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourceFilePath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ_MAX_PATH_LEN);
char resolvedSourceFilePath[AZ::IO::MaxPathLength] = {0};
ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ::IO::MaxPathLength);
char resolvedDestinationFilePath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ_MAX_PATH_LEN);
char resolvedDestinationFilePath[AZ::IO::MaxPathLength] = {0};
ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ::IO::MaxPathLength);
// Use standard C++ method of file copy.
{
@@ -45,17 +45,15 @@ namespace AZ
Result LocalFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback)
{
char resolvedPath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength] = {0};
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
AZStd::string withoutSlash = RemoveTrailingSlash(resolvedPath);
DIR* dir = opendir(withoutSlash.c_str());
if (dir != nullptr)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
AZ::IO::FixedMaxPath tempBuffer;
errno = 0;
struct dirent* entry = readdir(dir);
@@ -70,10 +68,9 @@ namespace AZ
AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
foundFilePath += entry->d_name;
// if aliased, dealias!
azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
if (!callback(tempBuffer))
if (!callback(tempBuffer.c_str()))
{
break;
}
@@ -92,8 +89,8 @@ namespace AZ
Result LocalFileIO::CreatePath(const char* filePath)
{
char resolvedPath[AZ_MAX_PATH_LEN] = {0};
ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
char resolvedPath[AZ::IO::MaxPathLength] = {0};
ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
// create all paths up to that directory.
// its not an error if the path exists.
@@ -125,28 +122,5 @@ namespace AZ
mkdir(buf.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
return path && path[0] == '/';
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
if (!IsAbsolutePath(path))
{
// 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.
const char* result = realpath(path, absolutePath);
if (result)
{
return true;
}
}
azstrcpy(absolutePath, maxLength, path);
return IsAbsolutePath(absolutePath);
}
} // namespace IO
} // namespace AZ
@@ -47,7 +47,7 @@ namespace AZ
if (hFind != INVALID_HANDLE_VALUE)
{
// because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
// because the absolute path might actually be SHORTER than the alias ("D:/o3de" -> "@engroot@"), we need to
// use a static buffer here.
char tempBuffer[AZ_MAX_PATH_LEN];
do
@@ -133,36 +133,5 @@ namespace AZ
return SystemFile::CreateDir(buf.c_str()) ? ResultCode::Success : ResultCode::Error;
}
bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
{
char* result = _fullpath(absolutePath, path, maxLength);
size_t len = ::strlen(absolutePath);
if (len > 0)
{
// strip trailing slash
if (absolutePath[len - 1] == '/' || absolutePath[len - 1] == '\\')
{
absolutePath[len - 1] = 0;
}
// For some reason, at least on windows, _fullpath returns a lowercase drive letter even though other systems like Qt, use upper case.
if (len > 2)
{
if (absolutePath[1] == ':')
{
absolutePath[0] = (char)toupper(absolutePath[0]);
}
}
}
return result != nullptr;
}
bool LocalFileIO::IsAbsolutePath(const char* path) const
{
char drive[16] = { 0 };
_splitpath_s(path, drive, 16, nullptr, 0, nullptr, 0, nullptr, 0);
return strlen(drive) > 0;
}
} // namespace IO
}//namespace AZ
@@ -20,6 +20,15 @@
namespace AzFramework
{
// xcb-xkb does not provide a generic event type, so we define our own.
// These fields are enough to get to the xkbType field, which can then be
// read to typecast the event to the right concrete type.
struct XcbXkbGenericEventT
{
uint8_t response_type;
uint8_t xkbType;
};
XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice)
: InputDeviceKeyboard::Implementation(inputDevice)
{
@@ -39,19 +48,22 @@ namespace AzFramework
return;
}
XcbStdFreePtr<xcb_xkb_use_extension_reply_t> xkbUseExtensionReply{
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
};
if (!xkbUseExtensionReply)
int initializeXkbExtensionSuccess = xkb_x11_setup_xkb_extension(
connection,
1,
0,
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
nullptr,
nullptr,
&m_xkbEventCode,
nullptr
);
if (!initializeXkbExtensionSuccess)
{
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
return;
}
if (!xkbUseExtensionReply->supported)
{
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
return;
}
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
@@ -59,6 +71,43 @@ namespace AzFramework
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
const uint16_t affectMap =
XCB_XKB_MAP_PART_KEY_TYPES
| XCB_XKB_MAP_PART_KEY_SYMS
| XCB_XKB_MAP_PART_MODIFIER_MAP
| XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS
| XCB_XKB_MAP_PART_KEY_ACTIONS
| XCB_XKB_MAP_PART_KEY_BEHAVIORS
| XCB_XKB_MAP_PART_VIRTUAL_MODS
| XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP
;
const uint16_t selectedEvents =
XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY
| XCB_XKB_EVENT_TYPE_MAP_NOTIFY
| XCB_XKB_EVENT_TYPE_STATE_NOTIFY
;
XcbStdFreePtr<xcb_generic_error_t> error{xcb_request_check(
connection,
xcb_xkb_select_events(
connection,
/* deviceSpec = */ XCB_XKB_ID_USE_CORE_KBD,
/* affectWhich = */ selectedEvents,
/* clear = */ 0,
/* selectAll = */ selectedEvents,
/* affectMap = */ affectMap,
/* map = */ affectMap,
/* details = */ nullptr
)
)};
if (error)
{
AZ_Warning("ApplicationLinux", false, "failed to select notify events from XKB");
return;
}
m_initialized = true;
}
@@ -70,15 +119,17 @@ namespace AzFramework
bool XcbInputDeviceKeyboard::HasTextEntryStarted() const
{
return false;
return m_hasTextEntryStarted;
}
void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options)
{
m_hasTextEntryStarted = true;
}
void XcbInputDeviceKeyboard::TextEntryStop()
{
m_hasTextEntryStarted = false;
}
void XcbInputDeviceKeyboard::TickInputDevice()
@@ -93,30 +144,45 @@ namespace AzFramework
return;
}
switch (event->response_type & ~0x80)
const auto responseType = event->response_type & ~0x80;
if (responseType == XCB_KEY_PRESS)
{
case XCB_KEY_PRESS:
{
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
const auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
{
auto text = TextFromKeycode(m_xkbState.get(), keyPress->detail);
if (!text.empty())
{
QueueRawTextEvent(AZStd::move(text));
}
}
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
if (key)
if (const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail))
{
QueueRawKeyEvent(*key, true);
}
break;
}
case XCB_KEY_RELEASE:
else if (responseType == XCB_KEY_RELEASE)
{
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
else if (responseType == m_xkbEventCode)
{
const auto* xkbEvent = reinterpret_cast<XcbXkbGenericEventT*>(event);
switch (xkbEvent->xkbType)
{
case XCB_XKB_STATE_NOTIFY:
{
const auto* stateNotifyEvent = reinterpret_cast<xcb_xkb_state_notify_event_t*>(event);
UpdateState(stateNotifyEvent);
break;
}
}
}
}
@@ -268,4 +334,34 @@ namespace AzFramework
default: return nullptr;
}
}
AZStd::string XcbInputDeviceKeyboard::TextFromKeycode(xkb_state* state, xkb_keycode_t code)
{
// Find out how much of a buffer we need
const size_t size = xkb_state_key_get_utf8(state, code, nullptr, 0);
if (!size)
{
return {};
}
// xkb_state_key_get_utf8 will null-terminate the resulting string, and
// will truncate the result to `size - 1` if there is not enough space
// for the null byte. The first call returns the size of the resulting
// string without including the null byte. AZStd::string internally
// includes space for the null byte, but that is not included in its
// `size()`. xkb_state_key_get_utf8 will always set `buf[size - 1] =
// 0`, so add 1 to `chars.size()` to include that internal null byte in
// the string.
AZStd::string chars;
chars.resize_no_construct(size);
xkb_state_key_get_utf8(state, code, chars.data(), chars.size() + 1);
return chars;
}
void XcbInputDeviceKeyboard::UpdateState(const xcb_xkb_state_notify_event_t* state)
{
if (m_initialized)
{
xkb_state_update_mask(m_xkbState.get(), state->baseMods, state->latchedMods, state->lockedMods, state->baseGroup, state->latchedGroup, state->lockedGroup);
}
}
} // namespace AzFramework
@@ -13,6 +13,8 @@
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
struct xcb_xkb_state_notify_event_t;
namespace AzFramework
{
class XcbInputDeviceKeyboard
@@ -37,10 +39,16 @@ namespace AzFramework
private:
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const;
static AZStd::string TextFromKeycode(xkb_state* state, xkb_keycode_t code);
void UpdateState(const xcb_xkb_state_notify_event_t* state);
XcbUniquePtr<xkb_context, xkb_context_unref> m_xkbContext;
XcbUniquePtr<xkb_keymap, xkb_keymap_unref> m_xkbKeymap;
XcbUniquePtr<xkb_state, xkb_state_unref> m_xkbState;
int m_coreDeviceId{-1};
uint8_t m_xkbEventCode{0};
bool m_initialized{false};
bool m_hasTextEntryStarted{false};
};
} // namespace AzFramework
@@ -7,26 +7,24 @@
*/
#include <AzCore/PlatformIncl.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
namespace AZ
namespace AZ::IO
{
namespace IO
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
AZ::IO::FixedMaxPath resolvedSourcePath;
ResolvePath(resolvedSourcePath, sourceFilePath);
AZ::IO::FixedMaxPath resolvedDestPath;
ResolvePath(resolvedDestPath, destinationFilePath);
Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
char resolvedSourcePath[AZ_MAX_PATH_LEN];
ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
char resolvedDestPath[AZ_MAX_PATH_LEN];
ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedSourcePathW;
AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedDestPathW;
AZStd::to_wstring(resolvedSourcePathW, resolvedSourcePath.Native());
AZStd::to_wstring(resolvedDestPathW, resolvedDestPath.Native());
if (::CopyFileA(resolvedSourcePath, resolvedDestPath, false) == 0)
{
return ResultCode::Error;
}
return ResultCode::Success;
}
} // namespace IO
}//namespace AZ
return ::CopyFileW(resolvedSourcePathW.c_str(), resolvedDestPathW.c_str(), false) != 0 ? ResultCode::Success : ResultCode::Error;
}
}//namespace AZ::IO
@@ -112,9 +112,10 @@ namespace AzFramework
void ApplicationIos::PumpSystemEventLoopUntilEmpty()
{
SInt32 result;
const CFTimeInterval MaxSecondsInRunLoop = 0.001; // One millisecond
do
{
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_EPSILON, TRUE);
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, MaxSecondsInRunLoop, TRUE);
}
while (result == kCFRunLoopRunHandledSource);
}
@@ -26,7 +26,7 @@ protected:
}
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
fileIoBase->SetAlias("@assets@", m_tempDirectory.GetDirectory());
fileIoBase->SetAlias("@products@", m_tempDirectory.GetDirectory());
}
}
@@ -50,7 +50,7 @@ namespace UnitTest
m_application->Start({});
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
@@ -262,7 +262,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders.c_str()));
EXPECT_TRUE(archive->IsFileExist(fileInArchiveFile));
}
@@ -353,7 +353,7 @@ namespace UnitTest
// and be able to IMMEDIATELY
// * read the file in the subfolder
// * enumerate the folders (including that subfolder) even though they are 'virtual', not real folders on physical media
// * all of the above even though the mount point for the archive is @assets@ wheras the physical pack lives in @usercache@
// * all of the above even though the mount point for the archive is @products@ wheras the physical pack lives in @usercache@
// finally, we're going to repeat the above test but with files mounted with subfolders
// so for example, the pack will contain levelinfo.xml at the root of it
// but it will be mounted at a subfolder (levels/mylevel).
@@ -388,7 +388,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders));
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders));
EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml"));
EXPECT_TRUE(archive->IsFileExist("levels//mylevel//levelinfo.xml"));
@@ -484,7 +484,7 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(archive->IsFileExist("uniquename\\mylevel2\\levelinfo.xml"));
@@ -543,7 +543,7 @@ namespace UnitTest
archive->ClosePack(testArchivePath_withMountPoint);
// --- test to make sure that when you iterate only the first component is found, so bury it deep and ask for the root
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
found_mylevel_folder = false;
handle = archive->FindFirst("uniquename\\*");
@@ -574,9 +574,9 @@ namespace UnitTest
found_mylevel_folder = false;
// now make sure no red herrings appear
// for example, if a file is mounted at "@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
// and the file "@assets@\\somethingelse" is requested it should not be found
// in addition if the file "@assets@\\uniquename\\mylevel3" is requested it should not be found
// for example, if a file is mounted at "@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
// and the file "@products@\\somethingelse" is requested it should not be found
// in addition if the file "@products@\\uniquename\\mylevel3" is requested it should not be found
handle = archive->FindFirst("somethingelse\\*");
EXPECT_FALSE(static_cast<bool>(handle));
@@ -610,7 +610,7 @@ namespace UnitTest
cpfio.Remove(genericArchiveFileName);
// create the asset alias directory
cpfio.CreatePath("@assets@");
cpfio.CreatePath("@products@");
// create generic file
@@ -635,11 +635,11 @@ namespace UnitTest
pArchive.reset();
EXPECT_TRUE(IsPackValid(genericArchiveFileName));
EXPECT_TRUE(archive->OpenPack("@assets@", genericArchiveFileName));
EXPECT_TRUE(archive->OpenPack("@products@", genericArchiveFileName));
// ---- BARRAGE OF TESTS
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml")); // this should be hte same file
EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml")); // this should be hte same file
EXPECT_TRUE(!cpfio.Exists("@log@/testfile.xml"));
EXPECT_TRUE(!cpfio.Exists("@usercache@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -685,9 +685,9 @@ namespace UnitTest
EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
EXPECT_TRUE(!cpfio.IsDirectory("testfile.xml"));
EXPECT_TRUE(cpfio.IsDirectory("@assets@"));
EXPECT_TRUE(cpfio.IsDirectory("@products@"));
EXPECT_TRUE(cpfio.IsReadOnly("testfile.xml"));
EXPECT_TRUE(cpfio.IsReadOnly("@assets@/testfile.xml"));
EXPECT_TRUE(cpfio.IsReadOnly("@products@/testfile.xml"));
EXPECT_TRUE(!cpfio.IsReadOnly("@log@/unittesttemp/realfileforunittest.xml"));
@@ -714,10 +714,10 @@ namespace UnitTest
// find files test.
AZ::IO::FixedMaxPath resolvedTestFilePath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml")));
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@products@/testfile.xml")));
bool foundIt = false;
// note that this file exists only in the archive.
cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
cpfio.FindFiles("@products@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
{
AZ::IO::FixedMaxPath resolvedFoundPath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName)));
@@ -734,10 +734,10 @@ namespace UnitTest
// The following test is disabled because it will trigger an AZ_ERROR which will affect the outcome of this entire test
// EXPECT_NE(ResultCode::Success, cpfio.Remove("@assets@/testfile.xml")); // may not delete archive files
// EXPECT_NE(ResultCode::Success, cpfio.Remove("@products@/testfile.xml")); // may not delete archive files
// make sure it works with and without alias:
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml"));
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -788,22 +788,22 @@ namespace UnitTest
EXPECT_TRUE(archive->ClosePack(realNameBuf));
// change its actual location:
EXPECT_TRUE(archive->OpenPack("@assets@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@assets@/foundit.dat"));
EXPECT_TRUE(archive->OpenPack("@products@", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@products@/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location!
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat"));
EXPECT_TRUE(archive->ClosePack(realNameBuf));
// try sub-folders
EXPECT_TRUE(archive->OpenPack("@assets@/mystuff", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@assets@/mystuff/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat")); // do not find it in the previous locations!
EXPECT_TRUE(archive->OpenPack("@products@/mystuff", realNameBuf));
EXPECT_TRUE(archive->IsFileExist("@products@/mystuff/foundit.dat"));
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations!
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); // non-existent file
EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/notfoundit.dat")); // non-existent file
EXPECT_TRUE(archive->ClosePack(realNameBuf));
}
@@ -861,7 +861,7 @@ namespace UnitTest
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, ioBase);
const char* assetsPath = ioBase->GetAlias("@assets@");
const char* assetsPath = ioBase->GetAlias("@products@");
ASSERT_NE(nullptr, assetsPath);
auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds";
@@ -872,7 +872,7 @@ namespace UnitTest
// it normalizes the string, so the slashes flip and everything is lowercased.
AZ::IO::FixedMaxPath resolvedAddedPath;
AZ::IO::FixedMaxPath resolvedResourcePath;
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds"));
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@products@/textures/test.dds"));
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst()));
EXPECT_EQ(resolvedAddedPath, resolvedResourcePath);
reslist->Clear();
@@ -53,31 +53,31 @@ namespace UnitTest
};
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook);
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(m_pivotChannelId);
m_pivotCamera->SetPivotFn(
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(m_orbitChannelId);
m_orbitCamera->SetPivotFn(
[this](const AZ::Vector3&, const AZ::Vector3&)
{
return m_pivot;
});
auto pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
// set rotate speed to be a value that will scale motion delta (pixels moved) by a thousandth.
pivotRotateCamera->m_rotateSpeedFn = []()
orbitRotateCamera->m_rotateSpeedFn = []()
{
return 0.001f;
};
auto pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::PivotTranslation, AzFramework::TranslateOffset);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::OrbitTranslation, AzFramework::TranslateOffsetOrbit);
m_pivotCamera->m_pivotCameras.AddCamera(pivotRotateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(pivotTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonRotateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_pivotCamera);
m_cameraSystem->m_cameras.AddCamera(m_orbitCamera);
// these tests rely on using motion delta, not cursor positions (default is true)
AzFramework::ed_cameraSystemUseCursor = false;
@@ -87,7 +87,7 @@ namespace UnitTest
{
AzFramework::ed_cameraSystemUseCursor = true;
m_pivotCamera.reset();
m_orbitCamera.reset();
m_firstPersonRotateCamera.reset();
m_firstPersonTranslateCamera.reset();
@@ -97,11 +97,11 @@ namespace UnitTest
AllocatorsTestFixture::TearDown();
}
AzFramework::InputChannelId m_pivotChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::InputChannelId m_orbitChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::TranslateCameraInputChannelIds m_translateCameraInputChannelIds;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero();
//! This is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
@@ -109,17 +109,17 @@ namespace UnitTest
inline static const int PixelMotionDelta = 1570;
};
TEST_F(CameraInputFixture, BeginAndEndPivotCameraInputConsumesCorrectEvents)
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
{
// begin pivot camera
// begin orbit camera
const bool consumed1 = HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceKeyboard::Key::ModifierAltL,
AzFramework::InputChannel::State::Began });
// begin listening for pivot rotate (click detector) - event is not consumed
// begin listening for orbit rotate (click detector) - event is not consumed
const bool consumed2 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
// begin pivot rotate (mouse has moved sufficient distance to initiate)
// begin orbit rotate (mouse has moved sufficient distance to initiate)
const bool consumed3 = HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ 5 });
// end pivot (mouse up) - event is not consumed
// end orbit (mouse up) - event is not consumed
const bool consumed4 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended });
@@ -260,10 +260,10 @@ namespace UnitTest
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, PivotCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenPivoting)
TEST_F(CameraInputFixture, OrbitCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenOrbiting)
{
// create pathological lookAtFn that just returns the same position as the camera
m_pivotCamera->SetPivotFn(
m_orbitCamera->SetPivotFn(
[](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return position;
@@ -275,7 +275,7 @@ namespace UnitTest
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), expectedCameraPosition));
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
// verify the camera yaw has not changed and pivot point matches the expected camera position
using ::testing::FloatNear;
@@ -321,14 +321,14 @@ namespace UnitTest
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero()));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta)
TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-20.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3::CreateAxisY(-10.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
@@ -344,14 +344,14 @@ namespace UnitTest
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta)
TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3(10.0f, -10.0f, 0.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta });
@@ -802,6 +802,51 @@ namespace UnitTest
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AliasTest, GetAlias_LogsError_WhenAccessingDeprecatedAlias_Succeeds)
{
AZ::IO::LocalFileIO local;
AZ::IO::FixedMaxPathString aliasFolder;
EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
local.SetAlias("@test@", aliasFolder.c_str());
local.SetDeprecatedAlias("@deprecated@", "@test@");
local.SetDeprecatedAlias("@deprecatednonexistent@", "@nonexistent@");
local.SetDeprecatedAlias("@deprecatedsecond@", "@deprecated@");
local.SetDeprecatedAlias("@deprecatednonaliaspath@", aliasFolder);
AZ_TEST_START_TRACE_SUPPRESSION;
const char* testAlias = local.GetAlias("@test@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(0);
// Validate that accessing Deprecated Alias results in AZ_Error
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecated@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatednonexistent@");
EXPECT_EQ(nullptr, testAlias);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatedsecond@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_START_TRACE_SUPPRESSION;
testAlias = local.GetAlias("@deprecatednonaliaspath@");
ASSERT_NE(nullptr, testAlias);
EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
class SmartMoveTests
: public FolderFixture
{
@@ -27,7 +27,7 @@ namespace UnitTest
const char DummyFile[] = "dummy.txt";
const char AnotherDummyFile[] = "Foo/Dummy.txt";
const char DummyPattern[] = R"(^(.+)_([a-z]+)\..+$)";
const char MatchingPatternFile[] = "Foo/dummy_abc.txt";
const char NonMatchingPatternFile[] = "Foo/dummy_a8c.txt";
@@ -75,7 +75,7 @@ namespace UnitTest
: public AllocatorsFixture
{
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
@@ -89,7 +89,7 @@ namespace UnitTest
const char* testAssetRoot = m_tempDirectory.GetDirectory();
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
@@ -98,7 +98,7 @@ namespace UnitTest
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO.get());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", testAssetRoot);
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", testAssetRoot);
m_data->m_excludeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Exclude);
m_data->m_includeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Include);
@@ -114,7 +114,7 @@ namespace UnitTest
AZStd::vector<AZStd::string> includedWildcardTags = { DummyFileTags[DummyFileTagIndex::GIdx] };
EXPECT_TRUE(m_data->m_fileTagManager.AddFilePatternTags(DummyWildcard, FilePatternType::Wildcard, FileTagType::Include, includedWildcardTags).IsSuccess());
AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", ExcludeFile, FileTagAsset::Extension()).c_str(), m_data->m_excludeFile);
AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", IncludeFile, FileTagAsset::Extension()).c_str(), m_data->m_includeFile);
@@ -184,7 +184,7 @@ namespace UnitTest
TEST_F(FileTagTest, FileTags_QueryByAbsoluteFilePath_Valid)
{
AZStd::string absoluteDummyFilePath = DummyFile;
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
AZStd::set<AZStd::string> tags = m_data->m_excludeFileQueryManager->GetTags(absoluteDummyFilePath);
@@ -196,7 +196,7 @@ namespace UnitTest
ASSERT_EQ(tags.size(), 0);
AZStd::string absoluteAnotherDummyFilePath = AnotherDummyFile;
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
tags = m_data->m_includeFileQueryManager->GetTags(absoluteAnotherDummyFilePath);
ASSERT_EQ(tags.size(), 2);
@@ -213,7 +213,7 @@ namespace UnitTest
// Set the customized alias
AZStd::string customizedAliasFilePath;
const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
AzFramework::StringFunc::AssetDatabasePath::Join(assetsAlias, "foo", customizedAliasFilePath);
AZ::IO::FileIOBase::GetInstance()->SetAlias("@customizedalias@", customizedAliasFilePath.c_str());
@@ -305,7 +305,7 @@ namespace UnitTest
m_data->m_excludeFileQueryManager->ClearData();
EXPECT_TRUE(m_data->m_excludeFileQueryManager->Load(m_data->m_excludeFile));
AZStd::set<AZStd::string> outputTags = m_data->m_excludeFileQueryManager->GetTags(MatchingWildcardFile);
EXPECT_EQ(outputTags.size(), 2);
@@ -6,81 +6,19 @@
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzTest/Utils.h>
#include <AzFramework/Application/Application.h>
namespace UnitTest
{
using namespace AZ;
class FileIOBaseRAII
{
public:
FileIOBaseRAII(AZ::IO::FileIOBase& fileIO)
: m_prevFileIO(AZ::IO::FileIOBase::GetInstance())
{
AZ::IO::FileIOBase::SetInstance(&fileIO);
}
~FileIOBaseRAII()
{
AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
}
private:
AZ::IO::FileIOBase* m_prevFileIO;
};
class GenAppDescriptors
: public AllocatorsTestFixture
{
public:
void run()
{
struct Config
{
const char* platformName;
const char* configName;
const char* libSuffix;
};
ComponentApplication app;
SerializeContext serializeContext;
AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
AZ::Entity::Reflect(&serializeContext);
DynamicModuleDescriptor::Reflect(&serializeContext);
AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
const Config config = {"Platform", "Config", "libSuffix"};
AZ::ComponentApplication::Descriptor descriptor;
if (config.libSuffix && config.libSuffix[0])
{
FakePopulateModules(descriptor, config.libSuffix);
}
const AZStd::string filename = AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
IO::FileIOStream stream(filename.c_str(), IO::OpenMode::ModeWrite);
ObjectStream* objStream = ObjectStream::Create(&stream, serializeContext, ObjectStream::ST_XML);
bool descWriteOk = objStream->WriteClass(&descriptor);
(void)descWriteOk;
AZ_Warning("ComponentApplication", descWriteOk, "Failed to write memory descriptor to application descriptor file %s!", filename.c_str());
bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
(void)entityWriteOk;
AZ_Warning("ComponentApplication", entityWriteOk, "Failed to write system entity to application descriptor file %s!", filename.c_str());
bool flushOk = objStream->Finalize();
(void)flushOk;
AZ_Warning("ComponentApplication", flushOk, "Failed finalizing application descriptor file %s!", filename.c_str());
}
void FakePopulateModules(AZ::ComponentApplication::Descriptor& desc, const char* libSuffix)
{
static const char* modules[] =
@@ -100,10 +38,44 @@ namespace UnitTest
}
};
TEST_F(GenAppDescriptors, Test)
TEST_F(GenAppDescriptors, WriteDescriptor_ToXML_Succeeds)
{
AZ::IO::LocalFileIO fileIO;
FileIOBaseRAII restoreFileIOScope(fileIO);
run();
struct Config
{
const char* platformName;
const char* configName;
const char* libSuffix;
};
AzFramework::Application app;
AZ::SerializeContext serializeContext;
AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
AZ::Entity::Reflect(&serializeContext);
AZ::DynamicModuleDescriptor::Reflect(&serializeContext);
AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
const Config config = {"Platform", "Config", "libSuffix"};
AZ::ComponentApplication::Descriptor descriptor;
if (config.libSuffix && config.libSuffix[0])
{
FakePopulateModules(descriptor, config.libSuffix);
}
AZ::Test::ScopedAutoTempDirectory tempDirectory;
const auto filename = AZ::IO::Path(tempDirectory.GetDirectory()) /
AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
AZ::IO::FileIOStream stream(filename.c_str(), AZ::IO::OpenMode::ModeWrite);
auto objStream = AZ::ObjectStream::Create(&stream, serializeContext, AZ::ObjectStream::ST_XML);
const bool descWriteOk = objStream->WriteClass(&descriptor);
EXPECT_TRUE(descWriteOk) << "Failed to write memory descriptor to application descriptor file " << filename.c_str() << "!";
const bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
EXPECT_TRUE(entityWriteOk) << "Failed to write system entity to application descriptor file " << filename.c_str() << "!";
const bool flushOk = objStream->Finalize();
EXPECT_TRUE(flushOk) << "Failed finalizing application descriptor file " << filename.c_str() << "!";
}
}
@@ -0,0 +1,17 @@
/*
* 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 <gmock/gmock.h>
#include <AzCore/std/string/string.h>
inline testing::PolymorphicMatcher<testing::internal::StrEqualityMatcher<AZStd::string>> StrEq(const AZStd::string& str)
{
return ::testing::MakePolymorphicMatcher(testing::internal::StrEqualityMatcher<AZStd::string>(str, true, true));
}
@@ -28,6 +28,10 @@ xcb_generic_event_t* xcb_poll_for_event(xcb_connection_t* c)
{
return MockXcbInterface::Instance()->xcb_poll_for_event(c);
}
xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t cookie)
{
return MockXcbInterface::Instance()->xcb_request_check(c, cookie);
}
// ----------------------------------------------------------------------------
// xcb-xkb
@@ -39,6 +43,10 @@ xcb_xkb_use_extension_reply_t* xcb_xkb_use_extension_reply(xcb_connection_t* c,
{
return MockXcbInterface::Instance()->xcb_xkb_use_extension_reply(c, cookie, e);
}
xcb_void_cookie_t xcb_xkb_select_events(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details)
{
return MockXcbInterface::Instance()->xcb_xkb_select_events(c, deviceSpec, affectWhich, clear, selectAll, affectMap, map, details);
}
// ----------------------------------------------------------------------------
// xkb-x11
@@ -46,7 +54,7 @@ int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t* connection)
{
return MockXcbInterface::Instance()->xkb_x11_get_core_keyboard_device_id(connection);
}
struct xkb_keymap* xkb_x11_keymap_new_from_device(struct xkb_context* context, xcb_connection_t* connection, int32_t device_id, enum xkb_keymap_compile_flags flags)
xkb_keymap* xkb_x11_keymap_new_from_device(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags)
{
return MockXcbInterface::Instance()->xkb_x11_keymap_new_from_device(context, connection, device_id, flags);
}
@@ -54,28 +62,58 @@ xkb_state* xkb_x11_state_new_from_device(xkb_keymap* keymap, xcb_connection_t* c
{
return MockXcbInterface::Instance()->xkb_x11_state_new_from_device(keymap, connection, device_id);
}
int xkb_x11_setup_xkb_extension(
xcb_connection_t* connection,
uint16_t major_xkb_version,
uint16_t minor_xkb_version,
xkb_x11_setup_xkb_extension_flags flags,
uint16_t* major_xkb_version_out,
uint16_t* minor_xkb_version_out,
uint8_t* base_event_out,
uint8_t* base_error_out)
{
return MockXcbInterface::Instance()->xkb_x11_setup_xkb_extension(
connection, major_xkb_version, minor_xkb_version, flags, major_xkb_version_out, minor_xkb_version_out, base_event_out,
base_error_out);
}
// ----------------------------------------------------------------------------
// xkbcommon
xkb_context* xkb_context_new(enum xkb_context_flags flags)
xkb_context* xkb_context_new(xkb_context_flags flags)
{
return MockXcbInterface::Instance()->xkb_context_new(flags);
}
void xkb_context_unref(xkb_context *context)
void xkb_context_unref(xkb_context* context)
{
return MockXcbInterface::Instance()->xkb_context_unref(context);
}
void xkb_keymap_unref(xkb_keymap *keymap)
void xkb_keymap_unref(xkb_keymap* keymap)
{
return MockXcbInterface::Instance()->xkb_keymap_unref(keymap);
}
void xkb_state_unref(xkb_state *state)
void xkb_state_unref(xkb_state* state)
{
return MockXcbInterface::Instance()->xkb_state_unref(state);
}
xkb_keysym_t xkb_state_key_get_one_sym(xkb_state *state, xkb_keycode_t key)
xkb_keysym_t xkb_state_key_get_one_sym(xkb_state* state, xkb_keycode_t key)
{
return MockXcbInterface::Instance()->xkb_state_key_get_one_sym(state, key);
}
int xkb_state_key_get_utf8(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)
{
return MockXcbInterface::Instance()->xkb_state_key_get_utf8(state, key, buffer, size);
}
xkb_state_component xkb_state_update_mask(
xkb_state* state,
xkb_mod_mask_t depressed_mods,
xkb_mod_mask_t latched_mods,
xkb_mod_mask_t locked_mods,
xkb_layout_index_t depressed_layout,
xkb_layout_index_t latched_layout,
xkb_layout_index_t locked_layout)
{
return MockXcbInterface::Instance()->xkb_state_update_mask(
state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout);
}
}
@@ -17,6 +17,7 @@
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#include "Printers.h"
@@ -35,6 +36,7 @@ struct xkb_keymap
struct xkb_state
{
xkb_mod_mask_t m_modifiers{};
};
class MockXcbInterface
@@ -48,7 +50,7 @@ public:
MockXcbInterface(MockXcbInterface&&) = delete;
MockXcbInterface& operator=(const MockXcbInterface&) = delete;
MockXcbInterface& operator=(MockXcbInterface&&) = delete;
~MockXcbInterface()
virtual ~MockXcbInterface()
{
self = nullptr;
}
@@ -59,22 +61,27 @@ public:
MOCK_CONST_METHOD2(xcb_connect, xcb_connection_t*(const char* displayname, int* screenp));
MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c));
MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c));
MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie));
// xcb-xkb
MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor));
MOCK_CONST_METHOD3(xcb_xkb_use_extension_reply, xcb_xkb_use_extension_reply_t*(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e));
MOCK_CONST_METHOD8(xcb_xkb_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details));
// xkb-x11
MOCK_CONST_METHOD1(xkb_x11_get_core_keyboard_device_id, int32_t(xcb_connection_t* connection));
MOCK_CONST_METHOD4(xkb_x11_keymap_new_from_device, xkb_keymap*(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags));
MOCK_CONST_METHOD3(xkb_x11_state_new_from_device, xkb_state*(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id));
MOCK_CONST_METHOD8(xkb_x11_setup_xkb_extension, int(xcb_connection_t* connection, uint16_t major_xkb_version, uint16_t minor_xkb_version, xkb_x11_setup_xkb_extension_flags flags, uint16_t* major_xkb_version_out, uint16_t* minor_xkb_version_out, uint8_t* base_event_out, uint8_t* base_error_out));
// xkbcommon
MOCK_CONST_METHOD1(xkb_context_new, xkb_context*(xkb_context_flags flags));
MOCK_CONST_METHOD1(xkb_context_unref, void(xkb_context* context));
MOCK_CONST_METHOD1(xkb_keymap_unref, void(xkb_keymap* keymap));
MOCK_CONST_METHOD1(xkb_state_unref, void(xkb_state* state));
MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state *state, xkb_keycode_t key));
MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state* state, xkb_keycode_t key));
MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size));
MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout));
private:
static inline MockXcbInterface* self = nullptr;
@@ -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 "XcbBaseTestFixture.h"
namespace AzFramework
{
void XcbBaseTestFixture::SetUp()
{
using testing::Return;
using testing::_;
testing::Test::SetUp();
EXPECT_CALL(m_interface, xcb_connect(_, _))
.WillOnce(Return(&m_connection));
EXPECT_CALL(m_interface, xcb_disconnect(&m_connection))
.Times(1);
}
} // namespace AzFramework
@@ -0,0 +1,29 @@
/*
* 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 <gtest/gtest.h>
#include <xcb/xcb.h>
#include "MockXcbInterface.h"
namespace AzFramework
{
// Sets up mock behavior for the xcb library, providing an xcb_connection_t that is returned from a call to xcb_connect
class XcbBaseTestFixture
: public testing::Test
{
public:
void SetUp() override;
protected:
testing::NiceMock<MockXcbInterface> m_interface;
xcb_connection_t m_connection{};
};
} // namespace AzFramework

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