Browse Source

Child allocator tagging for component system (#16968)

* Updating references to the AllocationsRecord::Mode enum

The references now explicit mention the "Mode" enum name itself.
This is needed for the next commit where the "Mode" member of the
AllocationsRecord class is being changed to a type alias for an
"AllocatorRecordMode" enum declared using AZ_ENUM to allow AZ Console
system to be able to convert an string specifying one of the `AllocationRecordMode` enum options to the actual enum type.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the ComponentApplication to support initalizing tracking of the allocator through a startup config file

The Component Application can now initialize the allocations settings using the following algorithm.

First it checks if there is an O3DE_STARTUP_CFG_FILE environment variable set.
If that is set that uses used as the startup config file

Next if the O3DE_STARTUP_CFG_FILE isn't set, a file of `<executable-directory>/Registry/startup.cfg` is checked for the startup config file
If the `<executable-directory>/Registry/startup.cfg` doesn't exist, the path of `~/.o3de/Registry/startup.cfg` is used for the startup config file.

Finally the startup config file location can be overridden with a new command line option of `--startup-cfg-file`
The command line option is parsed with using no heap allocations, before allocators are created.

If the startup config file is a valid file, it is opening up using the ConfigParser and checked for keys of the form "allocator_tracking_<allocator-name>"
The "allocator_tracking_<allocator-name>" is used to specifying the recording mode of the allocation records that should be used for the allocator with name "<allocator-name>"

For example given a cfg file with the following content
```
allocator_tracking_SystemAllocator = true
allocator_tracking_OSAllocator = RECORD_STACK_IF_NO_FILE_LINE
allocator_tracking_PoolAllocator = false
```

A mapping of allocator name to the AllocationRecordMode enum is registered with the AZ::AllocatorManager

1. The SystemAllocator has allocation tracking turned ON using the recording mode of `RECORD_FULL`
2. The OSAllocator has allocation traking turned ON using the recording mode of `RECORD_STACK_IF_NO_FILE_LINE`
3. The PoolAllocator has allocations tracking turned OFF and its recording mode set to `RECORD_NO_RECORDS`

The "allocator_tracking_<allocator-name>" setting can accept either a bool value or one of the options of the AllocationRecordMode located in AllocationsRecords.has
If a bool option is used, then the value of `true` maps to the AllocationRecordMode of `RECORD_FULL` and `false` maps to `RECORD_NO_RECORDS`

Two new Console commands of `sys_DumpAllocationRecordsSlow` and `sys_DumpAllocationRecordsInRange` have been added which can dump the allocation records of all allocators or a user specified allocator

The `sys_DumpAllocationRecordsSlow` command dumps ALL allocations of any specified allocator or all allocators if no allocator name is provided.
This function can take a long time to run (on the order of 10s of minutes) if dumping the allocation records of the SystemAllocator

The `sys_DumpAllocationRecordsInRange` command forces the user to specify a range of indices to cap how many allocations should be dumped from any following allocators if they are specified to the command or ALL allocators if not.
This is the better function to use if the allocator has a large number of records (on the order of 100000's)

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Update the AllocatorBase class to support specifying the flag to enable profiling on construction

The AllocatorBase also swaps the order of logic in PostCreate/PreDestroy.
The AllocationsRecords structure is now created before registering the allocator with the AllocatorManager and destroyed after unregistering the allocator with the AllocatorManager
This is needed as the AllocatorManager now checks a map of allocator name -> allocation tracking structure in its RegistryAllocator function to determine the record tracking mode for allocator

The ChildAllocatorSchema has been updated to inherit from the AllocatorBase class to allow it to automatically register with the AllocatorManager on creation.
It has also been provided AZ_RTTI information which allows derived allocator classes to override the allocator name via the RTTI system.

On a related change, the SimpleSchemaAllocator, now unregisters the schema it is managing with the AllocatorManager, as the SimpleSchemaAllocator itself registers with the AllocatorManager.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Adding RTTI to EMotionFX allocators and the RapidJSON allocator

This allows the those allocators to register their proper class name
with the AllocatorManager

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added new AZ_COMPONENT macros that allows specifying the allocator

The `AZ_COMPONENT_WITH_ALLOCATOR`, `AZ_COMPONENT_IMPL_WITH_ALLOCATOR`, `AZ_COMPONENT_DECL_WITH_ALLOCATOR` and `AZ_COMPONENT_IMPL_INLINE_WITH_ALLOCATOR` macro allows the user to specify a custom AZ Allocator for a derived component.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated Component derived classes which uses the AZ_COMPONENT_BASE macro

The AZ_COMPONENT_BASE macro now no longer specifies the AZ class
allocator of SystemAllocator and instead the AZ_CLASS_ALLOCATOR must be
explicitly used to set the class allocator.

All the classes have been updated to use the `AZ::ComponentAllocator`

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Adding a distinct Entity allocator class

The `AZ::Entity` and the `AZ::EntityId` class uses the Entity
allocator as their class allocator

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated references to `AZStd::vector<AZ::Component*>` and
`AZStd::vector<ComponentServiceType>` names.

As the ComponentDescriptor class has been updated to have their
`DependencyArrayType` and `StringWarningArray` type aliases to use the
new ComponentAllocator, the existing function signatures were updated to
either accept an `AZStd::span<AZ::Component* const>` or accept a
parameter using the type alias name itself.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Remove support for converting a boolean value to the
AllocationRecordMode enum

To set the allocation tracking mode to use via the startup.cfg file, either the name of an AllocationRecordMode must be specified or a numeric integer matching one of the enum values must be specified.

Valid values are as follows
```
allocation_tracking_SystemAllocator = RECORD_NO_RECORDS
allocation_tracking_SystemAllocator = 0 ; // Same as the RECORD_NO_RECORDS enum above
allocation_tracking_SystemAllocator = RECORD_STACK_NEVER
allocation_tracking_SystemAllocator = 1; // Same as RECORD_STACK_NEVER
allocation_tracking_SystemAllocator = RECORD_STACK_IF_NO_FILE_LINE
allocation_tracking_SystemAllocator = 2; // Same as RECORD_STACK_IF_NO_FILE_LINE
allocation_tracking_SystemAllocator = RECORD_FULL
allocation_tracking_SystemAllocator = 3; // Same as RECORD_FULL
```

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Re-purposed the AZ::Utils::GetDevWriteStoragePath function

The GetDevWriteStoragePath function will now read the
"/O3DE/Runtime/FilePaths/DevWriteStorage" key from the Settings Registry
and use that as the path that can developer files can be written to.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed the AllocatorWrappers.h header

The AllocatorGlobalWrapper and AllocatorPointerWrapper classes were the
same as the existing AZStdAlloc and ChildAllocatorSchema classes.

The AllocatorGlobalWRaper and AllocatorPointerWrapper classes however
did not support allocation record tracking of allocations made through
it's API.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AZStd container calls to allocate to cast the call to
allocate to void pointer first using static_cast before a
reinterpret_cast to the desired pointer type.

This change is in anticipation of the next couple of commits to update
the AZ IAllocator api allocate function to return struct encapsulating
the allocated pointer and the amount of bytes allocated instead of just
a void*.

The new struct of `AllocateAddress` supports an implicit conversion
t void* or an explicit conversion to any pointer type.
However to trigger the conversion operator a static_cast c-style cast
must be used. A reinterpret_cast will not use a class conversion
operator.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed printf format argument for printing allocation records to properly
use "%zu"

The byteSize parameter being output is a `size_t`, but "%d" was being
used which meant that any allocations above 4 GiB were log incorrectly.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AZ Allocator API

The `IAllocator::allocate` and `IAllocator::reallocate functions now returns a new struct called
AllocateAddress which is implicitly convertible to a void*.
The AllocateAddress struct encapsulates the allocated memory address
along with the actual amount of memory allocated by the underlying
allocator.

The `IAllocator::deallocate` function now returns a `size_t` containing
the amount of memory that was just de-allocated at the specified
address.

The ChildAllocatorSchema and SimpleAllocatorSchema classes have been
updated to use the new return values to track in an atomic integer the
amount of memory that was allocated when making calls through it's
interface.

It was returning the amount of allocated memory from the underlying
allocator, which is not very useful for diagnostic purposes.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the `sys_DumpAllocationRecords*` console commands

There are now the following console commands for dumping allocation
records
1. `sys_DumpAllocationRecordsToStdout` - Dumps either all allocator
   allocation records or any specified allocator allocation records to
   stdout
1. `sys_DumpAllocationRecordsToFile` - Dumps either all allocator
   allocation records or any specified allocator allocation records to
   a user specified file relative the current working directory
1. `sys_DumpAllocationRecordsToDevWriteStorage` - Dumps either all allocator
   allocation records or any specified allocator allocation records to
   `<DevWriteStorage>/allocation_records` directory. On host platforms
   such as Windows, Linux and MacOS, the `<DevWriteStorage>` directory
   is the same as the user directory underneath the project root.
   i.e `<project-path>/user`
   On mobile platforms this is the dedicated directory where installed
   bundle content can be written to.

Modified the `sys_DumpAllocators` command to better support CSV output
by removing the Window parameter and printing the total of active
allocators at the end of the output.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updating the engine framework api version to 1.2.0

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added a `O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE` CMake Cache variable

The `O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE` allows the user to override
whether they to allow checking for a startup config file for
initializing allocator tracking when a ComponentApplication is
initialized in O3DE.

The `O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE` is unset by default.
In that case the non-development build configurations of debug/profile
support checking for a startup.cfg on launch, and the release
configuration does not.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added a new `AZ_CHILD_ALLOCATOR_WITH_NAME` macro

The `AZ_CHILD_ALLOCATOR_WITH_NAME` macro fills out the boilerplate
associated with creating an AZ Allocator that is derived from the ChildAllocatorSchema.

The macro creates an allocator with the the "_ALLOCATOR_CLASS"
parameter to the macro as an allocator derived from the
ChildAllocatorSchemea and an AZStdAlloc alias for an AZStd compatible allocator made by concatenating the
"_ALLOCATOR_CLASS" parameter with the literal token of "_for_std_t".

Therfore `AZ_CHILD_ALLOCATOR_WITH_NAME(FooAllocator, ...)` would result in a type
identifier of `FooAllocator` and `FooAllocator_for_std_t` to be
available.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixing typos in comments and Console Commands description strings.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed output of allocation records for allocators with 0 allocations.

The `totalAllocation` variable which was bound to the `PrintAllocation`
lambda was invoked which doesn't occur in the scenario where there are
no allocation records.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated `ThreadPoolSchemea::DeAllocate` function to return pointer
allocation size from different thread

The DeAllocate function was returning 0 for a pointer de-allocated on
another thread because the pointer had yet to be deleted yet.
However this causes a mismatch in allocation sizes when tracking memory
due to the de-allocation size not being cataloged.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the RPI Builder to use the global BehaviorContext.

The RPI Builder now registers the Lua specific material functors and
classes with the Global Behavior Context and uses the associated global
ScriptContext to execute any Lua scripts.

This removes all the BehaviorContext and ScriptContext instances in the
Atom RPI gem.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Clear the global functions that the Lua Material functor calls

When the `ScriptContext::Execute` function loads and executes a Lua
script it adds any `functions` at script scope to the Lua global table.

The function that are added however are not remove from the global table
on subsequent invocations on `ScriptContext::Execute`, so if a new
script was loaded that didn't replace existing functions in the global
table, then attempting to invoke the existing function would call a
stale version of the function instead of not finding the function and
properly failing.

This was causing newer loaded lua scripts invoked in the `LuaMaterialFunctorSourceData` to call older scripts version of the `GetShaderOptionDependencies` function when the newer script didn't actually implement the function.

Therefore incorrect shared option names was being returned for the newer
script where it should have returned zero shader options.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added intermediate classes to the ChildAllocatorSchema

The ChildAllocatorSchema class template now inherits for a
ChildAllocatorSchemaBase which is used as a conduit to RTTI cast
IAllocators pointers to ChildAllocatorSchemas.

Also added a `GetParentAllocator` to the ChildAllocatorSchema which
allows access of the parent allocator schema.

The `sys_DumpAllocators` function uses the new
`ChildAllocatorSchemaBase::GetParentAllocator` when available to print
the name of the parent class.

Update the AzClothAllocator, RHISystemAllocator and Audio Gem allocators
to use the ChildAllocatorSchema to piggyback off of the System
Allocator.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updating base class in ChildAllocatorSchema constructors

Signed-off-by: lumberyard-employee-dm <[email protected]>

---------

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 1 year ago
parent
commit
212d3c4d32
100 changed files with 1985 additions and 1284 deletions
  1. 6 6
      Code/Editor/Lib/Tests/test_AzAssetBrowserRequestHandler.cpp
  2. 1 0
      Code/Framework/AzCore/AzCore/Component/Component.cpp
  3. 33 16
      Code/Framework/AzCore/AzCore/Component/Component.h
  4. 114 5
      Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
  5. 4 1
      Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
  6. 1 1
      Code/Framework/AzCore/AzCore/Component/Entity.cpp
  7. 3 3
      Code/Framework/AzCore/AzCore/Component/Entity.h
  8. 14 9
      Code/Framework/AzCore/AzCore/Component/EntityId.h
  9. 1 1
      Code/Framework/AzCore/AzCore/Component/EntityUtils.cpp
  10. 1 1
      Code/Framework/AzCore/AzCore/Component/EntityUtils.h
  11. 1 1
      Code/Framework/AzCore/AzCore/Component/NamedEntityId.h
  12. 7 7
      Code/Framework/AzCore/AzCore/DOM/DomValue.cpp
  13. 5 7
      Code/Framework/AzCore/AzCore/DOM/DomValue.h
  14. 1 5
      Code/Framework/AzCore/AzCore/JSON/RapidJsonAllocator.h
  15. 9 5
      Code/Framework/AzCore/AzCore/Memory/AllocationRecords.cpp
  16. 18 12
      Code/Framework/AzCore/AzCore/Memory/AllocationRecords.h
  17. 20 10
      Code/Framework/AzCore/AzCore/Memory/AllocatorBase.cpp
  18. 3 2
      Code/Framework/AzCore/AzCore/Memory/AllocatorBase.h
  19. 801 301
      Code/Framework/AzCore/AzCore/Memory/AllocatorManager.cpp
  20. 17 2
      Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h
  21. 1 1
      Code/Framework/AzCore/AzCore/Memory/AllocatorTrackingRecorder.cpp
  22. 0 149
      Code/Framework/AzCore/AzCore/Memory/AllocatorWrappers.h
  23. 116 11
      Code/Framework/AzCore/AzCore/Memory/ChildAllocatorSchema.h
  24. 107 184
      Code/Framework/AzCore/AzCore/Memory/HphaAllocator.cpp
  25. 3 3
      Code/Framework/AzCore/AzCore/Memory/HphaAllocator.h
  26. 64 4
      Code/Framework/AzCore/AzCore/Memory/IAllocator.h
  27. 1 1
      Code/Framework/AzCore/AzCore/Memory/Memory.h
  28. 3 3
      Code/Framework/AzCore/AzCore/Memory/Memory_fwd.h
  29. 15 12
      Code/Framework/AzCore/AzCore/Memory/OSAllocator.cpp
  30. 4 3
      Code/Framework/AzCore/AzCore/Memory/OSAllocator.h
  31. 42 41
      Code/Framework/AzCore/AzCore/Memory/PoolAllocator.cpp
  32. 10 14
      Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h
  33. 60 23
      Code/Framework/AzCore/AzCore/Memory/SimpleSchemaAllocator.h
  34. 11 12
      Code/Framework/AzCore/AzCore/Memory/SystemAllocator.cpp
  35. 4 3
      Code/Framework/AzCore/AzCore/Memory/SystemAllocator.h
  36. 2 2
      Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp
  37. 2 2
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
  38. 12 12
      Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h
  39. 18 0
      Code/Framework/AzCore/AzCore/Utils/Utils.cpp
  40. 10 5
      Code/Framework/AzCore/AzCore/Utils/Utils.h
  41. 10 10
      Code/Framework/AzCore/AzCore/std/containers/deque.h
  42. 2 2
      Code/Framework/AzCore/AzCore/std/containers/forward_list.h
  43. 11 11
      Code/Framework/AzCore/AzCore/std/containers/list.h
  44. 2 2
      Code/Framework/AzCore/AzCore/std/containers/rbtree.h
  45. 26 26
      Code/Framework/AzCore/AzCore/std/containers/ring_buffer.h
  46. 11 11
      Code/Framework/AzCore/AzCore/std/containers/vector.h
  47. 1 1
      Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_queue.h
  48. 1 1
      Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stack.h
  49. 1 1
      Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stamped_queue.h
  50. 1 1
      Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stamped_stack.h
  51. 4 4
      Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h
  52. 2 2
      Code/Framework/AzCore/AzCore/std/sort.h
  53. 4 4
      Code/Framework/AzCore/AzCore/std/string/string.h
  54. 19 0
      Code/Framework/AzCore/CMakeLists.txt
  55. 1 1
      Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp
  56. 1 1
      Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp
  57. 1 1
      Code/Framework/AzCore/Platform/Linux/AzCore/Utils/Utils_Linux.cpp
  58. 1 1
      Code/Framework/AzCore/Platform/Mac/AzCore/Utils/Utils_Mac.cpp
  59. 1 1
      Code/Framework/AzCore/Platform/iOS/AzCore/Utils/Utils_iOS.mm
  60. 5 6
      Code/Framework/AzCore/Tests/AZStd/Hashed.cpp
  61. 12 13
      Code/Framework/AzCore/Tests/AZStd/Ordered.cpp
  62. 7 7
      Code/Framework/AzCore/Tests/Components.cpp
  63. 4 4
      Code/Framework/AzCore/Tests/Memory.cpp
  64. 1 5
      Code/Framework/AzCore/Tests/Memory/HphaAllocator.cpp
  65. 7 7
      Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp
  66. 1 1
      Code/Framework/AzCore/Tests/Module.cpp
  67. 1 1
      Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp
  68. 2 2
      Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.h
  69. 2 2
      Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp
  70. 4 4
      Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp
  71. 9 10
      Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionRequestBus.h
  72. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h
  73. 21 21
      Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp
  74. 2 2
      Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h
  75. 14 14
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityActionComponent.cpp
  76. 6 6
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityActionComponent.h
  77. 39 5
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp
  78. 9 20
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h
  79. 2 2
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ComponentMimeData.cpp
  80. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ComponentMimeData.h
  81. 16 16
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.cpp
  82. 2 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h
  83. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h
  84. 2 2
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.cpp
  85. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.h
  86. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp
  87. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h
  88. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h
  89. 2 2
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.cpp
  90. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h
  91. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp
  92. 11 11
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp
  93. 10 10
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx
  94. 4 4
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteWidget.cpp
  95. 5 5
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteWidget.hxx
  96. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp
  97. 7 7
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp
  98. 7 7
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx
  99. 101 101
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp
  100. 15 15
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx

+ 6 - 6
Code/Editor/Lib/Tests/test_AzAssetBrowserRequestHandler.cpp

@@ -125,14 +125,14 @@ namespace UnitTest
     {
     public:
         MOCK_METHOD2(AddComponentsToEntities, AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome(const AzToolsFramework::EntityIdList&, const AZ::ComponentTypeList&));
-        MOCK_METHOD1(CutComponents, void(const AZStd::vector<AZ::Component*>&));
-        MOCK_METHOD1(CopyComponents, void(const AZStd::vector<AZ::Component*>&));
+        MOCK_METHOD1(CutComponents, void(AZStd::span<AZ::Component* const>));
+        MOCK_METHOD1(CopyComponents, void(AZStd::span<AZ::Component* const>));
         MOCK_METHOD1(PasteComponentsToEntity, void (AZ::EntityId ));
         MOCK_METHOD0(HasComponentsToPaste, bool ());
-        MOCK_METHOD1(EnableComponents,void (const AZStd::vector<AZ::Component*>&));
-        MOCK_METHOD1(DisableComponents, void (const AZStd::vector<AZ::Component*>&));
-        MOCK_METHOD2(AddExistingComponentsToEntityById,  AzToolsFramework::EntityCompositionRequests::AddExistingComponentsOutcome(const AZ::EntityId&, const AZStd::vector<AZ::Component*>&));
-        MOCK_METHOD1(RemoveComponents,AzToolsFramework::EntityCompositionRequests::RemoveComponentsOutcome(const AZStd::vector<AZ::Component*>&));
+        MOCK_METHOD1(EnableComponents,void (AZStd::span<AZ::Component* const>));
+        MOCK_METHOD1(DisableComponents, void (AZStd::span<AZ::Component* const>));
+        MOCK_METHOD2(AddExistingComponentsToEntityById,  AzToolsFramework::EntityCompositionRequests::AddExistingComponentsOutcome(const AZ::EntityId&, AZStd::span<AZ::Component* const>));
+        MOCK_METHOD1(RemoveComponents,AzToolsFramework::EntityCompositionRequests::RemoveComponentsOutcome(AZStd::span<AZ::Component* const>));
         MOCK_METHOD1(ScrubEntities, AzToolsFramework::EntityCompositionRequests::ScrubEntitiesOutcome(const AzToolsFramework::EntityList&));
         MOCK_METHOD1(GetPendingComponentInfo, AzToolsFramework::EntityCompositionRequests::PendingComponentInfo(const AZ::Component*));
         MOCK_METHOD1(GetComponentName, AZStd::string(const AZ::Component*));

+ 1 - 0
Code/Framework/AzCore/AzCore/Component/Component.cpp

@@ -22,6 +22,7 @@ namespace AZ
     // Add definition for type info and runtime type information to component
     AZ_TYPE_INFO_WITH_NAME_IMPL(Component, "AZ::Component", "{EDFCB2CF-F75D-43BE-B26B-F35821B29247}");
     AZ_RTTI_NO_TYPE_INFO_IMPL(AZ::Component);
+    AZ_CLASS_ALLOCATOR_IMPL(AZ::Component, ComponentAllocator);
 
     //=========================================================================
     // Component

+ 33 - 16
Code/Framework/AzCore/AzCore/Component/Component.h

@@ -21,11 +21,14 @@
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/SystemAllocator.h> // Used as the allocator for most components.
+#include <AzCore/Memory/ChildAllocatorSchema.h>
 #include <AzCore/Outcome/Outcome.h>
 #include <AzCore/std/containers/unordered_set.h>
 
 namespace AZ
 {
+    AZ_CHILD_ALLOCATOR_WITH_NAME(ComponentAllocator, "ComponentAllocator", "{1F0B962C-2D9D-414C-ABF1-E93E83C07A18}", SystemAllocator);
+
     class Entity;
     class ComponentDescriptor;
 
@@ -48,6 +51,7 @@ namespace AZ
          */
         AZ_TYPE_INFO_WITH_NAME_DECL(Component);
         AZ_RTTI_NO_TYPE_INFO_DECL();
+        AZ_CLASS_ALLOCATOR_DECL
 
         /**
          * Initializes a component's internals.
@@ -259,8 +263,7 @@ namespace AZ
      * This macro is typically included in other macros, such as AZ_COMPONENT, to
      * create a component.
      */
-    #define AZ_COMPONENT_BASE(_ComponentClass, ...)                                                                                     \
-    AZ_CLASS_ALLOCATOR(_ComponentClass, AZ::SystemAllocator)                                                                            \
+    #define AZ_COMPONENT_BASE(_ComponentClass)                                                                                          \
     template<class Comp, class Void> friend class AZ::HasComponentReflect;                                                              \
     template<class Comp, class Void> friend class AZ::HasComponentProvidedServices;                                                     \
     template<class Comp, class Void> friend class AZ::HasComponentDependentServices;                                                    \
@@ -367,10 +370,14 @@ namespace AZ
      * You are not required to use the AZ_COMPONENT macro if you want to implement your own creation
      * functions by calling AZ_CLASS_ALLOCATOR, AZ_RTTI, and so on.
      */
-    #define AZ_COMPONENT(_ComponentClass, ...)                  \
-    AZ_RTTI(_ComponentClass, __VA_ARGS__, AZ::Component)        \
-    AZ_COMPONENT_INTRUSIVE_DESCRIPTOR_TYPE(_ComponentClass)     \
-    AZ_COMPONENT_BASE(_ComponentClass, __VA_ARGS__)
+
+    #define AZ_COMPONENT_WITH_ALLOCATOR(_ComponentClass, _Allocator, ...) \
+    AZ_RTTI(_ComponentClass, __VA_ARGS__, AZ::Component) \
+    AZ_COMPONENT_INTRUSIVE_DESCRIPTOR_TYPE(_ComponentClass) \
+    AZ_COMPONENT_BASE(_ComponentClass) \
+    AZ_CLASS_ALLOCATOR(_ComponentClass, _Allocator)
+
+    #define AZ_COMPONENT(_ComponentClass, ...) AZ_COMPONENT_WITH_ALLOCATOR(_ComponentClass, AZ::ComponentAllocator, __VA_ARGS__)
 
     //! Declares the required AzTypeInfo(GetO3deTypeName and GetO3deTypeId),
     //! RTTI (RTTI_TypeName and RTTI_Type),
@@ -442,7 +449,7 @@ namespace AZ
     //! 3. `AZ_COMPONENT_BASE_IMPL`: The call to `AZ_COMPONENT_BASE_IMPL` requires the template placeholder arguments to be supplied as variadic args,
     //! with the simple template name as the first parameter.
     //! It goes through the same transformation as the `AZ_TYPE_INFO_WITH_NAME_IMPL` macro to split out the template name
-    //! from the template placholder parameters via parenthesis unwrapping and using the `AZ_INTERNAL_USE_FIRST_ELEMENT` and `AZ_INTERNAL_SKIP_FIRST`
+    //! from the template placeholder parameters via parenthesis unwrapping and using the `AZ_INTERNAL_USE_FIRST_ELEMENT` and `AZ_INTERNAL_SKIP_FIRST`
     //! macros
     //!
     //! 4. `AZ_CLASS_ALLOCATOR_IMPL`: The call to `AZ_CLASS_ALLOCATOR_IMPL` requires template placeholder argument
@@ -459,7 +466,8 @@ namespace AZ
     //! ```
     //! The `AZ_CLASS` is a placeholder macro that is used to substitute `class` template parameters
     //! For non-type template parameters such as `size_t` or `int` the `AZ_AUTO` placeholder can be used
-    #define AZ_COMPONENT_IMPL(_ComponentClassOrTemplate, _DisplayName, _Uuid, ...) \
+
+    #define AZ_COMPONENT_IMPL_WITH_ALLOCATOR(_ComponentClassOrTemplate, _DisplayName, _Uuid, _Allocator, ...) \
     AZ_COMPONENT_MACRO_CALL(AZ_TYPE_INFO_WITH_NAME_IMPL, \
         AZ_USE_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)), \
         _DisplayName, \
@@ -467,17 +475,21 @@ namespace AZ
         AZ_VA_OPT(AZ_COMMA_SEPARATOR, AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate))) \
         AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
     ) \
-    AZ_RTTI_NO_TYPE_INFO_IMPL(_ComponentClassOrTemplate, __VA_ARGS__) \
+    AZ_RTTI_NO_TYPE_INFO_IMPL(_ComponentClassOrTemplate, __VA_ARGS__ AZ_VA_OPT(AZ_COMMA_SEPARATOR, __VA_ARGS__) AZ::Component) \
     AZ_COMPONENT_MACRO_CALL(AZ_COMPONENT_BASE_IMPL, \
         AZ_USE_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
         AZ_VA_OPT(AZ_COMMA_SEPARATOR, AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate))) \
         AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
     ) \
-    AZ_CLASS_ALLOCATOR_IMPL(_ComponentClassOrTemplate, AZ::SystemAllocator)
+    AZ_CLASS_ALLOCATOR_IMPL(_ComponentClassOrTemplate, _Allocator)
+
+    #define AZ_COMPONENT_IMPL(_ComponentClassOrTemplate, _DisplayName, _Uuid, ...) \
+        AZ_COMPONENT_IMPL_WITH_ALLOCATOR(_ComponentClassOrTemplate, _DisplayName, _Uuid, AZ::ComponentAllocator, __VA_ARGS__)
 
     //! Version of the AZ_COMPONENT_IMPL macro which can be used in a header or inline file
     //! which is included in multiple translation units
-    #define AZ_COMPONENT_IMPL_INLINE(_ComponentClassOrTemplate, _DisplayName, _Uuid, ...) \
+
+    #define AZ_COMPONENT_IMPL_INLINE_WITH_ALLOCATOR(_ComponentClassOrTemplate, _DisplayName, _Uuid, _Allocator, ...) \
     AZ_COMPONENT_MACRO_CALL(AZ_TYPE_INFO_WITH_NAME_IMPL_INLINE, \
         AZ_USE_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)), \
         _DisplayName, \
@@ -485,13 +497,16 @@ namespace AZ
         AZ_VA_OPT(AZ_COMMA_SEPARATOR, AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate))) \
         AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
     ) \
-    AZ_RTTI_NO_TYPE_INFO_IMPL_INLINE(_ComponentClassOrTemplate, __VA_ARGS__) \
+    AZ_RTTI_NO_TYPE_INFO_IMPL_INLINE(_ComponentClassOrTemplate, __VA_ARGS__ AZ_VA_OPT(AZ_COMMA_SEPARATOR, __VA_ARGS__) AZ::Component) \
     AZ_COMPONENT_MACRO_CALL(AZ_COMPONENT_BASE_IMPL_INLINE, \
         AZ_USE_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
         AZ_VA_OPT(AZ_COMMA_SEPARATOR, AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate))) \
         AZ_SKIP_FIRST_ARG(AZ_UNWRAP(_ComponentClassOrTemplate)) \
     ) \
-    AZ_CLASS_ALLOCATOR_IMPL_INLINE(_ComponentClassOrTemplate, AZ::SystemAllocator)
+    AZ_CLASS_ALLOCATOR_IMPL_INLINE(_ComponentClassOrTemplate, _Allocator)
+
+    #define AZ_COMPONENT_IMPL_INLINE(_ComponentClassOrTemplate, _DisplayName, _Uuid, ...) \
+        AZ_COMPONENT_IMPL_INLINE_WITH_ALLOCATOR(_ComponentClassOrTemplate, _DisplayName, _Uuid, AZ::ComponentAllocator, __VA_ARGS__)
 
     /**
      * Provides an interface through which the system can get the details of a component
@@ -502,17 +517,18 @@ namespace AZ
     class ComponentDescriptor
     {
     public:
+        AZ_CLASS_ALLOCATOR(ComponentDescriptor, ComponentAllocator);
 
         /**
          * The type of array that components use to specify provided, required, dependent,
          * and incompatible services.
          */
-        typedef AZStd::vector<ComponentServiceType> DependencyArrayType;
+        using DependencyArrayType = AZStd::vector<ComponentServiceType, ComponentAllocator_for_std_t>;
 
         /**
         * This type of array is used by the warning
         */
-        typedef AZStd::vector<AZStd::string> StringWarningArray;
+        using StringWarningArray = AZStd::vector<AZStd::string, ComponentAllocator_for_std_t>;
 
          /**
           * Creates an instance of the component.
@@ -631,6 +647,7 @@ namespace AZ
         : public ComponentDescriptorBus::Handler
     {
     public:
+        AZ_CLASS_ALLOCATOR(ComponentDescriptorHelper, ComponentAllocator);
         /**
          * Connects to the component descriptor bus.
          */
@@ -694,7 +711,7 @@ namespace AZ
          * Specifies that this class should use the AZ::SystemAllocator for memory
          * management by default.
          */
-        AZ_CLASS_ALLOCATOR(ComponentDescriptorDefault<ComponentClass>, SystemAllocator);
+        AZ_CLASS_ALLOCATOR(ComponentDescriptorDefault<ComponentClass>, ComponentAllocator);
 
         /**
          * Calls the static function AZ::ComponentDescriptor::Reflect if the user provided it.

+ 114 - 5
Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp

@@ -38,6 +38,8 @@
 #include <AzCore/RTTI/AttributeReader.h>
 #include <AzCore/RTTI/BehaviorContext.h>
 
+#include <AzCore/Settings/CommandLineParser.h>
+#include <AzCore/Settings/ConfigParser.h>
 #include <AzCore/Settings/SettingsRegistryImpl.h>
 #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
 #include <AzCore/Settings/SettingsRegistryScriptUtils.h>
@@ -135,6 +137,16 @@ namespace AZ::Internal
 #endif
     }
 
+    static constexpr bool CanUseStartupCfgFile()
+    {
+#if defined(O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE)
+        return bool{ O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE };
+#elif AZ_RELEASE_BUILD
+        return false;
+#else
+        return true;
+#endif
+    }
 } // namespace AZ::Internal
 
 namespace AZ
@@ -198,7 +210,7 @@ namespace AZ
         m_enableScriptReflection = true;
 
         m_memoryBlocksByteSize = 0;
-        m_recordingMode = Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE;
+        m_recordingMode = Debug::AllocationRecords::Mode::RECORD_STACK_IF_NO_FILE_LINE;
     }
 
     bool AppDescriptorConverter(SerializeContext& serialize, SerializeContext::DataElementNode& node)
@@ -377,10 +389,10 @@ namespace AZ
             if (EditContext* ec = serializeContext->GetEditContext())
             {
                 ec->Enum<Debug::AllocationRecords::Mode>("Debug::AllocationRecords::Mode", "Allocator recording mode")
-                    ->Value("No records", Debug::AllocationRecords::RECORD_NO_RECORDS)
-                    ->Value("No stack trace", Debug::AllocationRecords::RECORD_STACK_NEVER)
-                    ->Value("Stack trace when file/line missing", Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE)
-                    ->Value("Stack trace always", Debug::AllocationRecords::RECORD_FULL);
+                    ->Value("No records", Debug::AllocationRecords::Mode::RECORD_NO_RECORDS)
+                    ->Value("No stack trace", Debug::AllocationRecords::Mode::RECORD_STACK_NEVER)
+                    ->Value("Stack trace when file/line missing", Debug::AllocationRecords::Mode::RECORD_STACK_IF_NO_FILE_LINE)
+                    ->Value("Stack trace always", Debug::AllocationRecords::Mode::RECORD_FULL);
                 ec->Class<Descriptor>("System memory settings", "Settings for managing application memory usage")
                     ->ClassElement(Edit::ClassElements::EditorData, "")
                         ->Attribute(Edit::Attributes::AutoExpand, true)
@@ -451,6 +463,9 @@ namespace AZ
             m_argV = &m_commandLineBufferAddress;
         }
 
+        // Check for a bootstrap config file that contains settings for configuring the allocator tracking settings
+        InitializeAllocatorSettings(m_argC, m_argV);
+
         // we are about to create allocators, so make sure that
         // the descriptor is filled with at least the defaults:
         m_descriptor.m_recordingMode = AllocatorManager::Instance().GetDefaultTrackingMode();
@@ -646,6 +661,100 @@ namespace AZ
         }
     }
 
+    void ComponentApplication::InitializeAllocatorSettings(int argc, char** argv)
+    {
+        if constexpr (Internal::CanUseStartupCfgFile())
+        {
+            AZ::IO::FixedMaxPath startupCfgPath;
+
+            // First check if there is an "O3DE_STARTUP_CFG_FILE" environment variable set
+            // in the OS and use that as the startup cfg path
+            auto QueryStartupEnv = [](char* buffer, size_t size) -> size_t
+            {
+                constexpr const char* StartupCfgEnvKey = "O3DE_STARTUP_CFG_FILE";
+                auto getEnvOutcome = AZ::Utils::GetEnv(AZStd::span(buffer, size), StartupCfgEnvKey);
+                return getEnvOutcome ? getEnvOutcome.GetValue().size() : 0;
+            };
+            startupCfgPath.Native().resize_and_overwrite(startupCfgPath.Native().max_size(), QueryStartupEnv);
+
+            if (startupCfgPath.empty())
+            {
+                // Default to looking for startup config file at "<exe-directory>/startup.cfg" if the O3DE_STARTUP_CFG_FILE env is not set
+                startupCfgPath =
+                    AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / SettingsRegistryInterface::RegistryFolder / "startup.cfg";
+                if (!AZ::IO::SystemFile::Exists(startupCfgPath.c_str()))
+                {
+                    // If a <exe-directory>/startup.cfg doesn't exist, try to locate the startup config file at "~/.o3de/Registry/startup.cfg"
+                    startupCfgPath =
+                        AZ::IO::FixedMaxPath(AZ::Utils::GetO3deManifestDirectory()) / SettingsRegistryInterface::RegistryFolder / "startup.cfg";
+                }
+            }
+
+            Settings::CommandLineParserSettings commandLineParserSettings;
+            commandLineParserSettings.m_parseCommandLineEntryFunc = [&startupCfgPath](const Settings::CommandLineArgument& argument) -> bool
+            {
+                constexpr AZStd::string_view StartupFileOption = "startup-cfg-file";
+                if (argument.m_option == StartupFileOption)
+                {
+                    // Remove any double quotes surrounding the startup cfg path
+                    // and store it in the path variable
+                    startupCfgPath = Settings::UnquoteArgument(argument.m_value);
+                }
+
+                return true;
+            };
+
+            // Parses the command line arguments without using any heap allocations
+            // This attempts to read the startup config file from the command line
+            Settings::ParseCommandLine(argc, argv, commandLineParserSettings);
+
+            // Try to the open up the Startup config file
+            constexpr auto openMode = AZ::IO::OpenMode::ModeRead;
+            AZ::IO::SystemFileStream startupCfgStream(startupCfgPath.c_str(), openMode);
+            if (!startupCfgStream.IsOpen())
+            {
+                return;
+            }
+
+            auto& allocatorManager = AZ::AllocatorManager::Instance();
+            // Now parse the config file settings without using heap allocations as well
+            Settings::ConfigParserSettings configParserSettings;
+            configParserSettings.m_parseConfigEntryFunc =
+                [&allocatorManager](const Settings::ConfigParserSettings::ConfigEntry& configEntry) -> bool
+            {
+                // If a key in the config file is of the form with "allocator_tracking_<allocator_name>,
+                // try to convert the value into a boolean. If the value is true
+                // An entry of the allocator name is registered with the AllocatorManager
+                // which is used to turn on Allocator Tracking when an allocator with the specified
+                // name is registered
+                constexpr AZStd::string_view AllocatorTrackingPrefix = "allocator_tracking_";
+                if (AZ::StringFunc::StartsWith(configEntry.m_keyValuePair.m_key, AllocatorTrackingPrefix))
+                {
+                    AZStd::string_view allocatorName = configEntry.m_keyValuePair.m_key.substr(AllocatorTrackingPrefix.size());
+                    if (!allocatorName.empty())
+                    {
+                        // Attempt to convert the string directly to an AllocationRecords::Mode enum
+                        // This occurs in the case when the string matches the name of an enum option
+                        AZ::Debug::AllocationRecords::Mode mode = allocatorManager.GetDefaultTrackingMode();
+                        if (!AZ::ConsoleTypeHelpers::ToValue(mode, configEntry.m_keyValuePair.m_value))
+                        {
+                            // The tracking value was unable to be converted to an enum, so return true
+                            // to parse the next command line entry
+                            return true;
+                        }
+
+                        allocatorManager.SetTrackingForAllocator(allocatorName, mode);
+                    }
+                }
+
+                return true;
+            };
+
+            // Now uses the ConfigParser to parse the allocator settings
+            AZ::Settings::ParseConfigFile(startupCfgStream, configParserSettings);
+        }
+    }
+
     void ComponentApplication::RegisterCoreEventLogger()
     {
         // Check the Core metrics "CreateLogger" setting to determine if it should be created at all

+ 4 - 1
Code/Framework/AzCore/AzCore/Component/ComponentApplication.h

@@ -128,7 +128,7 @@ namespace AZ
             bool            m_enableScriptReflection;   //!< True if we want to enable reflection to the script context.
 
             AZ::u64         m_memoryBlocksByteSize;     //!< Memory block size in bytes.
-            Debug::AllocationRecords::Mode m_recordingMode; //!< When to record stack traces (default: AZ::Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE)
+            Debug::AllocationRecords::Mode m_recordingMode; //!< When to record stack traces (default: AZ::Debug::AllocationRecords::Mode::RECORD_STACK_IF_NO_FILE_LINE)
 
             ModuleDescriptorList m_modules;             //!< Dynamic modules used by the application.
                                                         //!< These will be loaded on startup.
@@ -293,6 +293,9 @@ namespace AZ
         void InitializeEventLoggerFactory();
         void InitializeLifecyleEvents(SettingsRegistryInterface& settingsRegistry);
         void InitializeConsole(SettingsRegistryInterface& settingsRegistry);
+        //! Reads any allocator settings from the either the <executable-directory>/startup.cfg
+        //! or a file specified by the last --startup-file-cfg=<path> option
+        void InitializeAllocatorSettings(int argc, char** argv);
 
         void RegisterCoreEventLogger();
 

+ 1 - 1
Code/Framework/AzCore/AzCore/Component/Entity.cpp

@@ -1088,7 +1088,7 @@ namespace AZ
         AZStd::vector<ComponentInfo*> candidateComponents;
 
         // Components in final sorted order
-        AZStd::vector<Component*> sortedComponents;
+        ComponentArrayType sortedComponents;
 
         // Tmp vectors to re-use when querying services
         ComponentDescriptor::DependencyArrayType servicesTmp;

+ 3 - 3
Code/Framework/AzCore/AzCore/Component/Entity.h

@@ -36,17 +36,17 @@ namespace AZ
     public:
 
         //! Specifies that this class should use AZ::SystemAllocator for memory management by default.
-        AZ_CLASS_ALLOCATOR(Entity, SystemAllocator);
+        AZ_CLASS_ALLOCATOR(Entity, EntityAllocator);
 
         //! Adds run-time type information to this class.
         AZ_RTTI(AZ::Entity, "{75651658-8663-478D-9090-2432DFCAFA44}");
 
         //! The type of array that contains the entity's components. 
         //! Used when iterating over components.
-        typedef AZStd::vector<Component*> ComponentArrayType;
+        using ComponentArrayType = AZStd::vector<Component*>;
 
         //! This type of array is used by the warning
-        typedef AZStd::vector<AZStd::string> StringWarningArray;
+        using StringWarningArray = AZStd::vector<AZStd::string>;
 
         //! The state of the entity and its components.
         //! @note An entity is only initialized once. It can be activated and deactivated multiple times.

+ 14 - 9
Code/Framework/AzCore/AzCore/Component/EntityId.h

@@ -9,6 +9,8 @@
 #define AZCORE_ENTITY_ID_H
 
 #include <AzCore/base.h>
+#include <AzCore/Memory/ChildAllocatorSchema.h>
+#include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/RTTI/TypeInfoSimple.h>
 #include <AzCore/std/string/string.h>
 
@@ -19,6 +21,8 @@
 
 namespace AZ
 {
+    AZ_CHILD_ALLOCATOR_WITH_NAME(EntityAllocator, "EntityAllocator", "{C3FA54B6-DAFC-44A8-98C2-7EB0ACF92BE8}", SystemAllocator);
+
     /**
      * Entity ID type.
      * Entity IDs are used to uniquely identify entities. Each component that is 
@@ -31,17 +35,18 @@ namespace AZ
         friend class Entity;
 
     public:
+        AZ_CLASS_ALLOCATOR(EntityId, EntityAllocator);
 
-         /**
-          * Invalid entity ID with a machine ID of 0 and the maximum timestamp.
-          */
-         static const u64 InvalidEntityId = 0x00000000FFFFFFFFull; 
+        /**
+         * Invalid entity ID with a machine ID of 0 and the maximum timestamp.
+         */
+        static constexpr u64 InvalidEntityId = 0x00000000FFFFFFFFull;
                  
-         /**
-          * Enables this class to be identified across modules and serialized into
-          * different contexts.
-          */
-         AZ_TYPE_INFO(EntityId, "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}");
+        /**
+         * Enables this class to be identified across modules and serialized into
+         * different contexts.
+         */
+        AZ_TYPE_INFO(EntityId, "{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}");
 
         /**
          * Creates an entity ID instance. 

+ 1 - 1
Code/Framework/AzCore/AzCore/Component/EntityUtils.cpp

@@ -297,7 +297,7 @@ namespace AZ::EntityUtils
     }
 
     void ConvertComponentVectorToMap(
-        const AZ::Entity::ComponentArrayType& components, AZStd::unordered_map<AZStd::string, AZ::Component*>& componentMapOut)
+        AZStd::span<AZ::Component* const> components, AZStd::unordered_map<AZStd::string, AZ::Component*>& componentMapOut)
     {
         for (AZ::Component* component : components)
         {

+ 1 - 1
Code/Framework/AzCore/AzCore/Component/EntityUtils.h

@@ -218,7 +218,7 @@ namespace AZ
         //! \param components Component vector to be converted.
         //! \param[out] componentMapOut Component map that stores a component alias as key and a component as value.
         void ConvertComponentVectorToMap(
-            const AZ::Entity::ComponentArrayType& components, AZStd::unordered_map<AZStd::string, AZ::Component*>& componentMapOut);
+            AZStd::span<AZ::Component* const> components, AZStd::unordered_map<AZStd::string, AZ::Component*>& componentMapOut);
 
     } // namespace EntityUtils
 }   // namespace AZ

+ 1 - 1
Code/Framework/AzCore/AzCore/Component/NamedEntityId.h

@@ -21,7 +21,7 @@ namespace AZ
         : public EntityId
     {
     public: 
-        AZ_CLASS_ALLOCATOR(NamedEntityId, AZ::SystemAllocator);
+        AZ_CLASS_ALLOCATOR(NamedEntityId, EntityAllocator);
         AZ_RTTI(NamedEntityId, "{27F37921-4B40-4BE6-B47B-7D3AB8682D58}", EntityId);
 
         static void Reflect(AZ::ReflectContext* context);

+ 7 - 7
Code/Framework/AzCore/AzCore/DOM/DomValue.cpp

@@ -24,7 +24,7 @@ namespace AZ::Dom
             }
             else
             {
-                refCountedPointer = AZStd::allocate_shared<T>(StdValueAllocator(), *refCountedPointer);
+                refCountedPointer = AZStd::allocate_shared<T>(ValueAllocator_for_std_t(), *refCountedPointer);
                 return refCountedPointer;
             }
         }
@@ -142,7 +142,7 @@ namespace AZ::Dom
     }
 
     Value::Value(AZStd::any opaqueValue)
-        : m_value(AZStd::allocate_shared<AZStd::any>(StdValueAllocator(), AZStd::move(opaqueValue)))
+        : m_value(AZStd::allocate_shared<AZStd::any>(ValueAllocator_for_std_t(), AZStd::move(opaqueValue)))
     {
     }
 
@@ -446,7 +446,7 @@ namespace AZ::Dom
 
     Value& Value::SetObject()
     {
-        m_value = AZStd::allocate_shared<Object>(StdValueAllocator());
+        m_value = AZStd::allocate_shared<Object>(ValueAllocator_for_std_t());
         return *this;
     }
 
@@ -749,7 +749,7 @@ namespace AZ::Dom
 
     Value& Value::SetArray()
     {
-        m_value = AZStd::allocate_shared<Array>(StdValueAllocator());
+        m_value = AZStd::allocate_shared<Array>(ValueAllocator_for_std_t());
         return *this;
     }
 
@@ -857,7 +857,7 @@ namespace AZ::Dom
 
     void Value::SetNode(AZ::Name name)
     {
-        m_value = AZStd::allocate_shared<Node>(StdValueAllocator(), AZStd::move(name));
+        m_value = AZStd::allocate_shared<Node>(ValueAllocator_for_std_t(), AZStd::move(name));
     }
 
     void Value::SetNode(AZStd::string_view name)
@@ -1048,7 +1048,7 @@ namespace AZ::Dom
         }
         else
         {
-            SharedStringType sharedString = AZStd::allocate_shared<SharedStringContainer>(StdValueAllocator(), value.begin(), value.end());
+            SharedStringType sharedString = AZStd::allocate_shared<SharedStringContainer>(ValueAllocator_for_std_t(), value.begin(), value.end());
             m_value = AZStd::move(sharedString);
         }
     }
@@ -1060,7 +1060,7 @@ namespace AZ::Dom
 
     void Value::SetOpaqueValue(AZStd::any value)
     {
-        m_value = AZStd::allocate_shared<AZStd::any>(StdValueAllocator(), AZStd::move(value));
+        m_value = AZStd::allocate_shared<AZStd::any>(ValueAllocator_for_std_t(), AZStd::move(value));
     }
 
     void Value::SetNull()

+ 5 - 7
Code/Framework/AzCore/AzCore/DOM/DomValue.h

@@ -10,7 +10,7 @@
 
 #include <AzCore/DOM/DomBackend.h>
 #include <AzCore/DOM/DomVisitor.h>
-#include <AzCore/Memory/AllocatorWrappers.h>
+#include <AzCore/Memory/ChildAllocatorSchema.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/PoolAllocator.h>
 #include <AzCore/Memory/SystemAllocator.h>
@@ -45,9 +45,7 @@ namespace AZ::Dom
 
     //! The allocator used by Value.
     //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside
-    AZ_ALLOCATOR_DEFAULT_GLOBAL_WRAPPER(ValueAllocator, AZ::SystemAllocator, "{5BC8B389-72C7-459E-B502-12E74D61869F}")
-
-    using StdValueAllocator = ValueAllocator;
+    AZ_CHILD_ALLOCATOR_WITH_NAME(ValueAllocator, "ValueAllocator", "{5BC8B389-72C7-459E-B502-12E74D61869F}", AZ::SystemAllocator);
 
     class Value;
 
@@ -55,11 +53,11 @@ namespace AZ::Dom
     class Array
     {
     public:
-        using ContainerType = AZStd::vector<Value, StdValueAllocator>;
+        using ContainerType = AZStd::vector<Value, ValueAllocator_for_std_t>;
         using Iterator = ContainerType::iterator;
         using ConstIterator = ContainerType::const_iterator;
         static constexpr const size_t ReserveIncrement = 4;
-        static_assert((ReserveIncrement & (ReserveIncrement - 1)) == 0, "ReserveIncremenet must be a power of 2");
+        static_assert((ReserveIncrement & (ReserveIncrement - 1)) == 0, "ReserveIncrement must be a power of 2");
 
         const ContainerType& GetValues() const;
 
@@ -77,7 +75,7 @@ namespace AZ::Dom
     {
     public:
         using EntryType = AZStd::pair<KeyType, Value>;
-        using ContainerType = AZStd::vector<EntryType, StdValueAllocator>;
+        using ContainerType = AZStd::vector<EntryType, ValueAllocator_for_std_t>;
         using Iterator = ContainerType::iterator;
         using ConstIterator = ContainerType::const_iterator;
         static constexpr const size_t ReserveIncrement = 8;

+ 1 - 5
Code/Framework/AzCore/AzCore/JSON/RapidJsonAllocator.h

@@ -13,11 +13,7 @@
 
 namespace AZ::JSON
 {
-    class RapidJSONAllocator : public AZ::ChildAllocatorSchema<AZ::OSAllocator>
-    {
-    public:
-        AZ_TYPE_INFO(RapidJSONAllocator, "{CCD24805-0E41-48CC-B92E-DB77F10FBEE3}");
-    };
+    AZ_CHILD_ALLOCATOR_WITH_NAME(RapidJSONAllocator, "RapidJSONAllocator", "{CCD24805-0E41-48CC-B92E-DB77F10FBEE3}", AZ::OSAllocator);
 } // namespace AZ::JSON
 
 

+ 9 - 5
Code/Framework/AzCore/AzCore/Memory/AllocationRecords.cpp

@@ -106,15 +106,17 @@ namespace AZ::Debug
         }
 
         Debug::AllocationRecordsType::pair_iter_bool iterBool;
+        size_t numRecords;
         {
             AZStd::scoped_lock lock(m_recordsMutex);
             iterBool = m_records.insert_key(address);
+            numRecords = m_records.size();
         }
 
         if (!iterBool.second)
         {
             // If that memory address was already registered, print the stack trace of the previous registration
-            PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels);
+            PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels, numRecords);
             AZ_Assert(iterBool.second, "Memory address 0x%p is already allocated and in the records!", address);
         }
 
@@ -490,7 +492,7 @@ namespace AZ::Debug
         }
         for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
         {
-            if (!cb(iter->first, iter->second, m_numStackLevels))
+            if (!cb(iter->first, iter->second, m_numStackLevels, recordsCopy.size()))
             {
                 break;
             }
@@ -535,17 +537,17 @@ namespace AZ::Debug
     // operator()
     // [9/29/2009]
     //=========================================================================
-    bool PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels)
+    bool PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels, size_t numRecords)
     {
         if (m_includeNameAndFilename && info.m_name)
         {
             AZ_Printf(
-                "Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %d Alignment: %d\n", info.m_name, address, info.m_byteSize,
+                "Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %zu Alignment: %u\n", info.m_name, address, info.m_byteSize,
                 info.m_alignment);
         }
         else
         {
-            AZ_Printf("Memory", "Allocation Addr: 0%p Size: %d Alignment: %d\n", address, info.m_byteSize, info.m_alignment);
+            AZ_Printf("Memory", "Allocation Addr: 0%p Size: %zu Alignment: %u\n", address, info.m_byteSize, info.m_alignment);
         }
 
         if (m_isDetailed)
@@ -575,6 +577,8 @@ namespace AZ::Debug
                     iFrame += numToDecode;
                 }
             }
+
+            AZ_Printf("Memory", "Total number of allocation records %zu\n", numRecords);
         }
         return true; // continue enumerating
     }

+ 18 - 12
Code/Framework/AzCore/AzCore/Memory/AllocationRecords.h

@@ -49,9 +49,10 @@ namespace AZ
          * \param void* allocation address
          * \param const AllocationInfo& reference to the allocation record.
          * \param unsigned char number of stack records/levels, if AllocationInfo::m_stackFrames != NULL.
+         * \param size_t total number of AllocationRecord objects being enumerated . This will remain the same throughout enumeration
          * \returns true if you want to continue traverse of the records and false if you want to stop.
          */
-        using AllocationInfoCBType = AZStd::function<bool (void*, const AllocationInfo&, unsigned char)>;
+        using AllocationInfoCBType = AZStd::function<bool (void*, const AllocationInfo&, unsigned char, size_t numRecords)>;
         /**
          * Example of records enumeration callback.
          */
@@ -60,7 +61,7 @@ namespace AZ
             PrintAllocationsCB(bool isDetailed = false, bool includeNameAndFilename = false)
                 : m_isDetailed(isDetailed), m_includeNameAndFilename(includeNameAndFilename) {}
 
-            bool operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels);
+            bool operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels, size_t numRecords);
 
             bool m_isDetailed;      ///< True to print allocation line and allocation callstack, otherwise false.
             bool m_includeNameAndFilename;  /// < True to print the source name and source filename, otherwise skip
@@ -80,6 +81,19 @@ namespace AZ
             u32     m_value;
         };
 
+        // Using the AZ_ENUM* macro to allow an AZ EnumTraits structure
+        // to be associated with the AllocationRecord Mode enum
+        // This is used by the ConsoleTypeHelpers to allow
+        // conversion from an enum Value string to an AllocationRecordMode
+        AZ_ENUM_WITH_UNDERLYING_TYPE(AllocationRecordMode, int,
+            RECORD_NO_RECORDS,              ///< Never record any information.
+            RECORD_STACK_NEVER,             ///< Never record stack traces. All other info is stored.
+            RECORD_STACK_IF_NO_FILE_LINE,   ///< Record stack if fileName and lineNum are not available. (default)
+            RECORD_FULL,                    ///< Always record the full stack.
+
+            RECORD_MAX                      ///< Must be last
+        );
+
         /**
         * Container for debug allocation records. This
         * records can be thread safe or not depending on your
@@ -93,16 +107,8 @@ namespace AZ
         */
         class AllocationRecords
         {
-         public:
-            enum Mode : int
-            {
-                RECORD_NO_RECORDS,              ///< Never record any information.
-                RECORD_STACK_NEVER,             ///< Never record stack traces. All other info is stored.
-                RECORD_STACK_IF_NO_FILE_LINE,   ///< Record stack if fileName and lineNum are not available. (default)
-                RECORD_FULL,                    ///< Always record the full stack.
-
-                RECORD_MAX                      ///< Must be last
-            };
+        public:
+            using Mode = AllocationRecordMode;
 
             /**
              * IMPORTANT: if isAllocationGuard

+ 20 - 10
Code/Framework/AzCore/AzCore/Memory/AllocatorBase.cpp

@@ -139,6 +139,12 @@ namespace
 
 namespace AZ
 {
+    AllocatorBase::AllocatorBase() = default;
+    AllocatorBase::AllocatorBase(bool enableProfiling)
+        : m_isProfilingActive{ enableProfiling }
+    {
+    }
+
     AllocatorBase::~AllocatorBase()
     {
         PreDestroy();
@@ -167,11 +173,6 @@ namespace AZ
 
     void AllocatorBase::PostCreate()
     {
-        if (m_registrationEnabled)
-        {
-            AllocatorManager::Instance().RegisterAllocator(this);
-        }
-
         const auto debugConfig = GetDebugConfig();
         if (!debugConfig.m_excludeFromDebugging)
         {
@@ -180,20 +181,29 @@ namespace AZ
                 GetName()));
         }
 
+        // Create the AllocationRecords before registering the allocator with the AllocatorManager
+        // The allocator manager stores a mapping of allocator name -> Allocation Record mode
+        // which is checked on registration to determine which tracking mode the Allocation Records should use
+        //
+        if (m_registrationEnabled)
+        {
+            AllocatorManager::Instance().RegisterAllocator(this);
+        }
+
         m_isReady = true;
     }
 
     void AllocatorBase::PreDestroy()
     {
-        if (m_records)
+        if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
         {
-            delete m_records;
-            SetRecords(nullptr);
+            AllocatorManager::Instance().UnRegisterAllocator(this);
         }
 
-        if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
+        if (m_records)
         {
-            AllocatorManager::Instance().UnRegisterAllocator(this);
+            delete m_records;
+            SetRecords(nullptr);
         }
 
         m_isReady = false;

+ 3 - 2
Code/Framework/AzCore/AzCore/Memory/AllocatorBase.h

@@ -22,8 +22,9 @@ namespace AZ
     class AllocatorBase : public IAllocator
     {
     protected:
-        AllocatorBase() = default;
-        ~AllocatorBase();
+        AllocatorBase();
+        explicit AllocatorBase(bool enableProfiling);
+        ~AllocatorBase() override;
 
     public:
         AZ_RTTI(AllocatorBase, "{E89B953E-FAB2-4BD0-A754-74AD5F8902F5}", IAllocator)

+ 801 - 301
Code/Framework/AzCore/AzCore/Memory/AllocatorManager.cpp

@@ -7,401 +7,901 @@
  */
 
 #include <AzCore/Console/IConsole.h>
+#include <AzCore/Date/DateFormat.h>
+#include <AzCore/Debug/StackTracer.h>
+#include <AzCore/IO/GenericStreams.h>
 #include <AzCore/Math/Crc.h>
+#include <AzCore/Memory/AllocationRecords.h>
 #include <AzCore/Memory/AllocatorManager.h>
+#include <AzCore/Memory/ChildAllocatorSchema.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/IAllocator.h>
-
 #include <AzCore/Memory/OSAllocator.h>
-#include <AzCore/Memory/AllocationRecords.h>
+#include <AzCore/Memory/SimpleSchemaAllocator.h>
+
+#include <AzCore/Platform.h>
 
 #include <AzCore/std/parallel/lock.h>
-#include <AzCore/std/smart_ptr/make_shared.h>
 #include <AzCore/std/containers/array.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzCore/Utils/Utils.h>
 
 namespace AZ
 {
+    static void sys_DumpAllocators([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
+    {
+        AllocatorManager::Instance().DumpAllocators();
+    }
+    AZ_CONSOLEFREEFUNC(sys_DumpAllocators, AZ::ConsoleFunctorFlags::Null, "Print memory allocator statistics.");
 
-static void sys_DumpAllocators([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
-{
-    AllocatorManager::Instance().DumpAllocators();
-}
-AZ_CONSOLEFREEFUNC(sys_DumpAllocators, AZ::ConsoleFunctorFlags::Null, "Print memory allocator statistics.");
+    // Provides a range of allocations to dump. The min value is inclusive and the max value is exclusive
+    // Therefore the range is [min, max)
+    struct AllocationDumpRange
+    {
+        size_t m_min = 0;
+        size_t m_max = AZStd::numeric_limits<size_t>::max();
+    };
 
-static EnvironmentVariable<AllocatorManager>& GetAllocatorManagerEnvVar()
-{
-    static EnvironmentVariable<AllocatorManager> s_allocManager;
-    return s_allocManager;
-}
+    // Add console command for outputting the Allocation records for all or a specified set of allocators with the provided names
+    static void DumpAllocationsForAllocatorHelper(
+        AZStd::span<AZStd::string_view const> allocatorNameArguments,
+        AZ::IO::GenericStream& printStream,
+        const AllocationDumpRange& allocationDumpRange = {})
+    {
+        auto& allocatorManager = AZ::AllocatorManager::Instance();
+        const int numAllocators = allocatorManager.GetNumAllocators();
+        // Garbage collect the allocations before dumping them
+        allocatorManager.GarbageCollect();
 
-static AllocatorManager* s_allocManagerDebug;  // For easier viewing in crash dumps
+        AZStd::vector<IAllocator*> allocatorsToDump;
+        for (int i = 0; i < numAllocators; i++)
+        {
+            auto* allocator = allocatorManager.GetAllocator(i);
+            const AZ::Debug::AllocationRecords* records = allocator->GetRecords();
+            if (records)
+            {
+                // If no allocator name arguments to the Console command have been supplied
+                // dump all allocators that record allocation records
+                if (allocatorNameArguments.empty())
+                {
+                    allocatorsToDump.emplace_back(allocator);
+                    // Skip back to the next allocator
+                    continue;
+                }
 
-//////////////////////////////////////////////////////////////////////////
-bool AllocatorManager::IsReady()
-{
-    return GetAllocatorManagerEnvVar().IsConstructed();
-}
-//////////////////////////////////////////////////////////////////////////
-void AllocatorManager::Destroy()
-{
-}
+                // If the allocator has records and it matches the name of one of the allocators
+                auto IsAllocatorInNameSet = [allocator](AZStd::string_view searchName)
+                {
+                    return AZ::StringFunc::Equal(searchName, allocator->GetName());
+                };
+                if (AZStd::any_of(allocatorNameArguments.begin(), allocatorNameArguments.end(), IsAllocatorInNameSet))
+                {
+                    allocatorsToDump.emplace_back(allocator);
+                }
+            }
+        }
 
-//////////////////////////////////////////////////////////////////////////
-// The only allocator manager instance.
-AllocatorManager& AllocatorManager::Instance()
-{
-    auto& allocatorManager = GetAllocatorManagerEnvVar();
-    if (!allocatorManager)
+        using AllocationString = AZStd::fixed_string<1024>;
+
+        static constexpr const char* MemoryTag = "mem";
+
+        size_t allocationCount{};
+        size_t totalAllocations{};
+
+        constexpr bool includeAllocationLineAndCallstack = true;
+        constexpr bool includeAllocatorNameAndSourceName = true;
+        auto PrintAllocations =
+            [&printStream,
+             includeAllocationLineAndCallstack = includeAllocationLineAndCallstack,
+             includeAllocatorNameAndSourceName = includeAllocatorNameAndSourceName,
+             &allocationDumpRange,
+             &allocationCount,
+             &totalAllocations](void* address, const Debug::AllocationInfo& info, unsigned int numStackLevels, size_t numRecords)
+        {
+            totalAllocations = numRecords;
+            AllocationString writeString;
+
+            // Only dump allocations in specified index range
+            if (allocationCount >= allocationDumpRange.m_min && allocationCount < allocationDumpRange.m_max)
+            {
+                if (includeAllocatorNameAndSourceName && info.m_name)
+                {
+                    writeString = AllocationString::format(
+                        R"(Allocation Name: "%s" Addr: 0%p Size: %zu Alignment: %u)"
+                        "\n",
+                        info.m_name,
+                        address,
+                        info.m_byteSize,
+                        info.m_alignment);
+                    printStream.Write(writeString.size(), writeString.data());
+                }
+                else
+                {
+                    writeString = AllocationString::format(
+                        "Allocation Addr: 0%p Size: %zu Alignment: %u\n", address, info.m_byteSize, info.m_alignment);
+                    printStream.Write(writeString.size(), writeString.c_str());
+                }
+
+                if (includeAllocationLineAndCallstack)
+                {
+                    // If there is no stack frame records, output the location where the allocation took place
+                    if (!info.m_stackFrames)
+                    {
+                        writeString = AllocationString::format(
+                            R"( "%s" (%d))"
+                            "\n",
+                            info.m_fileName,
+                            info.m_lineNum);
+                        printStream.Write(writeString.size(), writeString.c_str());
+                    }
+                    else
+                    {
+                        constexpr unsigned int MaxStackFramesToDecode = 30;
+                        Debug::SymbolStorage::StackLine decodedStackLines[MaxStackFramesToDecode];
+
+                        for (size_t currentStackFrame{}; numStackLevels > 0;)
+                        {
+                            // Decode up to lower of MaxStackFramesToDecode and the remaining number of stack frames per loop iteration
+                            unsigned int stackFramesToDecode =
+                                AZStd::GetMin(MaxStackFramesToDecode, static_cast<unsigned int>(numStackLevels));
+                            Debug::SymbolStorage::DecodeFrames(
+                                info.m_stackFrames + currentStackFrame, stackFramesToDecode, decodedStackLines);
+                            for (unsigned int i = 0; i < stackFramesToDecode; ++i)
+                            {
+                                if (info.m_stackFrames[currentStackFrame + i].IsValid())
+                                {
+                                    writeString = ' ';
+                                    writeString += decodedStackLines[i];
+                                    writeString += "\n";
+                                    printStream.Write(writeString.size(), writeString.c_str());
+                                }
+                            }
+                            numStackLevels -= stackFramesToDecode;
+                            currentStackFrame += stackFramesToDecode;
+                        }
+                    }
+                }
+            }
+
+            ++allocationCount;
+
+            return true;
+        };
+
+        // GetAllocationCount runs EnumerateAllocations for the only the first allocation record
+        // in order to get the current allocation count at the time of the call
+        size_t estimateAllocationCount{};
+        auto GetAllocationCount = [&estimateAllocationCount](void*, const Debug::AllocationInfo&, unsigned int, size_t numRecords)
+        {
+            estimateAllocationCount = numRecords;
+            return false;
+        };
+
+        // Stores an estimate of how many allocation records can be output per second
+        // This is based on empirical data of dumping the SystemAllocator in the Editor
+        // About 10000 records can be printed per second
+        constexpr size_t AllocationRecordsPerSecondEstimate = 10000;
+
+        // Iterate over each allocator to dump and print their allocation records
+        for (AZ::IAllocator* allocator : allocatorsToDump)
+        {
+            AZ::Debug::AllocationRecords* records = allocator->GetRecords();
+
+            estimateAllocationCount = 0;
+            // Get the allocation count at the time of the first EnumerateAllocations call
+            // NOTE: This is only an estimation of the count as the number of allocations
+            // can change between this call and the next call to EnumerationAllocations to print the records
+            records->EnumerateAllocations(GetAllocationCount);
+
+            using SecondsAsDouble = AZStd::chrono::duration<double>;
+            auto allocationRecordsSecondsEstimate = AZStd::chrono::ceil<AZStd::chrono::seconds>(SecondsAsDouble(estimateAllocationCount / double(AllocationRecordsPerSecondEstimate)));
+
+            AllocationString printString = AllocationString::format(
+                R"(Printing allocation records for allocator %s. Estimated time to print all records is %lld seconds)"
+                "\n",
+                allocator->GetName(),
+                static_cast<AZ::s64>(allocationRecordsSecondsEstimate.count()));
+            printStream.Write(printString.size(), printString.c_str());
+            // Also write the print string above to the Trace system
+            AZ::Debug::Trace::Instance().Output(MemoryTag, printString.c_str());
+
+            // Reset allocationCount and totalAllocationsCount to zero
+            // The PrintAllocations lambda is not called in the case where there are no recorded allocations
+            allocationCount = 0;
+            totalAllocations = 0;
+            auto startTime = AZStd::chrono::steady_clock::now();
+            records->EnumerateAllocations(PrintAllocations);
+            auto endTime = AZStd::chrono::steady_clock::now();
+            auto durationInMilliseconds = AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(endTime - startTime);
+            auto durationInSeconds = AZStd::chrono::ceil<AZStd::chrono::seconds>(durationInMilliseconds);
+
+            AllocationString allocationsPerSecond = u8"\u221e";
+            if (durationInSeconds.count() != 0)
+            {
+                // Use a double for the seconds duration in order to allow decimal values
+                AZStd::to_string(allocationsPerSecond, static_cast<AZ::s64>(totalAllocations / durationInSeconds.count()));
+            }
+
+            printString = AllocationString::format(
+                R"(Printed %zu allocations in %lld seconds for allocator "%s" (%s records per seconds))"
+                "\n",
+                totalAllocations,
+                static_cast<AZ::s64>(durationInSeconds.count()),
+                allocator->GetName(),
+                allocationsPerSecond.c_str());
+            printStream.Write(printString.size(), printString.c_str());
+            // Also write the amount of time taken to the console window
+            AZ::Debug::Trace::Instance().Output(MemoryTag, printString.c_str());
+        }
+    }
+
+    static void DumpAllocationsForAllocatorToStdout(const AZ::ConsoleCommandContainer& arguments)
     {
-        allocatorManager = Environment::CreateVariable<AllocatorManager>(AZ_CRC_CE("AZ::AllocatorManager::s_allocManager"));
+        // Dump allocations to stdout by default
+        AZ::IO::SystemFileStream printStream(AZ::IO::SystemFile::GetStdout());
+        DumpAllocationsForAllocatorHelper(arguments, printStream);
+    }
+    AZ_CONSOLEFREEFUNC("sys_DumpAllocationRecordsToStdout", DumpAllocationsForAllocatorToStdout, AZ::ConsoleFunctorFlags::Null,
+        "Print ALL individual allocations for the specified allocator to stdout.\n"
+        " If no allocator is specified, then all allocations are dumped\n"
+        "NOTE: This can be slow depending on the number of allocations\n"
+        R"(For better control of which allocations get printed, use the "sys_DumpAllocationRecordInRange" command)" "\n"
+        "usage: sys_DumpAllocationRecordsToStdout [<allocator name...>]\n"
+        "Ex. `sys_DumpAllocationRecordsToStdout SystemAllocator`");
+
+    static void DumpAllocationsForAllocatorToFile(const AZ::ConsoleCommandContainer& arguments)
+    {
+        using AllocationString = AZStd::fixed_string<1024>;
+        static constexpr const char* MemoryTag = "mem";
+
+        constexpr size_t DumpToFileMinArgumentCount = 1;
+
+        if (arguments.size() < DumpToFileMinArgumentCount)
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString(
+                    R"("sys_DumpAllocationRecordsToFile" command requires the first argument to specify the file path where the allocation records will be written.)"
+                    "\n")
+                    .c_str());
+            return;
+        }
 
-        s_allocManagerDebug = &(*allocatorManager);
+        // Get the file path from the first argument
+        AZStd::string_view filePath = arguments[0];
+        // Create a subrange to the indices that would contain the allocator name arguments
+        AZStd::span<AZStd::string_view const> allocatorNameArguments{ AZStd::next(arguments.begin(), DumpToFileMinArgumentCount), arguments.end() };
+
+        // Open the file stream
+        // if the file path is '-', then a stream to stdout is opened
+        constexpr auto openMode = AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite;
+
+        AZ::IO::SystemFileStream printStream;
+        if (filePath != "-")
+        {
+            printStream = AZ::IO::SystemFileStream(AZ::IO::FixedMaxPathString(filePath).c_str(), openMode);
+        }
+        else
+        {
+            printStream = AZ::IO::SystemFileStream(AZ::IO::SystemFile::GetStdout());
+        }
+
+        if (!printStream.IsOpen())
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString::format(
+                    R"("sys_DumpAllocationRecordsToFile" command could not open file path of "%s".)"
+                    "\n",
+                    printStream.GetFilename())
+                    .c_str());
+            return;
+        }
+        DumpAllocationsForAllocatorHelper(arguments, printStream);
+    }
+    AZ_CONSOLEFREEFUNC("sys_DumpAllocationRecordsToFile", DumpAllocationsForAllocatorToFile, AZ::ConsoleFunctorFlags::Null,
+        "Write ALL individual allocations for the specified allocator to the user specified file path.\n"
+        "The path is relative to the current working directory of the running application.\n"
+        "If no allocator is specified, then all allocations are dumped\n"
+        "NOTE: This can be slow depending on the number of allocations\n"
+        R"(For better control of which allocations get printed, use the "sys_DumpAllocationRecordInRange" command)" "\n"
+        "usage: sys_DumpAllocationRecordsToFile <file-path> [<allocator name...>]\n"
+        "Ex. `sys_DumpAllocationRecordsToFile /home/user/allocation_records.log SystemAllocator`");
+
+    static void DumpAllocationsForAllocatorToDevWriteStorage(const AZ::ConsoleCommandContainer& arguments)
+    {
+        using AllocationString = AZStd::fixed_string<1024>;
+        static constexpr const char* MemoryTag = "mem";
+
+        // Dump allocation records to <dev-write-storage>/allocation_records/records.<iso8601-timestamp>.<process-id>.log
+        // Use a ISO8601 timestamp + the process ID to provide uniqueness to the metrics json files
+        AZ::Date::Iso8601TimestampString utcTimestampString;
+        AZ::Date::GetFilenameCompatibleFormatNow(utcTimestampString);
+        // append process id
+        AZStd::fixed_string<32> processIdString;
+        AZStd::to_string(processIdString, AZ::Platform::GetCurrentProcessId());
+
+        // Append the relative file name portion to the <project-root>/user directory
+        const auto filePath = AZ::IO::FixedMaxPath{ AZ::Utils::GetDevWriteStoragePath() } / "allocation_records" /
+            AZ::IO::FixedMaxPathString::format("records.%s.%s.log", utcTimestampString.c_str(), processIdString.c_str());
+        constexpr auto openMode = AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite;
+        AZ::IO::SystemFileStream printStream(filePath.c_str(), openMode);
+        if (!printStream.IsOpen())
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString::format(
+                    R"("sys_DumpAllocationRecordsToFile" command could not open file path of "%s".)"
+                    "\n",
+                    printStream.GetFilename())
+                .c_str());
+            return;
+        }
+        DumpAllocationsForAllocatorHelper(arguments, printStream);
     }
+    AZ_CONSOLEFREEFUNC("sys_DumpAllocationRecordsToDevWriteStorage", DumpAllocationsForAllocatorToDevWriteStorage, AZ::ConsoleFunctorFlags::Null,
+        "Write ALL individual allocations for the specified allocator to <dev-write-storage>/allocation_records/records.<iso8601-timestamp>.<process-id>.log.\n"
+        "On host plaforms such as Windows/Linux/MacOS, <dev-write-storage> is equivalent to <project-root>/user directory.\n"
+        "On non-host platforms such as Android/iOS this folder is a writable directory based on those operating systems' Data container/storage APIs\n"
+        "If no allocator is specified, then all allocations are dumped\n"
+        "NOTE: This can be slow depending on the number of allocations\n"
+        R"(For better control of which allocations get printed, use the "sys_DumpAllocationRecordInRange" command)" "\n"
+        "usage: sys_DumpAllocationRecordsToDevWriteStorage [<allocator name...>]\n"
+        "Ex. `sys_DumpAllocationRecordsToDevWriteStorage SystemAllocator`");
+
+    static void DumpAllocationsForAllocatorInRange(const AZ::ConsoleCommandContainer& arguments)
+    {
+        using AllocationString = AZStd::fixed_string<1024>;
+        static constexpr const char* MemoryTag = "mem";
 
-    return *allocatorManager;
-}
-//////////////////////////////////////////////////////////////////////////
+        constexpr size_t rangeArgumentCount = 2;
 
-//=========================================================================
-// AllocatorManager
-// [9/11/2009]
-//=========================================================================
-AllocatorManager::AllocatorManager()
-    : m_profilingRefcount(0)
-{
-    m_numAllocators = 0;
-    m_isAllocatorLeaking = false;
-    m_defaultTrackingRecordMode = Debug::AllocationRecords::RECORD_NO_RECORDS;
-}
-
-//=========================================================================
-// ~AllocatorManager
-// [9/11/2009]
-//=========================================================================
-AllocatorManager::~AllocatorManager()
-{
-    InternalDestroy();
-}
-
-//=========================================================================
-// RegisterAllocator
-// [9/17/2009]
-//=========================================================================
-void
-AllocatorManager::RegisterAllocator(class IAllocator* alloc)
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    AZ_Assert(m_numAllocators < m_maxNumAllocators, "Too many allocators %d! Max is %d", m_numAllocators, m_maxNumAllocators);
+        if (arguments.size() < rangeArgumentCount)
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString(
+                    R"("sys_DumpAllocationRecordsInRange" command requires the first two arguments to specify a range of allocation records to dump.)"
+                    "\n")
+                    .c_str());
+            return;
+        }
 
-    for (size_t i = 0; i < m_numAllocators; i++)
+        // Try to convert the first argument to a number
+        AllocationDumpRange dumpRange;
+        if (!ConsoleTypeHelpers::ToValue(dumpRange.m_min, arguments[0]))
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString::format(
+                    R"(Unable to convert the min argument of "%.*s" to an integer .)"
+                    "\n", AZ_STRING_ARG(arguments[0]))
+                .c_str());
+            return;
+        }
+        if (!ConsoleTypeHelpers::ToValue(dumpRange.m_max, arguments[1]))
+        {
+            AZ_Error(
+                MemoryTag,
+                false,
+                AllocationString::format(
+                    R"(Unable to convert the max argument of "%.*s" to an integer .)"
+                    "\n", AZ_STRING_ARG(arguments[1]))
+                .c_str());
+            return;
+        }
+
+        // Create a subspan of the list of arguments where the allocator name starts from
+        // The first two arguments represent the range of allocations to print
+        AZStd::span<AZStd::string_view const> allocatorNameArguments{ AZStd::next(arguments.begin(), rangeArgumentCount), arguments.end() };
+
+        // Dump allocations to stdout by default
+        AZ::IO::SystemFileStream printStream(AZ::IO::SystemFile::GetStdout());
+        DumpAllocationsForAllocatorHelper(allocatorNameArguments, printStream, dumpRange);
+    }
+
+    AZ_CONSOLEFREEFUNC(
+        "sys_DumpAllocationRecordsInRange",
+        DumpAllocationsForAllocatorInRange,
+        AZ::ConsoleFunctorFlags::Null,
+        "Print allocations records in the specified index range of min to max for any allocations.\n"
+        " If no allocator is specified, then all registered allocator allocations records are dumped in the specified range\n"
+        "usage: sys_DumpAllocationsRecords <min-inclusive-index> <max-exclusive-index> [<allocator name...>]\n"
+        "Ex. Dump the first 100 allocations of the System Allocator\n"
+        "`sys_DumpAllocationsRecords 0 100 SystemAllocator'\n"
+        "Ex. Dump all but first 100 records of the OSAllocator\n"
+        "`sys_DumpAllocationsRecords 100 18446744073709552000 OSAllocator'\n"
+        "NOTE: smaller values for the max index can be specified and still print out all the allocations, as long as it larger than the "
+        "total number of allocation records\n");
+
+    static EnvironmentVariable<AllocatorManager>& GetAllocatorManagerEnvVar()
     {
-        AZ_Assert(m_allocators[i] != alloc, "Allocator %s (%s) registered twice!", alloc->GetName());
+        static EnvironmentVariable<AllocatorManager> s_allocManager;
+        return s_allocManager;
     }
 
-    m_allocators[m_numAllocators++] = alloc;
-    alloc->SetProfilingActive(m_defaultProfilingState);
-}
+    static AllocatorManager* s_allocManagerDebug; // For easier viewing in crash dumps
 
-//=========================================================================
-// InternalDestroy
-// [11/7/2018]
-//=========================================================================
-void
-AllocatorManager::InternalDestroy()
-{
-    while (m_numAllocators > 0)
+    //////////////////////////////////////////////////////////////////////////
+    bool AllocatorManager::IsReady()
+    {
+        return GetAllocatorManagerEnvVar().IsConstructed();
+    }
+    //////////////////////////////////////////////////////////////////////////
+    void AllocatorManager::Destroy()
     {
-        IAllocator* allocator = m_allocators[m_numAllocators - 1];
-        (void)allocator;
-        m_allocators[--m_numAllocators] = nullptr;
-        // Do not actually destroy the lazy allocator as it may have work to do during non-deterministic shutdown
     }
 
-    if (!m_isAllocatorLeaking)
+    //////////////////////////////////////////////////////////////////////////
+    // The only allocator manager instance.
+    AllocatorManager& AllocatorManager::Instance()
+    {
+        auto& allocatorManager = GetAllocatorManagerEnvVar();
+        if (!allocatorManager)
+        {
+            allocatorManager = Environment::CreateVariable<AllocatorManager>(AZ_CRC_CE("AZ::AllocatorManager::s_allocManager"));
+
+            s_allocManagerDebug = &(*allocatorManager);
+        }
+
+        return *allocatorManager;
+    }
+    //////////////////////////////////////////////////////////////////////////
+
+    //=========================================================================
+    // AllocatorManager
+    // [9/11/2009]
+    //=========================================================================
+    AllocatorManager::AllocatorManager()
+        : m_profilingRefcount(0)
     {
-        AZ_Assert(m_numAllocators == 0, "There are still %d registered allocators!", m_numAllocators);
+        m_numAllocators = 0;
+        m_isAllocatorLeaking = false;
+        m_defaultTrackingRecordMode = Debug::AllocationRecords::Mode::RECORD_NO_RECORDS;
     }
-}
 
-//=========================================================================
-// UnRegisterAllocator
-// [9/17/2009]
-//=========================================================================
-void
-AllocatorManager::UnRegisterAllocator(class IAllocator* alloc)
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+    //=========================================================================
+    // ~AllocatorManager
+    // [9/11/2009]
+    //=========================================================================
+    AllocatorManager::~AllocatorManager()
+    {
+        InternalDestroy();
+    }
 
-    for (int i = 0; i < m_numAllocators; ++i)
+    //=========================================================================
+    // RegisterAllocator
+    // [9/17/2009]
+    //=========================================================================
+    void AllocatorManager::RegisterAllocator(class IAllocator* alloc)
     {
-        if (m_allocators[i] == alloc)
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        AZ_Assert(m_numAllocators < m_maxNumAllocators, "Too many allocators %d! Max is %d", m_numAllocators, m_maxNumAllocators);
+
+        for (size_t i = 0; i < m_numAllocators; i++)
         {
-            --m_numAllocators;
-            m_allocators[i] = m_allocators[m_numAllocators];
+            AZ_Assert(m_allocators[i] != alloc, "Allocator %s (%s) registered twice!", alloc->GetName());
         }
-    }
-}
 
+        m_allocators[m_numAllocators++] = alloc;
+        alloc->SetProfilingActive(m_defaultProfilingState);
 
+        // If there is an allocator tracking config entry for the allocator store
+        // use its recording mode option to turn on allocation tracking
+        auto FindAllocatorTrackingConfigByName = [allocatorName = alloc->GetName()](const AllocatorTrackingConfig& trackingConfig)
+        {
+            // performs a case-insensitive compare of the allocator name against the tracking config value
+            return AZ::StringFunc::Equal(trackingConfig.m_allocatorName, allocatorName);
+        };
 
-//=========================================================================
-// LockAllocators
-// [11/21/2018]
-//=========================================================================
-AllocatorManager::AllocatorLock::~AllocatorLock()
-{
-}
+        if (auto foundTrackingConfigIt =
+                AZStd::find_if(m_allocatorTrackingConfigs.begin(), m_allocatorTrackingConfigs.end(), FindAllocatorTrackingConfigByName);
+            foundTrackingConfigIt != m_allocatorTrackingConfigs.end())
+        {
+            ConfigureTrackingForAllocator(alloc, *foundTrackingConfigIt);
+        }
+    }
 
-AZStd::shared_ptr<AllocatorManager::AllocatorLock>
-AllocatorManager::LockAllocators()
-{
-    class AllocatorLockImpl : public AllocatorLock
+    void AllocatorManager::ConfigureTrackingForAllocator(IAllocator* alloc, const AllocatorTrackingConfig& allocatorTrackingConfig)
     {
-    public:
-        AllocatorLockImpl(AZStd::mutex& mutex) : m_lock(mutex)
+        if (AZ::Debug::AllocationRecords* records = alloc->GetRecords(); records != nullptr)
         {
+            records->SetMode(allocatorTrackingConfig.m_recordMode);
+            if (allocatorTrackingConfig.m_recordMode != AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS)
+            {
+                // The ProfileAllocation function requires that the allocator has profiling turned on to record allocations
+                // Therefore profiling is turned on if the Allocation Record mode is set to record any kind of records
+                constexpr bool profileAllocations = true;
+                alloc->SetProfilingActive(profileAllocations);
+            }
         }
+    }
 
-        AZStd::unique_lock<AZStd::mutex> m_lock;
-    };
+    //=========================================================================
+    // InternalDestroy
+    // [11/7/2018]
+    //=========================================================================
+    void AllocatorManager::InternalDestroy()
+    {
+        while (m_numAllocators > 0)
+        {
+            IAllocator* allocator = m_allocators[m_numAllocators - 1];
+            (void)allocator;
+            m_allocators[--m_numAllocators] = nullptr;
+            // Do not actually destroy the lazy allocator as it may have work to do during non-deterministic shutdown
+        }
 
-    AZStd::shared_ptr<AllocatorLock> result = AZStd::allocate_shared<AllocatorLockImpl>(OSStdAllocator(), m_allocatorListMutex);
+        if (!m_isAllocatorLeaking)
+        {
+            AZ_Assert(m_numAllocators == 0, "There are still %d registered allocators!", m_numAllocators);
+        }
+    }
 
-    return result;
-}
+    //=========================================================================
+    // UnRegisterAllocator
+    // [9/17/2009]
+    //=========================================================================
+    void AllocatorManager::UnRegisterAllocator(class IAllocator* alloc)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
 
-//=========================================================================
-// GarbageCollect
-// [9/11/2009]
-//=========================================================================
-void
-AllocatorManager::GarbageCollect()
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        for (int i = 0; i < m_numAllocators; ++i)
+        {
+            if (m_allocators[i] == alloc)
+            {
+                --m_numAllocators;
+                m_allocators[i] = m_allocators[m_numAllocators];
+            }
+        }
+    }
 
-    // Allocators can use other allocators. When this happens, the dependent
-    // allocators are registered first. By running garbage collect on the
-    // allocators in reverse order, the ones with no dependencies are garbage
-    // collected first, which frees up more allocations for the dependent
-    // allocators to release.
-    for (int i = m_numAllocators - 1; i >= 0; --i)
+    //=========================================================================
+    // LockAllocators
+    // [11/21/2018]
+    //=========================================================================
+    AllocatorManager::AllocatorLock::~AllocatorLock()
     {
-        m_allocators[i]->GarbageCollect();
     }
-}
 
-//=========================================================================
-// AddOutOfMemoryListener
-// [12/2/2010]
-//=========================================================================
-bool
-AllocatorManager::AddOutOfMemoryListener(const OutOfMemoryCBType& cb)
-{
-    AZ_Warning("Memory", !m_outOfMemoryListener, "Out of memory listener was already installed!");
-    if (!m_outOfMemoryListener)
+    AZStd::shared_ptr<AllocatorManager::AllocatorLock> AllocatorManager::LockAllocators()
     {
-        m_outOfMemoryListener = cb;
-        return true;
+        class AllocatorLockImpl : public AllocatorLock
+        {
+        public:
+            AllocatorLockImpl(AZStd::mutex& mutex)
+                : m_lock(mutex)
+            {
+            }
+
+            AZStd::unique_lock<AZStd::mutex> m_lock;
+        };
+
+        AZStd::shared_ptr<AllocatorLock> result = AZStd::allocate_shared<AllocatorLockImpl>(OSStdAllocator(), m_allocatorListMutex);
+
+        return result;
     }
 
-    return false;
-}
+    //=========================================================================
+    // GarbageCollect
+    // [9/11/2009]
+    //=========================================================================
+    void AllocatorManager::GarbageCollect()
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+
+        // Allocators can use other allocators. When this happens, the dependent
+        // allocators are registered first. By running garbage collect on the
+        // allocators in reverse order, the ones with no dependencies are garbage
+        // collected first, which frees up more allocations for the dependent
+        // allocators to release.
+        for (int i = m_numAllocators - 1; i >= 0; --i)
+        {
+            m_allocators[i]->GarbageCollect();
+        }
+    }
 
-//=========================================================================
-// RemoveOutOfMemoryListener
-// [12/2/2010]
-//=========================================================================
-void
-AllocatorManager::RemoveOutOfMemoryListener()
-{
-    m_outOfMemoryListener = nullptr;
-}
-
-//=========================================================================
-// SetTrackingMode
-// [9/16/2011]
-//=========================================================================
-void
-AllocatorManager::SetTrackingMode(Debug::AllocationRecords::Mode mode)
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    for (int i = 0; i < m_numAllocators; ++i)
+    //=========================================================================
+    // AddOutOfMemoryListener
+    // [12/2/2010]
+    //=========================================================================
+    bool AllocatorManager::AddOutOfMemoryListener(const OutOfMemoryCBType& cb)
     {
-        Debug::AllocationRecords* records = m_allocators[i]->GetRecords();
-        if (records)
+        AZ_Warning("Memory", !m_outOfMemoryListener, "Out of memory listener was already installed!");
+        if (!m_outOfMemoryListener)
         {
-            records->SetMode(mode);
+            m_outOfMemoryListener = cb;
+            return true;
         }
+
+        return false;
     }
-}
 
-void
-AllocatorManager::EnterProfilingMode()
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    for (int i = 0; i < m_numAllocators; ++i)
+    //=========================================================================
+    // RemoveOutOfMemoryListener
+    // [12/2/2010]
+    //=========================================================================
+    void AllocatorManager::RemoveOutOfMemoryListener()
     {
-        m_allocators[i]->SetProfilingActive(true);
+        m_outOfMemoryListener = nullptr;
     }
-}
 
-void
-AllocatorManager::ExitProfilingMode()
-{
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    for (int i = 0; i < m_numAllocators; ++i)
+    //=========================================================================
+    // SetTrackingMode
+    // [9/16/2011]
+    //=========================================================================
+    void AllocatorManager::SetTrackingMode(Debug::AllocationRecords::Mode mode)
     {
-        m_allocators[i]->SetProfilingActive(false);
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        for (int i = 0; i < m_numAllocators; ++i)
+        {
+            Debug::AllocationRecords* records = m_allocators[i]->GetRecords();
+            if (records)
+            {
+                records->SetMode(mode);
+            }
+        }
     }
-}
 
-void
-AllocatorManager::DumpAllocators()
-{
-    static const char TAG[] = "mem";
+    void AllocatorManager::SetTrackingForAllocator(AZStd::string_view allocatorName, AZ::Debug::AllocationRecords::Mode recordMode)
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        auto FindAllocatorTrackingConfigByName = [&allocatorName](const AllocatorTrackingConfig& trackingConfig)
+        {
+            // performs a case-insensitive compare of the allocator name against the tracking config value
+            return AZ::StringFunc::Equal(trackingConfig.m_allocatorName, allocatorName);
+        };
+
+        // Store a pointer to the allocator tracking config that was either found
+        // or inserted into the allocator tracking config container
+        AllocatorTrackingConfig* trackingConfig{};
+
+        auto foundTrackingConfigIt =
+            AZStd::find_if(m_allocatorTrackingConfigs.begin(), m_allocatorTrackingConfigs.end(), FindAllocatorTrackingConfigByName);
+        if (foundTrackingConfigIt != m_allocatorTrackingConfigs.end())
+        {
+            // Update the recording mode
+            foundTrackingConfigIt->m_recordMode = recordMode;
+            trackingConfig = AZStd::to_address(foundTrackingConfigIt);
+        }
+        else
+        {
+            trackingConfig = &m_allocatorTrackingConfigs.emplace_back(AllocatorTrackingConfig{ AllocatorName(allocatorName), recordMode });
+        }
 
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    size_t totalUsedBytes = 0;
-    size_t totalReservedBytes = 0;
-    size_t totalConsumedBytes = 0;
+        // If the allocator is already registered with the Allocator Manager update the allocation record tracking
+        // to either start or stop tracking
+        auto FindAllocatorByName = [&allocatorName](IAllocator* allocator)
+        {
+            // performs a case-insensitive compare of the allocator name against the tracking config value
+            return AZ::StringFunc::Equal(allocator->GetName(), allocatorName);
+        };
 
-    memset(m_dumpInfo, 0, sizeof(m_dumpInfo));
+        auto allocatorsEnd = m_allocators + m_numAllocators;
+        if (auto foundAllocatorIt = AZStd::find_if(m_allocators, allocatorsEnd, FindAllocatorByName);
+            foundAllocatorIt != allocatorsEnd)
+        {
+            ConfigureTrackingForAllocator(*foundAllocatorIt, *trackingConfig);
+        }
+    }
 
-    AZ_Printf(TAG, "%d allocators active\n", m_numAllocators);
-    AZ_Printf(TAG, "Index,Name,Used kb,Reserved kb,Consumed kb\n");
+    bool AllocatorManager::RemoveTrackingForAllocator(AZStd::string_view allocatorName)
+    {
+        auto FindAllocatorByName = [&allocatorName](const AllocatorTrackingConfig& trackingConfig)
+        {
+            // performs a case-insensitive compare of the allocator name against the tracking config value
+            return AZ::StringFunc::Equal(trackingConfig.m_allocatorName, allocatorName);
+        };
+        return AZStd::erase_if(m_allocatorTrackingConfigs, FindAllocatorByName) != 0;
+    }
 
-    for (int i = 0; i < m_numAllocators; i++)
+    void AllocatorManager::EnterProfilingMode()
     {
-        IAllocator* allocator = GetAllocator(i);
-        const char* name = allocator->GetName();
-        size_t usedBytes = allocator->NumAllocatedBytes();
-        size_t reservedBytes = allocator->Capacity();
-        size_t consumedBytes = reservedBytes;
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        for (int i = 0; i < m_numAllocators; ++i)
+        {
+            m_allocators[i]->SetProfilingActive(true);
+        }
+    }
 
-        totalUsedBytes += usedBytes;
-        totalReservedBytes += reservedBytes;
-        totalConsumedBytes += consumedBytes;
-        m_dumpInfo[i].m_name = name;
-        m_dumpInfo[i].m_used = usedBytes;
-        m_dumpInfo[i].m_reserved = reservedBytes;
-        m_dumpInfo[i].m_consumed = consumedBytes;
-        AZ_Printf(TAG, "%d,%s,%.2f,%.2f,%.2f\n", i, name, usedBytes / 1024.0f, reservedBytes / 1024.0f, consumedBytes / 1024.0f);
+    void AllocatorManager::ExitProfilingMode()
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        for (int i = 0; i < m_numAllocators; ++i)
+        {
+            m_allocators[i]->SetProfilingActive(false);
+        }
     }
 
-    AZ_Printf(TAG, "-,Totals,%.2f,%.2f,%.2f\n", totalUsedBytes / 1024.0f, totalReservedBytes / 1024.0f, totalConsumedBytes / 1024.0f);
-}
-void AllocatorManager::GetAllocatorStats(size_t& allocatedBytes, size_t& capacityBytes, AZStd::vector<AllocatorStats>* outStats)
-{
-    allocatedBytes = 0;
-    capacityBytes = 0;
+    void AllocatorManager::DumpAllocators()
+    {
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        size_t totalUsedBytes = 0;
+        size_t totalReservedBytes = 0;
+        size_t totalConsumedBytes = 0;
+
+        memset(m_dumpInfo, 0, sizeof(m_dumpInfo));
 
-    AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
-    const int allocatorCount = GetNumAllocators();
+        AZ_Printf(AZ::Debug::NoWindow, "Index,Name,Used KiB,Reserved KiB,Consumed KiB,Parent Allocator\n");
+
+        for (int i = 0; i < m_numAllocators; i++)
+        {
+            IAllocator* allocator = GetAllocator(i);
+            const char* name = allocator->GetName();
+            size_t usedBytes = allocator->NumAllocatedBytes();
+            size_t reservedBytes = allocator->Capacity();
+            size_t consumedBytes = reservedBytes;
+            const char* parentName = "";
+            if (auto childAllocatorSchema = azrtti_cast<AZ::ChildAllocatorSchemaBase*>(allocator);
+                childAllocatorSchema != nullptr)
+            {
+                auto parentAllocator = childAllocatorSchema->GetParentAllocator();
+                parentName = parentAllocator != nullptr ? parentAllocator->GetName() : "";
+            }
+
+            totalUsedBytes += usedBytes;
+            totalReservedBytes += reservedBytes;
+            totalConsumedBytes += consumedBytes;
+            m_dumpInfo[i].m_name = name;
+            m_dumpInfo[i].m_used = usedBytes;
+            m_dumpInfo[i].m_reserved = reservedBytes;
+            m_dumpInfo[i].m_consumed = consumedBytes;
+            AZ_Printf(
+                AZ::Debug::NoWindow,
+                "%d,%s,%.2f,%.2f,%.2f,%s\n",
+                i,
+                name,
+                usedBytes / 1024.0f,
+                reservedBytes / 1024.0f,
+                consumedBytes / 1024.0f,
+                parentName);
+        }
 
-    // Build a mapping of original allocator sources to their allocators
-    for (int i = 0; i < allocatorCount; ++i)
+        AZ_Printf(AZ::Debug::NoWindow, "-,Totals,%.2f,%.2f,%.2f,\n", totalUsedBytes / 1024.0f, totalReservedBytes / 1024.0f, totalConsumedBytes / 1024.0f);
+        AZ_Printf(AZ::Debug::NoWindow, "%d allocators active\n", m_numAllocators);
+    }
+    void AllocatorManager::GetAllocatorStats(size_t& allocatedBytes, size_t& capacityBytes, AZStd::vector<AllocatorStats>* outStats)
     {
-        IAllocator* allocator = GetAllocator(i);
-        allocatedBytes += allocator->NumAllocatedBytes();
-        capacityBytes += allocator->Capacity();   
-           
-        if (outStats)
+        allocatedBytes = 0;
+        capacityBytes = 0;
+
+        AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
+        const int allocatorCount = GetNumAllocators();
+
+        // Build a mapping of original allocator sources to their allocators
+        for (int i = 0; i < allocatorCount; ++i)
         {
-            outStats->emplace(outStats->end(),
-                allocator->GetName(),
-                allocator->NumAllocatedBytes(),
-                allocator->Capacity());
+            IAllocator* allocator = GetAllocator(i);
+            allocatedBytes += allocator->NumAllocatedBytes();
+            capacityBytes += allocator->Capacity();
+
+            if (outStats)
+            {
+                outStats->emplace(outStats->end(), allocator->GetName(), allocator->NumAllocatedBytes(), allocator->Capacity());
+            }
         }
     }
-}
 
-//=========================================================================
-// MemoryBreak
-// [2/24/2011]
-//=========================================================================
-AllocatorManager::MemoryBreak::MemoryBreak()
-{
-    addressStart = nullptr;
-    addressEnd = nullptr;
-    byteSize = 0;
-    alignment = static_cast<size_t>(0xffffffff);
-    name = nullptr;
-
-    fileName = nullptr;
-    lineNum = -1;
-}
-
-//=========================================================================
-// SetMemoryBreak
-// [2/24/2011]
-//=========================================================================
-void
-AllocatorManager::SetMemoryBreak(int slot, const MemoryBreak& mb)
-{
-    AZ_Assert(slot < MaxNumMemoryBreaks, "Invalid slot index");
-    m_activeBreaks |= 1 << slot;
-    m_memoryBreak[slot] = mb;
-}
-
-//=========================================================================
-// ResetMemoryBreak
-// [2/24/2011]
-//=========================================================================
-void
-AllocatorManager::ResetMemoryBreak(int slot)
-{
-    if (slot == -1)
+    //=========================================================================
+    // MemoryBreak
+    // [2/24/2011]
+    //=========================================================================
+    AllocatorManager::MemoryBreak::MemoryBreak()
     {
-        m_activeBreaks = 0;
+        addressStart = nullptr;
+        addressEnd = nullptr;
+        byteSize = 0;
+        alignment = static_cast<size_t>(0xffffffff);
+        name = nullptr;
+
+        fileName = nullptr;
+        lineNum = -1;
     }
-    else
+
+    //=========================================================================
+    // SetMemoryBreak
+    // [2/24/2011]
+    //=========================================================================
+    void AllocatorManager::SetMemoryBreak(int slot, const MemoryBreak& mb)
     {
         AZ_Assert(slot < MaxNumMemoryBreaks, "Invalid slot index");
-        m_activeBreaks &= ~(1 << slot);
+        m_activeBreaks |= 1 << slot;
+        m_memoryBreak[slot] = mb;
     }
-}
 
-//=========================================================================
-// DebugBreak
-// [2/24/2011]
-//=========================================================================
-void
-AllocatorManager::DebugBreak(void* address, const Debug::AllocationInfo& info)
-{
-    (void)address;
-    (void)info;
-    if (m_activeBreaks)
+    //=========================================================================
+    // ResetMemoryBreak
+    // [2/24/2011]
+    //=========================================================================
+    void AllocatorManager::ResetMemoryBreak(int slot)
     {
-        void* addressEnd = (char*)address + info.m_byteSize;
-        (void)addressEnd;
-        for (int i = 0; i < MaxNumMemoryBreaks; ++i)
+        if (slot == -1)
         {
-            if ((m_activeBreaks & (1 << i)) != 0)
-            {
-                // check address range
-                AZ_Assert(!((address <= m_memoryBreak[i].addressStart && m_memoryBreak[i].addressStart < addressEnd) ||
-                            (address < m_memoryBreak[i].addressEnd && m_memoryBreak[i].addressEnd <= addressEnd) ||
-                            (address >= m_memoryBreak[i].addressStart && addressEnd <= m_memoryBreak[i].addressEnd)),
-                    "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]", address, addressEnd, m_memoryBreak[i].addressStart, m_memoryBreak[i].addressEnd);
-
-                AZ_Assert(!(m_memoryBreak[i].addressStart <= address && address < m_memoryBreak[i].addressEnd), "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]", address, addressEnd, m_memoryBreak[i].addressStart, m_memoryBreak[i].addressEnd);
-                AZ_Assert(!(m_memoryBreak[i].addressStart < addressEnd && addressEnd <= m_memoryBreak[i].addressEnd), "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]", address, addressEnd, m_memoryBreak[i].addressStart, m_memoryBreak[i].addressEnd);
-
+            m_activeBreaks = 0;
+        }
+        else
+        {
+            AZ_Assert(slot < MaxNumMemoryBreaks, "Invalid slot index");
+            m_activeBreaks &= ~(1 << slot);
+        }
+    }
 
-                AZ_Assert(!(m_memoryBreak[i].alignment == info.m_alignment), "User triggered breakpoint - alignment (%d)", info.m_alignment);
-                AZ_Assert(!(m_memoryBreak[i].byteSize == info.m_byteSize), "User triggered breakpoint - allocation size (%d)", info.m_byteSize);
-                AZ_Assert(!(info.m_name != nullptr && m_memoryBreak[i].name != nullptr && strcmp(m_memoryBreak[i].name, info.m_name) == 0), "User triggered breakpoint - name \"%s\"", info.m_name);
-                if (m_memoryBreak[i].lineNum != 0)
-                {
-                    AZ_Assert(!(info.m_fileName != nullptr && m_memoryBreak[i].fileName != nullptr && strcmp(m_memoryBreak[i].fileName, info.m_fileName) == 0 && m_memoryBreak[i].lineNum == info.m_lineNum), "User triggered breakpoint - file/line number : %s(%d)", info.m_fileName, info.m_lineNum);
-                }
-                else
+    //=========================================================================
+    // DebugBreak
+    // [2/24/2011]
+    //=========================================================================
+    void AllocatorManager::DebugBreak(void* address, const Debug::AllocationInfo& info)
+    {
+        (void)address;
+        (void)info;
+        if (m_activeBreaks)
+        {
+            void* addressEnd = (char*)address + info.m_byteSize;
+            (void)addressEnd;
+            for (int i = 0; i < MaxNumMemoryBreaks; ++i)
+            {
+                if ((m_activeBreaks & (1 << i)) != 0)
                 {
-                    AZ_Assert(!(info.m_fileName != nullptr && m_memoryBreak[i].fileName != nullptr && strcmp(m_memoryBreak[i].fileName, info.m_fileName) == 0), "User triggered breakpoint - file name \"%s\"", info.m_fileName);
+                    // check address range
+                    AZ_Assert(
+                        !((address <= m_memoryBreak[i].addressStart && m_memoryBreak[i].addressStart < addressEnd) ||
+                          (address < m_memoryBreak[i].addressEnd && m_memoryBreak[i].addressEnd <= addressEnd) ||
+                          (address >= m_memoryBreak[i].addressStart && addressEnd <= m_memoryBreak[i].addressEnd)),
+                        "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]",
+                        address,
+                        addressEnd,
+                        m_memoryBreak[i].addressStart,
+                        m_memoryBreak[i].addressEnd);
+
+                    AZ_Assert(
+                        !(m_memoryBreak[i].addressStart <= address && address < m_memoryBreak[i].addressEnd),
+                        "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]",
+                        address,
+                        addressEnd,
+                        m_memoryBreak[i].addressStart,
+                        m_memoryBreak[i].addressEnd);
+                    AZ_Assert(
+                        !(m_memoryBreak[i].addressStart < addressEnd && addressEnd <= m_memoryBreak[i].addressEnd),
+                        "User triggered breakpoint - address overlap [%p,%p] with [%p,%p]",
+                        address,
+                        addressEnd,
+                        m_memoryBreak[i].addressStart,
+                        m_memoryBreak[i].addressEnd);
+
+                    AZ_Assert(
+                        !(m_memoryBreak[i].alignment == info.m_alignment), "User triggered breakpoint - alignment (%d)", info.m_alignment);
+                    AZ_Assert(
+                        !(m_memoryBreak[i].byteSize == info.m_byteSize),
+                        "User triggered breakpoint - allocation size (%d)",
+                        info.m_byteSize);
+                    AZ_Assert(
+                        !(info.m_name != nullptr && m_memoryBreak[i].name != nullptr && strcmp(m_memoryBreak[i].name, info.m_name) == 0),
+                        "User triggered breakpoint - name \"%s\"",
+                        info.m_name);
+                    if (m_memoryBreak[i].lineNum != 0)
+                    {
+                        AZ_Assert(
+                            !(info.m_fileName != nullptr && m_memoryBreak[i].fileName != nullptr &&
+                              strcmp(m_memoryBreak[i].fileName, info.m_fileName) == 0 && m_memoryBreak[i].lineNum == info.m_lineNum),
+                            "User triggered breakpoint - file/line number : %s(%d)",
+                            info.m_fileName,
+                            info.m_lineNum);
+                    }
+                    else
+                    {
+                        AZ_Assert(
+                            !(info.m_fileName != nullptr && m_memoryBreak[i].fileName != nullptr &&
+                              strcmp(m_memoryBreak[i].fileName, info.m_fileName) == 0),
+                            "User triggered breakpoint - file name \"%s\"",
+                            info.m_fileName);
+                    }
                 }
             }
         }
     }
-}
 
 } // namespace AZ

+ 17 - 2
Code/Framework/AzCore/AzCore/Memory/AllocatorManager.h

@@ -90,9 +90,12 @@ namespace AZ
         void SetDefaultProfilingState(bool newState) { m_defaultProfilingState = newState; }
         bool GetDefaultProfilingState() const { return m_defaultProfilingState; }
 
-        /// Outputs allocator useage to the console, and also stores the values in m_dumpInfo for viewing in the crash dump
+        /// Outputs allocator usage to the console, and also stores the values in m_dumpInfo for viewing in the crash dump
         void DumpAllocators();
 
+        void SetTrackingForAllocator(AZStd::string_view allocatorName, AZ::Debug::AllocationRecords::Mode recordMode);
+        bool RemoveTrackingForAllocator(AZStd::string_view allocatorName);
+
         struct DumpInfo
         {
             // Must contain only POD types
@@ -149,7 +152,7 @@ namespace AZ
         AllocatorManager(const AllocatorManager&);
         AllocatorManager& operator=(const AllocatorManager&);
 
-        static const int m_maxNumAllocators = 100;
+        static constexpr int m_maxNumAllocators = 100;
         IAllocator*         m_allocators[m_maxNumAllocators];
         volatile int        m_numAllocators;
         OutOfMemoryCBType   m_outOfMemoryListener;
@@ -165,6 +168,18 @@ namespace AZ
 
         AZ::Debug::AllocationRecords::Mode m_defaultTrackingRecordMode;
 
+        using AllocatorName = AZStd::fixed_string<128>;
+        //! Stores the name
+        struct AllocatorTrackingConfig
+        {
+            AllocatorName m_allocatorName;
+            AZ::Debug::AllocationRecords::Mode m_recordMode{ AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS };
+        };
+        AZStd::fixed_vector<AllocatorTrackingConfig, m_maxNumAllocators> m_allocatorTrackingConfigs;
+
         static AllocatorManager g_allocMgr;    ///< The single instance of the allocator manager
+
+    private:
+        void ConfigureTrackingForAllocator(IAllocator* alloc, const AllocatorTrackingConfig& foundTrackingConfigIt);
     };
 }

+ 1 - 1
Code/Framework/AzCore/AzCore/Memory/AllocatorTrackingRecorder.cpp

@@ -169,7 +169,7 @@ namespace AZ
         }
         else
         {
-            AZ_Error("Memory", false, "There are %d allocations in allocator 0%p (%s):\n", allocations.size(), this, GetName());
+            AZ_Error("Memory", false, "There are %zu allocations in allocator 0%p (%s):\n", allocations.size(), this, GetName());
             for (const AllocationRecord& record : allocations)
             {
                 record.Print();

+ 0 - 149
Code/Framework/AzCore/AzCore/Memory/AllocatorWrappers.h

@@ -1,149 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-#pragma once
-
-#include <AzCore/Memory/IAllocator.h>
-#include <AzCore/Memory/AllocatorInstance.h>
-
-namespace AZ
-{
-    /**
-     * Wrapper for a virtual interface of an allocator.
-     *
-     */
-    class AllocatorPointerWrapper : public IAllocator
-    {
-        friend bool operator==(const AllocatorPointerWrapper&, const AllocatorPointerWrapper&);
-        friend bool operator!=(const AllocatorPointerWrapper&, const AllocatorPointerWrapper&);
-
-    public:
-        AllocatorPointerWrapper(IAllocator* allocator = nullptr)
-            : m_allocator(allocator)
-        {
-        }
-
-        AllocatorPointerWrapper(IAllocator& allocator)
-            : m_allocator(&allocator)
-        {
-        }
-        ~AllocatorPointerWrapper() override = default;
-
-        AllocatorPointerWrapper(const AllocatorPointerWrapper& aOther)
-        {
-            m_allocator = aOther.m_allocator;
-        }
-
-        pointer allocate(size_type byteSize, align_type alignment = 1) override
-        {
-            return m_allocator->allocate(byteSize, alignment);
-        }
-
-        void deallocate(pointer ptr, size_type byteSize = 0, align_type = 0) override
-        {
-            m_allocator->deallocate(ptr, byteSize);
-        }
-
-        pointer reallocate(pointer ptr, size_type newSize, align_type alignment = 1) override
-        {
-            return m_allocator->reallocate(ptr, newSize, alignment);
-        }
-
-        size_type get_allocated_size(pointer ptr, align_type alignment = 1) const override
-        {
-            return m_allocator->get_allocated_size(ptr, alignment);
-        }
-
-        AllocatorPointerWrapper& operator=(IAllocator* allocator)
-        {
-            m_allocator = allocator;
-            return *this;
-        }
-
-        AllocatorPointerWrapper& operator=(IAllocator& allocator)
-        {
-            m_allocator = &allocator;
-            return *this;
-        }
-
-        AllocatorPointerWrapper& operator=(const AllocatorPointerWrapper& allocator)
-        {
-            m_allocator = allocator.m_allocator;
-            return *this;
-        }
-    private:
-        IAllocator* m_allocator;
-    };
-
-    AZ_FORCE_INLINE bool operator==(const AllocatorPointerWrapper& a, const AllocatorPointerWrapper& b)
-    {
-        return a.m_allocator == b.m_allocator;
-    }
-
-    AZ_FORCE_INLINE bool operator!=(const AllocatorPointerWrapper& a, const AllocatorPointerWrapper& b)
-    {
-        return a.m_allocator != b.m_allocator;
-    }
-
-    /**
-     * Allocator that uses a global instance for allocation instead of a specific instance.
-     * This allocator can be copied/moved/etc and will always use the global instance.
-     */
-    template<typename Allocator>
-    class AllocatorGlobalWrapper : public IAllocator
-    {
-    public:
-        AllocatorGlobalWrapper() = default;
-        AllocatorGlobalWrapper(const AllocatorGlobalWrapper&) {}
-        AllocatorGlobalWrapper(AllocatorGlobalWrapper&&)  {}
-        AllocatorGlobalWrapper& operator=(const AllocatorGlobalWrapper&) { return *this; }
-        AllocatorGlobalWrapper& operator=(AllocatorGlobalWrapper&&) { return *this; }
-
-        pointer allocate(size_type byteSize, align_type alignment = 1) override
-        {
-            return AllocatorInstance<Allocator>::Get().allocate(byteSize, alignment);
-        }
-
-        void deallocate(pointer ptr, size_type byteSize = 0, align_type alignment = 0) override
-        {
-            AllocatorInstance<Allocator>::Get().deallocate(ptr, byteSize, alignment);
-        }
-
-        pointer reallocate(pointer ptr, size_type newSize, align_type alignment = 1) override
-        {
-            return AllocatorInstance<Allocator>::Get().reallocate(ptr, newSize, alignment);
-        }
-
-        size_type get_allocated_size(pointer ptr, align_type alignment = 1) const override
-        {
-            return AllocatorInstance<Allocator>::Get().get_allocated_size(ptr, alignment);
-        }
-
-        void Create() {}
-    };
-
-    template<typename Allocator>
-    AZ_FORCE_INLINE bool operator==(const AllocatorGlobalWrapper<Allocator>&, const AllocatorGlobalWrapper<Allocator>&)
-    {
-        return true;
-    }
-
-    template<typename Allocator>
-    AZ_FORCE_INLINE bool operator!=(const AllocatorGlobalWrapper<Allocator>&, const AllocatorGlobalWrapper<Allocator>&)
-    {
-        return false;
-    }
-
-    AZ_TYPE_INFO_TEMPLATE(AllocatorGlobalWrapper, "{0994AE22-B98C-427B-A8EC-110F50D1ECC1}", AZ_TYPE_INFO_TYPENAME);
-} // namespace AZ
-
-#define AZ_ALLOCATOR_DEFAULT_GLOBAL_WRAPPER(AllocatorName, AllocatorGlobalType, AllocatorGUID)                                             \
-    class AllocatorName : public AZ::AllocatorGlobalWrapper<AllocatorGlobalType>                                                           \
-    {                                                                                                                                      \
-    public:                                                                                                                                \
-        AZ_RTTI(AllocatorName, AllocatorGUID, IAllocator)                                                                                  \
-    };

+ 116 - 11
Code/Framework/AzCore/AzCore/Memory/ChildAllocatorSchema.h

@@ -7,41 +7,133 @@
  */
 #pragma once
 
-#include <AzCore/Memory/IAllocator.h>
+#include <AzCore/Memory/AllocatorBase.h>
 #include <AzCore/Memory/AllocatorInstance.h>
+#include <AzCore/Memory/Config.h>
 
 namespace AZ
 {
+    //! Helper macro to define a child allocator with a custom name for the allocator
+    //! It provides the following types
+    //! "<_ALLOCATOR_CLASS>" for use with the AZ::AllocatorInstance template and the AZ_CLASS_ALLOCATOR macro
+    //! "<_ALLOCATOR_CLASS>_for_std_t" for use with AZStd container types such as AZStd::vector, AZStd::basic_string, etc...
+    //! Ex. AZ_CHILD_ALLOCATOR_WITH_NAME(FooAllocator, "Foo", "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", AZ::SystemAllocator)
+    //! Would create an allocator type which piggybacks off of the parent allocator schema named the following
+    //! `FooAllocator` and `FooAllocator_for_std_t`
+    //! The `FooAllocator` can be supplied as AZ_CLASS_ALLOCATOR for a class
+    //! Such as `class VoxelCoordinates { AZ_CLASS_ALLOCATOR(VoxelCoordinates, FooAllocator); };`
+    //!
+    //! The `FooAllocator_for_std_t` can be supplied as the allocator argument to AZStd container such as an AZStd::vector
+    //! Such as `AZStd::vector<AZ::Vector4, FooAllocator_for_std_t> m_var;`
+#define AZ_CHILD_ALLOCATOR_WITH_NAME(_ALLOCATOR_CLASS, _ALLOCATOR_NAME, _ALLOCATOR_UUID, _ALLOCATOR_BASE) \
+    class _ALLOCATOR_CLASS \
+        : public AZ::ChildAllocatorSchema<_ALLOCATOR_BASE> \
+    { \
+        friend class AZ::AllocatorInstance<_ALLOCATOR_CLASS>; \
+    public: \
+        AZ_TYPE_INFO_WITH_NAME_DECL(_ALLOCATOR_CLASS); \
+        using Base = AZ::ChildAllocatorSchema<_ALLOCATOR_BASE>; \
+        AZ_RTTI_NO_TYPE_INFO_DECL(); \
+    };\
+    \
+    using AZ_JOIN(_ALLOCATOR_CLASS, _for_std_t) = AZ::AZStdAlloc<_ALLOCATOR_CLASS>; \
+    AZ_TYPE_INFO_WITH_NAME_IMPL_INLINE(_ALLOCATOR_CLASS, _ALLOCATOR_NAME, _ALLOCATOR_UUID); \
+    AZ_RTTI_NO_TYPE_INFO_IMPL_INLINE(_ALLOCATOR_CLASS, _ALLOCATOR_CLASS ::Base);
+
+    // Base class for ChildAllocatorSchema template to allow
+    // rtti-cast to common type that indicates the allocator is a child allocator
+    // This classes exposes a method to get the parent allocator
+    class ChildAllocatorSchemaBase
+        : public AllocatorBase
+    {
+    public:
+        AZ_RTTI(ChildAllocatorSchemaBase, "{AF5C2C64-EED4-4BF7-BBD9-3328A81BBC00}", AllocatorBase);
+        using AllocatorBase::AllocatorBase;
+        virtual IAllocator* GetParentAllocator() const = 0;
+    };
+
     // Schema which acts as a pass through to another allocator. This allows for allocators
     // which exist purely to categorize/track memory separately, piggy backing on the
     // structure of another allocator
     template <class ParentAllocator>
     class ChildAllocatorSchema
-        : public IAllocator
+        : public ChildAllocatorSchemaBase
     {
     public:
-        AZ_TYPE_INFO(ChildAllocatorSchema, "{2A28BEF4-278A-4A98-AC7D-5C1D6D190A36}")
+        AZ_RTTI((ChildAllocatorSchema, "{2A28BEF4-278A-4A98-AC7D-5C1D6D190A36}", ParentAllocator), ChildAllocatorSchemaBase);
 
         using Parent = ParentAllocator;
 
-        ChildAllocatorSchema() = default;
+
+        //! Invoke the AllocatorBase PostCreate function to register
+        //! the child schema allocator with the AZ::Allocator Manager
+        //! as well as to create the Allocation Records for the ChildAllocatorSchema
+        ChildAllocatorSchema()
+        {
+            PostCreate();
+        }
+
+        //! @param enableProfiling determines whether the allocations should be recorded
+        //! in an allocation records structure
+        explicit ChildAllocatorSchema(bool enableProfiling)
+            : ChildAllocatorSchemaBase{ enableProfiling }
+        {
+            PostCreate();
+        }
+
+        //! @param enableProfiling determines whether the allocations should be recorded
+        //! in an allocation records structure
+        //! @param skipRegistration determines if this instance of the allocator
+        //! should be registered with the allocator manager
+        ChildAllocatorSchema(bool enableProfiling, bool skipRegistration)
+            : ChildAllocatorSchemaBase{ enableProfiling }
+        {
+            if (skipRegistration)
+            {
+                DisableRegistration();
+            }
+            PostCreate();
+        }
+
+        //! Invoke the AllocatorBase PreDestroy function to deregister
+        //! the child schema allocator with the AZ::Allocator Manager
+        ~ChildAllocatorSchema() override
+        {
+            PreDestroy();
+        }
 
         //---------------------------------------------------------------------
         // IAllocator
         //---------------------------------------------------------------------
-        pointer allocate(size_type byteSize, size_type alignment) override
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override
         {
-            return AZ::AllocatorInstance<Parent>::Get().allocate(byteSize, alignment);
+            const AllocateAddress allocateAddress = AZ::AllocatorInstance<Parent>::Get().allocate(byteSize, alignment);
+            m_totalAllocatedBytes += allocateAddress.GetAllocatedBytes();
+            AZ_MEMORY_PROFILE(ProfileAllocation(allocateAddress.GetAddress(), byteSize, alignment, 1));
+            return allocateAddress;
         }
 
-        void deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override
+        size_type deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override
         {
-            AZ::AllocatorInstance<Parent>::Get().deallocate(ptr, byteSize, alignment);
+            // Record de-allocations in the ChildAllocatorSchema Allocations
+            // before calling the parent allocator to make sure the allocation records
+            // are up-to-date
+            AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
+
+            const size_type bytesDeallocated = AZ::AllocatorInstance<Parent>::Get().deallocate(ptr, byteSize, alignment);
+            m_totalAllocatedBytes -= bytesDeallocated;
+            return bytesDeallocated;
         }
 
-        pointer reallocate(pointer ptr, size_type newSize, size_type newAlignment) override
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override
         {
-            return AZ::AllocatorInstance<Parent>::Get().reallocate(ptr, newSize, newAlignment);
+            const size_type oldAllocatedSize = get_allocated_size(ptr, 1);
+            AllocateAddress newAddress = AZ::AllocatorInstance<Parent>::Get().reallocate(ptr, newSize, newAlignment);
+            // The reallocation might have clamped the newSize to be at least the minimum allocation size
+            // used by the parent schema. For example the HphaSchemaBase has a minimum allocation size of 8 bytes
+            AZ_MEMORY_PROFILE(ProfileReallocation(ptr, newAddress, newAddress.GetAllocatedBytes(), newAlignment));
+            m_totalAllocatedBytes += newAddress.GetAllocatedBytes() - oldAllocatedSize;
+            return newAddress;
         }
 
         size_type get_allocated_size(pointer ptr, align_type alignment) const override
@@ -56,7 +148,20 @@ namespace AZ
 
         size_type NumAllocatedBytes() const override
         {
-            return AZ::AllocatorInstance<Parent>::Get().NumAllocatedBytes();
+            AZ_Assert(
+                m_totalAllocatedBytes >= 0,
+                "Total allocated bytes is less than zero with a value of %td. Was deallocate() invoked with an address "
+                "that is not associated with this allocator? This should never occur",
+                m_totalAllocatedBytes.load());
+            return static_cast<size_type>(m_totalAllocatedBytes);
         }
+
+        IAllocator* GetParentAllocator() const override
+        {
+            return &AZ::AllocatorInstance<Parent>::Get();
+        }
+
+    private:
+        AZStd::atomic<ptrdiff_t> m_totalAllocatedBytes{};
     };
 }

+ 107 - 184
Code/Framework/AzCore/AzCore/Memory/HphaAllocator.cpp

@@ -325,12 +325,12 @@ namespace AZ
         void* bucket_system_alloc();
         void bucket_system_free(void* ptr);
         page* bucket_grow(size_t elemSize, size_t marker);
-        void* bucket_alloc(size_t size);
-        void* bucket_alloc_direct(unsigned bi);
-        void* bucket_realloc(void* ptr, size_t size);
-        void* bucket_realloc_aligned(void* ptr, size_t size, size_t alignment);
-        void bucket_free(void* ptr);
-        void bucket_free_direct(void* ptr, unsigned bi);
+        AllocateAddress bucket_alloc(size_t size);
+        AllocateAddress bucket_alloc_direct(unsigned bi);
+        AllocateAddress bucket_realloc(void* ptr, size_t size);
+        AllocateAddress bucket_realloc_aligned(void* ptr, size_t size, size_t alignment);
+        size_type bucket_free(void* ptr);
+        size_type bucket_free_direct(void* ptr, unsigned bi);
         /// return the block size for the pointer.
         size_t bucket_ptr_size(void* ptr) const;
         size_t bucket_get_max_allocation() const;
@@ -420,15 +420,12 @@ namespace AZ
         void tree_detach(block_header* bl);
         void tree_purge_block(block_header* bl);
 
-        void* tree_alloc(size_t size);
-        void* tree_alloc_aligned(size_t size, size_t alignment);
-        void* tree_alloc_bucket_page();
-        void* tree_realloc(void* ptr, size_t size);
-        void* tree_realloc_aligned(void* ptr, size_t size, size_t alignment);
+        AllocateAddress tree_alloc(size_t size);
+        AllocateAddress tree_alloc_aligned(size_t size, size_t alignment);
+        AllocateAddress tree_realloc(void* ptr, size_t size);
+        AllocateAddress tree_realloc_aligned(void* ptr, size_t size, size_t alignment);
         size_t tree_resize(void* ptr, size_t size);
-        void tree_free(void* ptr);
-        void tree_free_bucket_page(
-            void* ptr); // only allocations with tree_alloc_bucket_page should be passed here, we don't have any guards.
+        size_type tree_free(void* ptr);
         size_t tree_ptr_size(void* ptr) const;
         size_t tree_get_max_allocation() const;
         size_t tree_get_unused_memory(bool isPrint) const;
@@ -597,29 +594,29 @@ namespace AZ
         HpAllocator();
         ~HpAllocator() override;
 
-        pointer allocate(size_type byteSize, align_type alignment = 1) override;
-        void deallocate(pointer ptr, size_type byteSize = 0, align_type alignment = 0) override;
-        pointer reallocate(pointer ptr, size_type newSize, align_type alignment = 1) override;
+        AllocateAddress allocate(size_type byteSize, align_type alignment = 1) override;
+        size_type deallocate(pointer ptr, size_type byteSize = 0, align_type alignment = 0) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, align_type alignment = 1) override;
         size_type get_allocated_size(pointer ptr, align_type alignment = 1) const override;
 
         // allocate memory using DEFAULT_ALIGNMENT
         // size == 0 returns NULL
-        inline void* alloc(size_t size)
+        inline AllocateAddress alloc(size_t size)
         {
             if (size == 0)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
             if (is_small_allocation(size))
             {
                 size = clamp_small_allocation(size);
-                void* ptr = bucket_alloc_direct(bucket_spacing_function(size + MEMORY_GUARD_SIZE));
+                AllocateAddress ptr = bucket_alloc_direct(bucket_spacing_function(size + MEMORY_GUARD_SIZE));
                 debug_add(ptr, size, DEBUG_SOURCE_BUCKETS);
                 return ptr;
             }
             else
             {
-                void* ptr = tree_alloc(size + MEMORY_GUARD_SIZE);
+                AllocateAddress ptr = tree_alloc(size + MEMORY_GUARD_SIZE);
                 debug_add(ptr, size, DEBUG_SOURCE_TREE);
                 return ptr;
             }
@@ -628,7 +625,7 @@ namespace AZ
         // allocate memory with a specific alignment
         // size == 0 returns NULL
         // alignment <= DEFAULT_ALIGNMENT acts as alloc
-        inline void* alloc(size_t size, size_t alignment)
+        inline AllocateAddress alloc(size_t size, size_t alignment)
         {
             HPPA_ASSERT((alignment & (alignment - 1)) == 0);
             if (alignment <= DEFAULT_ALIGNMENT)
@@ -637,18 +634,18 @@ namespace AZ
             }
             if (size == 0)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
             if (is_small_allocation(size) && alignment <= MAX_SMALL_ALLOCATION)
             {
                 size = clamp_small_allocation(size);
-                void* ptr = bucket_alloc_direct(bucket_spacing_function(AZ::SizeAlignUp(size + MEMORY_GUARD_SIZE, alignment)));
+                AllocateAddress ptr = bucket_alloc_direct(bucket_spacing_function(AZ::SizeAlignUp(size + MEMORY_GUARD_SIZE, alignment)));
                 debug_add(ptr, size, DEBUG_SOURCE_BUCKETS);
                 return ptr;
             }
             else
             {
-                void* ptr = tree_alloc_aligned(size + MEMORY_GUARD_SIZE, alignment);
+                AllocateAddress ptr = tree_alloc_aligned(size + MEMORY_GUARD_SIZE, alignment);
                 debug_add(ptr, size, DEBUG_SOURCE_TREE);
                 return ptr;
             }
@@ -657,7 +654,7 @@ namespace AZ
         // realloc the memory block using DEFAULT_ALIGNMENT
         // ptr == NULL acts as alloc
         // size == 0 acts as free
-        void* realloc(void* ptr, size_t size)
+        AllocateAddress realloc(void* ptr, size_t size)
         {
             if (ptr == nullptr)
             {
@@ -666,10 +663,10 @@ namespace AZ
             if (size == 0)
             {
                 free(ptr);
-                return nullptr;
+                return AllocateAddress{};
             }
             debug_check(ptr);
-            void* newPtr = nullptr;
+            AllocateAddress newPtr;
             if (ptr_in_bucket(ptr))
             {
                 if (is_small_allocation(size))
@@ -723,7 +720,7 @@ namespace AZ
         // ptr == NULL acts as alloc
         // size == 0 acts as free
         // alignment <= DEFAULT_ALIGNMENT acts as realloc
-        void* realloc(void* ptr, size_t size, size_t alignment)
+        AllocateAddress realloc(void* ptr, size_t size, size_t alignment)
         {
             HPPA_ASSERT((alignment & (alignment - 1)) == 0);
             if (alignment <= DEFAULT_ALIGNMENT)
@@ -737,14 +734,14 @@ namespace AZ
             if (size == 0)
             {
                 free(ptr);
-                return nullptr;
+                return AllocateAddress{};
             }
             if ((size_t)ptr & (alignment - 1))
             {
-                void* newPtr = alloc(size, alignment);
+                AllocateAddress newPtr = alloc(size, alignment);
                 if (!newPtr)
                 {
-                    return nullptr;
+                    return AllocateAddress{};
                 }
                 size_t count = this->size(ptr);
                 if (count > size)
@@ -756,7 +753,7 @@ namespace AZ
                 return newPtr;
             }
             debug_check(ptr);
-            void* newPtr = nullptr;
+            AllocateAddress newPtr;
             if (ptr_in_bucket(ptr))
             {
                 if (is_small_allocation(size) && alignment <= MAX_SMALL_ALLOCATION)
@@ -876,11 +873,11 @@ namespace AZ
         }
 
         // free the memory block
-        inline void free(void* ptr)
+        inline size_type free(void* ptr)
         {
             if (ptr == nullptr)
             {
-                return;
+                return 0;
             }
             if (ptr_in_bucket(ptr))
             {
@@ -888,15 +885,15 @@ namespace AZ
                 return bucket_free(ptr);
             }
             debug_remove(ptr, DEBUG_UNKNOWN_SIZE, DEBUG_SOURCE_TREE);
-            tree_free(ptr);
+            return tree_free(ptr);
         }
 
         // free the memory block supplying the original size with DEFAULT_ALIGNMENT
-        inline void free(void* ptr, size_t origSize)
+        inline size_type free(void* ptr, size_t origSize)
         {
             if (ptr == nullptr)
             {
-                return;
+                return 0;
             }
             if (is_small_allocation(origSize))
             {
@@ -906,15 +903,15 @@ namespace AZ
                 return bucket_free_direct(ptr, bucket_spacing_function(origSize + MEMORY_GUARD_SIZE));
             }
             debug_remove(ptr, origSize, DEBUG_SOURCE_TREE);
-            tree_free(ptr);
+            return tree_free(ptr);
         }
 
         // free the memory block supplying the original size and alignment
-        inline void free(void* ptr, size_t origSize, size_t oldAlignment)
+        inline size_type free(void* ptr, size_t origSize, size_t oldAlignment)
         {
             if (ptr == nullptr)
             {
-                return;
+                return 0;
             }
             if (is_small_allocation(origSize) && oldAlignment <= MAX_SMALL_ALLOCATION)
             {
@@ -923,7 +920,7 @@ namespace AZ
                 return bucket_free_direct(ptr, bucket_spacing_function(AZ::SizeAlignUp(origSize + MEMORY_GUARD_SIZE, oldAlignment)));
             }
             debug_remove(ptr, origSize, DEBUG_SOURCE_TREE);
-            tree_free(ptr);
+            return tree_free(ptr);
         }
 
         // return all unused memory pages to the OS
@@ -1202,7 +1199,7 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_alloc(size_t size)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_alloc(size_t size)
     {
         HPPA_ASSERT(size <= MAX_SMALL_ALLOCATION);
         unsigned bi = bucket_spacing_function(size);
@@ -1223,17 +1220,17 @@ namespace AZ
             p = bucket_grow(bsize, mBuckets[bi].marker());
             if (!p)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
             mBuckets[bi].add_free_page(p);
         }
         HPPA_ASSERT(p->elem_size() >= size);
         mTotalAllocatedSizeBuckets += p->elem_size();
-        return mBuckets[bi].alloc(p);
+        return AllocateAddress{ mBuckets[bi].alloc(p), p->elem_size() };
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_alloc_direct(unsigned bi)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_alloc_direct(unsigned bi)
     {
         HPPA_ASSERT(bi < NUM_BUCKETS);
 #ifdef MULTITHREADED
@@ -1250,16 +1247,16 @@ namespace AZ
             p = bucket_grow(bsize, mBuckets[bi].marker());
             if (!p)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
             mBuckets[bi].add_free_page(p);
         }
         mTotalAllocatedSizeBuckets += p->elem_size();
-        return mBuckets[bi].alloc(p);
+        return AllocateAddress(mBuckets[bi].alloc(p), p->elem_size());
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_realloc(void* ptr, size_t size)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_realloc(void* ptr, size_t size)
     {
         page* p = ptr_get_page(ptr);
         size_t elemSize = p->elem_size();
@@ -1267,12 +1264,12 @@ namespace AZ
         // if (size <= elemSize)
         if (size == elemSize)
         {
-            return ptr;
+            return AllocateAddress{ ptr, size };
         }
-        void* newPtr = bucket_alloc(size);
-        if (!newPtr)
+        AllocateAddress newPtr = bucket_alloc(size);
+        if (newPtr.GetAddress() == nullptr)
         {
-            return nullptr;
+            return AllocateAddress{};
         }
         memcpy(newPtr, ptr, AZStd::GetMin(elemSize - MEMORY_GUARD_SIZE, size - MEMORY_GUARD_SIZE));
         bucket_free(ptr);
@@ -1280,7 +1277,7 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_realloc_aligned(void* ptr, size_t size, size_t alignment)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_realloc_aligned(void* ptr, size_t size, size_t alignment)
     {
         page* p = ptr_get_page(ptr);
         size_t elemSize = p->elem_size();
@@ -1288,12 +1285,12 @@ namespace AZ
         // if (size <= elemSize && (elemSize&(alignment-1))==0)
         if (size == elemSize && (elemSize & (alignment - 1)) == 0)
         {
-            return ptr;
+            return AllocateAddress{ ptr, size };
         }
-        void* newPtr = bucket_alloc_direct(bucket_spacing_function(AZ::SizeAlignUp(size, alignment)));
-        if (!newPtr)
+        AllocateAddress newPtr = bucket_alloc_direct(bucket_spacing_function(AZ::SizeAlignUp(size, alignment)));
+        if (newPtr.GetAddress() == nullptr)
         {
-            return nullptr;
+            return AllocateAddress{};
         }
         memcpy(newPtr, ptr, AZStd::GetMin(elemSize - MEMORY_GUARD_SIZE, size - MEMORY_GUARD_SIZE));
         bucket_free(ptr);
@@ -1301,7 +1298,7 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_free(void* ptr)
+    auto HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_free(void* ptr) -> size_type
     {
         page* p = ptr_get_page(ptr);
         unsigned bi = p->bucket_index();
@@ -1313,12 +1310,15 @@ namespace AZ
         AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
 #endif
 #endif
-        mTotalAllocatedSizeBuckets -= p->elem_size();
+        size_type allocatedByteCount = p->elem_size();
+        mTotalAllocatedSizeBuckets -= allocatedByteCount;
         mBuckets[bi].free(p, ptr);
+
+        return allocatedByteCount;
     }
 
     template<bool DebugAllocatorEnable>
-    void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_free_direct(void* ptr, unsigned bi)
+    auto HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::bucket_free_direct(void* ptr, unsigned bi) -> size_type
     {
         HPPA_ASSERT(bi < NUM_BUCKETS);
         page* p = ptr_get_page(ptr);
@@ -1332,8 +1332,11 @@ namespace AZ
         AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
 #endif
 #endif
-        mTotalAllocatedSizeBuckets -= p->elem_size();
+        size_type allocatedByteCount = p->elem_size();
+        mTotalAllocatedSizeBuckets -= allocatedByteCount;
         mBuckets[bi].free(p, ptr);
+
+        return allocatedByteCount;
     }
 
     template<bool DebugAllocatorEnable>
@@ -1649,7 +1652,7 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_alloc(size_t size)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_alloc(size_t size)
     {
 #ifdef MULTITHREADED
         AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
@@ -1668,7 +1671,7 @@ namespace AZ
             newBl = tree_grow(size);
             if (!newBl)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
         }
         HPPA_ASSERT(!newBl->used());
@@ -1681,11 +1684,11 @@ namespace AZ
         }
         newBl->set_used();
         mTotalAllocatedSizeTree += newBl->size();
-        return newBl->mem();
+        return AllocateAddress{ newBl->mem(), newBl->size() };
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_alloc_aligned(size_t size, size_t alignment)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_alloc_aligned(size_t size, size_t alignment)
     {
 #ifdef MULTITHREADED
         AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
@@ -1701,7 +1704,7 @@ namespace AZ
             newBl = tree_grow(size + alignment);
             if (!newBl)
             {
-                return nullptr;
+                return AllocateAddress{};
             }
         }
         HPPA_ASSERT(newBl && newBl->size() >= size);
@@ -1725,54 +1728,11 @@ namespace AZ
         newBl->set_used();
         HPPA_ASSERT(((size_t)newBl->mem() & (alignment - 1)) == 0);
         mTotalAllocatedSizeTree += newBl->size();
-        return newBl->mem();
+        return AllocateAddress{ newBl->mem(), newBl->size() };
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_alloc_bucket_page()
-    {
-#ifdef MULTITHREADED
-        AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
-#endif
-        // We are allocating pool pages m_poolPageSize aligned at m_poolPageSize
-        // what is special is that we are keeping the block_header at the beginning of the
-        // memory and we return the offset pointer.
-        size_t size = m_poolPageSize;
-        size_t alignment = m_poolPageSize;
-        block_header* newBl = tree_extract_bucket_page();
-        if (!newBl)
-        {
-            newBl = tree_grow(size + alignment);
-            if (!newBl)
-            {
-                return nullptr;
-            }
-        }
-        HPPA_ASSERT(newBl && (newBl->size() + sizeof(block_header)) >= size);
-        size_t alignmentOffs = AZ::PointerAlignUp((char*)newBl, alignment) - (char*)newBl;
-        HPPA_ASSERT(newBl->size() + sizeof(block_header) >= size + alignmentOffs);
-        if (alignmentOffs >= sizeof(block_header) + sizeof(free_node))
-        {
-            split_block(newBl, alignmentOffs - sizeof(block_header));
-            tree_attach(newBl);
-            newBl = newBl->next();
-        }
-        else if (alignmentOffs > 0)
-        {
-            newBl = shift_block(newBl, alignmentOffs);
-        }
-        if (newBl->size() >= (size - sizeof(block_header)) + sizeof(block_header) + sizeof(free_node))
-        {
-            split_block(newBl, size - sizeof(block_header));
-            tree_attach(newBl->next());
-        }
-        newBl->set_used();
-        HPPA_ASSERT(((size_t)newBl & (alignment - 1)) == 0);
-        return newBl;
-    }
-
-    template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_realloc(void* ptr, size_t size)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_realloc(void* ptr, size_t size)
     {
 #ifdef MULTITHREADED
         AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
@@ -1796,7 +1756,7 @@ namespace AZ
                 mTotalAllocatedSizeTree += bl->size() - blSize;
             }
             HPPA_ASSERT(bl->size() >= size);
-            return ptr;
+            return AllocateAddress{ ptr, bl->size() };
         }
         // check if the following block is free and if it can accommodate the requested size
         block_header* next = bl->next();
@@ -1813,7 +1773,7 @@ namespace AZ
                 tree_attach(bl->next());
             }
             mTotalAllocatedSizeTree += bl->size() - blSize;
-            return ptr;
+            return AllocateAddress{ ptr, bl->size() };
         }
         // check if the previous block can be used to avoid searching
         block_header* prev = bl->prev();
@@ -1840,21 +1800,21 @@ namespace AZ
                 tree_attach(bl->next());
             }
             mTotalAllocatedSizeTree += bl->size() - blSize;
-            return newPtr;
+            return AllocateAddress{ newPtr, bl->size() };
         }
         // fall back to alloc/copy/free
-        void* newPtr = tree_alloc(size);
+        AllocateAddress newPtr = tree_alloc(size);
         if (newPtr)
         {
             memcpy(newPtr, ptr, blSize - MEMORY_GUARD_SIZE);
             tree_free(ptr);
             return newPtr;
         }
-        return nullptr;
+        return AllocateAddress{};
     }
 
     template<bool DebugAllocatorEnable>
-    void* HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_realloc_aligned(void* ptr, size_t size, size_t alignment)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_realloc_aligned(void* ptr, size_t size, size_t alignment)
     {
         HPPA_ASSERT(((size_t)ptr & (alignment - 1)) == 0);
 #ifdef MULTITHREADED
@@ -1878,7 +1838,7 @@ namespace AZ
             }
             mTotalAllocatedSizeTree += bl->size() - blSize;
             HPPA_ASSERT(bl->size() >= size);
-            return ptr;
+            return AllocateAddress{ ptr, bl->size() };
         }
         block_header* next = bl->next();
         size_t nextSize = next->used() ? 0 : next->size() + sizeof(block_header);
@@ -1894,7 +1854,7 @@ namespace AZ
                 tree_attach(bl->next());
             }
             mTotalAllocatedSizeTree += bl->size() - blSize;
-            return ptr;
+            return AllocateAddress{ ptr, bl->size() };
         }
         block_header* prev = bl->prev();
         size_t prevSize = prev->used() ? 0 : prev->size() + sizeof(block_header);
@@ -1930,16 +1890,16 @@ namespace AZ
                 tree_attach(bl->next());
             }
             mTotalAllocatedSizeTree += bl->size() - blSize;
-            return newPtr;
+            return AllocateAddress{ newPtr, bl->size() };
         }
-        void* newPtr = tree_alloc_aligned(size, alignment);
+        AllocateAddress newPtr = tree_alloc_aligned(size, alignment);
         if (newPtr)
         {
             memcpy(newPtr, ptr, blSize - MEMORY_GUARD_SIZE);
             tree_free(ptr);
             return newPtr;
         }
-        return nullptr;
+        return AllocateAddress{};
     }
 
     template<bool DebugAllocatorEnable>
@@ -1986,7 +1946,7 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_free(void* ptr)
+    auto HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_free(void* ptr) -> size_type
     {
 #ifdef MULTITHREADED
         AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
@@ -1994,26 +1954,16 @@ namespace AZ
         block_header* bl = ptr_get_block_header(ptr);
         // This assert detects a double-free of ptr
         HPPA_ASSERT(bl->used());
-        mTotalAllocatedSizeTree -= bl->size();
+        size_type allocatedBytes = bl->size();
+        mTotalAllocatedSizeTree -= allocatedBytes;
         bl->set_unused();
         bl = coalesce_block(bl);
         tree_attach(bl);
-    }
 
-    template<bool DebugAllocatorEnable>
-    void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_free_bucket_page(void* ptr)
-    {
-#ifdef MULTITHREADED
-        AZStd::lock_guard<AZStd::recursive_mutex> lock(mTreeMutex);
-#endif
-        HPPA_ASSERT(AZ::PointerAlignDown(ptr, m_poolPageSize) == ptr);
-        block_header* bl = (block_header*)ptr;
-        HPPA_ASSERT(bl->size() >= m_poolPageSize - sizeof(block_header));
-        bl->set_unused();
-        bl = coalesce_block(bl);
-        tree_attach(bl);
+        return allocatedBytes;
     }
 
+
     template<bool DebugAllocatorEnable>
     void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::tree_purge_block(block_header* bl)
     {
@@ -2094,9 +2044,9 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    typename HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::pointer HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::allocate(size_type byteSize, align_type alignment)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::allocate(size_type byteSize, align_type alignment)
     {
-        pointer address = alloc(byteSize, static_cast<size_t>(alignment));
+        AllocateAddress address = alloc(byteSize, static_cast<size_t>(alignment));
         if (address == nullptr)
         {
             purge();
@@ -2106,31 +2056,32 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    void HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::deallocate(pointer ptr, size_type byteSize, align_type alignment)
+    auto HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::deallocate(pointer ptr, size_type byteSize, align_type alignment)
+        -> size_type
     {
         if (ptr == nullptr)
         {
-            return;
+            return 0;
         }
         if (byteSize == 0)
         {
-            free(ptr);
+            return free(ptr);
         }
         else if (alignment == 0)
         {
-            free(ptr, byteSize);
+            return free(ptr, byteSize);
         }
         else
         {
-            free(ptr, byteSize, alignment);
+            return free(ptr, byteSize, alignment);
         }
     }
 
     template<bool DebugAllocatorEnable>
-    typename HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::pointer HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::reallocate(pointer ptr, size_type newSize, align_type alignment)
+    AllocateAddress HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::reallocate(pointer ptr, size_type newSize, align_type alignment)
     {
-        pointer address = realloc(ptr, newSize, static_cast<size_t>(alignment));
-        if (address == nullptr && newSize > 0)
+        AllocateAddress address = realloc(ptr, newSize, static_cast<size_t>(alignment));
+        if (address.GetAddress() == nullptr && newSize > 0)
         {
             purge();
             address = realloc(ptr, newSize, static_cast<size_t>(alignment));
@@ -2139,7 +2090,8 @@ namespace AZ
     }
 
     template<bool DebugAllocatorEnable>
-    typename HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::size_type HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::get_allocated_size(pointer ptr, [[maybe_unused]] align_type alignment) const
+    auto HphaSchemaBase<DebugAllocatorEnable>::HpAllocator::get_allocated_size(pointer ptr, [[maybe_unused]] align_type alignment) const
+        -> size_type
     {
         return size(ptr);
     }
@@ -2474,16 +2426,9 @@ namespace AZ
     // [2/22/2011]
     //=========================================================================
     template<bool DebugAllocator>
-    auto HphaSchemaBase<DebugAllocator>::allocate(size_type byteSize, size_type alignment)
-        -> pointer
+    AllocateAddress HphaSchemaBase<DebugAllocator>::allocate(size_type byteSize, size_type alignment)
     {
-        pointer address = m_allocator->alloc(byteSize, alignment);
-        if (address == nullptr)
-        {
-            GarbageCollect();
-            address = m_allocator->alloc(byteSize, alignment);
-        }
-        return address;
+        return m_allocator->allocate(byteSize, alignment);
     }
 
     //=========================================================================
@@ -2491,16 +2436,9 @@ namespace AZ
     // [2/22/2011]
     //=========================================================================
     template<bool DebugAllocator>
-    auto HphaSchemaBase<DebugAllocator>::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
-        -> pointer
+    AllocateAddress HphaSchemaBase<DebugAllocator>::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
     {
-        pointer address = m_allocator->realloc(ptr, newSize, newAlignment);
-        if (address == nullptr && newSize > 0)
-        {
-            GarbageCollect();
-            address = m_allocator->realloc(ptr, newSize, newAlignment);
-        }
-        return address;
+        return m_allocator->reallocate(ptr, newSize, newAlignment);
     }
 
     //=========================================================================
@@ -2508,24 +2446,9 @@ namespace AZ
     // [2/22/2011]
     //=========================================================================
     template<bool DebugAllocator>
-    void HphaSchemaBase<DebugAllocator>::deallocate(pointer ptr, size_type size, size_type alignment)
+    auto HphaSchemaBase<DebugAllocator>::deallocate(pointer ptr, size_type size, size_type alignment) -> size_type
     {
-        if (ptr == nullptr)
-        {
-            return;
-        }
-        if (size == 0)
-        {
-            m_allocator->free(ptr);
-        }
-        else if (alignment == 0)
-        {
-            m_allocator->free(ptr, size);
-        }
-        else
-        {
-            m_allocator->free(ptr, size, alignment);
-        }
+        return m_allocator->deallocate(ptr, size, alignment);
     }
 
     //=========================================================================

+ 3 - 3
Code/Framework/AzCore/AzCore/Memory/HphaAllocator.h

@@ -30,9 +30,9 @@ namespace AZ
         HphaSchemaBase();
         virtual ~HphaSchemaBase();
 
-        pointer         allocate(size_type byteSize, size_type alignment) override;
-        void            deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
-        pointer         reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override;
+        size_type       deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
         size_type get_allocated_size(pointer ptr, align_type alignment = 1) const override;
 
         size_type       NumAllocatedBytes() const override;

+ 64 - 4
Code/Framework/AzCore/AzCore/Memory/IAllocator.h

@@ -21,6 +21,64 @@
 
 #define AZ_ALLOCATOR_DEFAULT_TRAITS AZ_ALLOCATOR_TRAITS(void)
 
+ //! Wraps an allocated memory address in a struct that is convertible to a void pointer
+ //! This is used to allow implicit conversion of the IAllocate::allocate function
+ //! to a void pointer to maintain backwards compatibility, while providing
+ //! the ability for newer code to get access to the actual amount of bytes allocated.
+ //! This can be used by wrapping allocators to track memory
+struct AllocateAddress
+{
+    //! default an empty address object
+    AllocateAddress() = default;
+
+    //! constructs an allocate address out of a void* and size
+    AllocateAddress(void* address, size_t size)
+        : m_value(address)
+        , m_size(size)
+    {}
+
+
+    //! default copy constructor and assignment operator
+    AllocateAddress(const AllocateAddress&) = default;
+    AllocateAddress& operator=(const AllocateAddress&) = default;
+
+    void* GetAddress() const
+    {
+        return m_value;
+    }
+
+    size_t GetAllocatedBytes() const
+    {
+        return m_size;
+    }
+
+    //! implicit void* operator allows assigning
+    //! an AllocateAddress directly to a void*
+    operator void* () const
+    {
+        return m_value;
+    }
+
+    //! explicit T* operator which is used
+    //! for explicit casting from an AllocateAddress
+    //! to a T* pointer
+    template <class T>
+    explicit operator T* () const
+    {
+        return reinterpret_cast<T*>(m_value);
+    }
+
+    explicit operator bool() const
+    {
+        return m_value != nullptr;
+    }
+
+    //! Store allocated memory address
+    void* m_value{};
+    //! Stores the amount of bytes allocated
+    size_t m_size{};
+};
+
 namespace AZ
 {
     namespace Debug
@@ -73,8 +131,10 @@ namespace AZ
         IAllocator& operator=(IAllocator&&) = delete;
         virtual ~IAllocator() = default;
 
-        virtual pointer allocate(size_type byteSize, align_type alignment = 1) = 0;
-        virtual void deallocate(pointer ptr, size_type byteSize = 0, align_type alignment = 0) = 0;
+        //! Allocates memory and returns the address + the amount of memory allocated
+        virtual AllocateAddress allocate(size_type byteSize, align_type alignment = 1) = 0;
+        //! Deallocates memory at the ptr address and returns the amount of memory that was allocated
+        virtual size_type deallocate(pointer ptr, size_type byteSize = 0, align_type alignment = 0) = 0;
 
         // Reallocates a bigger block. Allocators will do best effort to maintain the same pointer,
         // but they can return a new pointer if is not possible or the allocator doesnt support it.
@@ -83,7 +143,7 @@ namespace AZ
         // Memory pointed by ptr is copied to the returned pointer if the returned pointer is different
         // than the passed ptr
         // Memory pointed by ptr is freed if the returned pointer is different than the passed ptr
-        virtual pointer reallocate(pointer ptr, size_type newSize, align_type newAlignment = 1) = 0;
+        virtual AllocateAddress reallocate(pointer ptr, size_type newSize, align_type newAlignment = 1) = 0;
 
         virtual size_type max_size() const
         {
@@ -102,7 +162,7 @@ namespace AZ
         {
         }
 
-        const char* GetName() const
+        virtual const char* GetName() const
         {
             return RTTI_GetTypeName();
         }

+ 1 - 1
Code/Framework/AzCore/AzCore/Memory/Memory.h

@@ -11,7 +11,7 @@
 #include <AzCore/Memory/Memory_fwd.h>
 #include <AzCore/Memory/AllocatorWrapper.h>
 #include <AzCore/Memory/Config.h>
-#include <AzCore/Memory/SimpleSchemaAllocator.h>
+#include <AzCore/Memory/IAllocator.h>
 #include <AzCore/std/typetraits/alignment_of.h>
 #include <AzCore/std/typetraits/aligned_storage.h>
 

+ 3 - 3
Code/Framework/AzCore/AzCore/Memory/Memory_fwd.h

@@ -20,11 +20,11 @@
 #define aznewex(_Name)                                          new
 
 /// azmalloc(size)
-#define azmalloc_1(_1)                                          AZ::AllocatorInstance< AZ::SystemAllocator >::Get().allocate(_1)
+#define azmalloc_1(_1)                                          static_cast<void*>(AZ::AllocatorInstance< AZ::SystemAllocator >::Get().allocate(_1))
 /// azmalloc(size,alignment)
-#define azmalloc_2(_1, _2)                                      AZ::AllocatorInstance< AZ::SystemAllocator >::Get().allocate(_1, _2)
+#define azmalloc_2(_1, _2)                                      static_cast<void*>(AZ::AllocatorInstance< AZ::SystemAllocator >::Get().allocate(_1, _2))
 /// azmalloc(size,alignment,Allocator)
-#define azmalloc_3(_1, _2, _3)                                  AZ::AllocatorInstance< _3 >::Get().allocate(_1, _2)
+#define azmalloc_3(_1, _2, _3)                                  static_cast<void*>(AZ::AllocatorInstance< _3 >::Get().allocate(_1, _2))
 
 /// azcreate(class)
 #define azcreate_1(_1)                                          new(azmalloc_3(sizeof(_1), alignof( _1 ), AZ::SystemAllocator)) _1()

+ 15 - 12
Code/Framework/AzCore/AzCore/Memory/OSAllocator.cpp

@@ -8,6 +8,8 @@
 
 #include <AzCore/Memory/OSAllocator.h>
 
+#include <AzCore/Debug/MemoryProfiler.h>
+
 namespace AZ
 {
     OSAllocator::OSAllocator()
@@ -56,7 +58,7 @@ namespace AZ
     // Allocate
     // [9/2/2009]
     //=========================================================================
-    OSAllocator::pointer OSAllocator::allocate(size_type byteSize, size_type alignment)
+    AllocateAddress OSAllocator::allocate(size_type byteSize, size_type alignment)
     {
         pointer address = AZ_OS_MALLOC(byteSize, alignment);
 
@@ -67,39 +69,40 @@ namespace AZ
             OnOutOfMemory(byteSize, alignment);
         }
 
-#if defined(AZ_ENABLE_TRACING)
         // We assume 1 alignment because alignment is sometimes not passed in deallocate. This does mean that we are under-reporting
         // for cases where alignment != 1 and the OS could not find a block specifically for that alignment (the OS will give use a
-        // block that is byteSize+(alignment-1) and place the ptr in the first address that satisfies the alignment).
-        const size_type allocatedSize = get_allocated_size(address, 1);
+        // block that is byteSize + (alignment - 1) and place the ptr in the first address that satisfies the alignment).
+        size_type allocatedSize = get_allocated_size(address, 1);
+#if defined(AZ_ENABLE_TRACING)
         m_numAllocatedBytes += allocatedSize;
         AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
         AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, 1));
 #endif
 
-        return address;
+        return AllocateAddress{ address, allocatedSize };
     }
 
     //=========================================================================
     // DeAllocate
     // [9/2/2009]
     //=========================================================================
-    void OSAllocator::deallocate(pointer ptr, [[maybe_unused]] size_type byteSize, [[maybe_unused]] size_type alignment)
+    auto OSAllocator::deallocate(pointer ptr, [[maybe_unused]] size_type byteSize, [[maybe_unused]] size_type alignment)
+        -> size_type
     {
+        size_type allocatedSize = get_allocated_size(ptr, 1);
 #if defined(AZ_ENABLE_TRACING)
         if (ptr)
         {
-            const size_type allocatedSize = get_allocated_size(ptr, 1);
             m_numAllocatedBytes -= allocatedSize;
             AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
             AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
         }
 #endif
         AZ_OS_FREE(ptr);
-
+        return allocatedSize;
     }
 
-    OSAllocator::pointer OSAllocator::reallocate(pointer ptr, size_type newSize, align_type alignment)
+    AllocateAddress OSAllocator::reallocate(pointer ptr, size_type newSize, align_type alignment)
     {
 #if defined(AZ_ENABLE_TRACING)
         const size_type previouslyAllocatedSize = ptr ? get_allocated_size(ptr, 1) : 0;
@@ -107,17 +110,17 @@ namespace AZ
 
         pointer newPtr = AZ_OS_REALLOC(ptr, newSize, static_cast<AZStd::size_t>(alignment));
 
-#if defined(AZ_ENABLE_TRACING)
         const size_type allocatedSize = get_allocated_size(newPtr, 1);
+#if defined(AZ_ENABLE_TRACING)
         m_numAllocatedBytes += (allocatedSize - previouslyAllocatedSize);
         AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
         AZ_MEMORY_PROFILE(ProfileReallocation(ptr, newPtr, allocatedSize, 1));
 #endif
 
-        return newPtr;
+        return AllocateAddress{ newPtr, allocatedSize };
     }
 
-    OSAllocator::size_type OSAllocator::get_allocated_size(pointer ptr, align_type alignment) const
+    auto OSAllocator::get_allocated_size(pointer ptr, align_type alignment) const -> size_type
     {
         return ptr ? AZ_OS_MSIZE(ptr, alignment) : 0;
     }

+ 4 - 3
Code/Framework/AzCore/AzCore/Memory/OSAllocator.h

@@ -8,6 +8,7 @@
 #ifndef AZCORE_OS_ALLOCATOR_H
 #define AZCORE_OS_ALLOCATOR_H
 
+#include <AzCore/Memory/AllocatorBase.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/allocator.h>
 
@@ -45,9 +46,9 @@ namespace AZ
 
         //////////////////////////////////////////////////////////////////////////
         // IAllocator
-        pointer    allocate(size_type byteSize, size_type alignment) override;
-        void            deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
-        pointer         reallocate(pointer ptr, size_type newSize, align_type newAlignment) override;
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override;
+        size_type       deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, align_type newAlignment) override;
         size_type       get_allocated_size(pointer ptr, align_type alignment = 1) const override;
 
         size_type       NumAllocatedBytes() const override       { return m_numAllocatedBytes; }

+ 42 - 41
Code/Framework/AzCore/AzCore/Memory/PoolAllocator.cpp

@@ -120,8 +120,8 @@ namespace AZ
         PoolAllocation(Allocator* alloc, size_t pageSize, size_t minAllocationSize, size_t maxAllocationSize);
         virtual ~PoolAllocation();
 
-        void* Allocate(size_t byteSize, size_t alignment);
-        void DeAllocate(void* ptr);
+        AllocateAddress Allocate(size_t byteSize, size_t alignment);
+        size_t DeAllocate(void* ptr);
         size_t get_allocated_size(void* ptr) const;
         void GarbageCollect();
 
@@ -145,8 +145,8 @@ namespace AZ
         PoolSchemaImpl();
         ~PoolSchemaImpl();
 
-        PoolSchema::pointer Allocate(PoolSchema::size_type byteSize, PoolSchema::size_type alignment);
-        void DeAllocate(PoolSchema::pointer ptr);
+        AllocateAddress Allocate(PoolSchema::size_type byteSize, PoolSchema::size_type alignment);
+        PoolSchema::size_type DeAllocate(PoolSchema::pointer ptr);
         PoolSchema::size_type get_allocated_size(PoolSchema::pointer ptr, PoolSchema::align_type alignment) const;
 
         /**
@@ -225,7 +225,7 @@ namespace AZ
 
         inline Bucket* AllocateBuckets(size_t numBuckets)
         {
-            Bucket* buckets = reinterpret_cast<Bucket*>(m_pageAllocator->allocate(sizeof(Bucket) * numBuckets, alignof(Bucket)));
+            Bucket* buckets = reinterpret_cast<Bucket*>(static_cast<void*>(m_pageAllocator->allocate(sizeof(Bucket) * numBuckets, alignof(Bucket))));
             for (size_t i = 0; i < numBuckets; ++i)
             {
                 new (buckets + i) Bucket();
@@ -309,8 +309,8 @@ namespace AZ
 
         ~ThreadPoolSchemaImpl();
 
-        ThreadPoolSchema::pointer Allocate(ThreadPoolSchema::size_type byteSize, ThreadPoolSchema::size_type alignment);
-        void DeAllocate(ThreadPoolSchema::pointer ptr);
+        AllocateAddress Allocate(ThreadPoolSchema::size_type byteSize, ThreadPoolSchema::size_type alignment);
+        ThreadPoolSchema::size_type DeAllocate(ThreadPoolSchema::pointer ptr);
         ThreadPoolSchema::size_type get_allocated_size(ThreadPoolSchema::pointer ptr, ThreadPoolSchema::align_type alignment) const;
         /// Return unused memory to the OS. Don't call this too often because you will force unnecessary allocations.
         void GarbageCollect();
@@ -323,8 +323,8 @@ namespace AZ
         {
             // We store the page struct at the end of the block
             char* memBlock;
-            memBlock = reinterpret_cast<char*>(
-                m_pageAllocator->allocate(m_pageSize, m_pageSize));
+            memBlock = reinterpret_cast<char*>(static_cast<void*>(
+                m_pageAllocator->allocate(m_pageSize, m_pageSize)));
             size_t pageDataSize = m_pageSize - sizeof(Page);
             Page* page = new (memBlock + pageDataSize) Page(m_threadPoolGetter());
             page->SetupFreeList(elementSize, pageDataSize);
@@ -357,7 +357,7 @@ namespace AZ
 
         inline Bucket* AllocateBuckets(size_t numBuckets)
         {
-            Bucket* buckets = reinterpret_cast<Bucket*>(m_pageAllocator->allocate(sizeof(Bucket) * numBuckets, alignof(Bucket)));
+            Bucket* buckets = static_cast<Bucket*>(static_cast<void*>(m_pageAllocator->allocate(sizeof(Bucket) * numBuckets, alignof(Bucket))));
             for (size_t i = 0; i < numBuckets; ++i)
             {
                 new (buckets + i) Bucket();
@@ -481,7 +481,7 @@ namespace AZ
     // [9/09/2009]
     //=========================================================================
     template<class Allocator>
-    AZ_INLINE void* PoolAllocation<Allocator>::Allocate(size_t byteSize, size_t alignment)
+    inline AllocateAddress PoolAllocation<Allocator>::Allocate(size_t byteSize, size_t alignment)
     {
         AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
         AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
@@ -493,7 +493,7 @@ namespace AZ
         if (byteSize > m_maxAllocationSize)
         {
             AZ_Assert(false, "Allocation size (%d) is too big (max: %d) for pools!", byteSize, m_maxAllocationSize);
-            return nullptr;
+            return AllocateAddress{};
         }
 
         if (!m_buckets)
@@ -552,7 +552,7 @@ namespace AZ
 
         m_numBytesAllocated += byteSize;
 
-        return address;
+        return AllocateAddress{ address, byteSize };
     }
 
     //=========================================================================
@@ -560,13 +560,13 @@ namespace AZ
     // [9/09/2009]
     //=========================================================================
     template<class Allocator>
-    AZ_INLINE void PoolAllocation<Allocator>::DeAllocate(void* ptr)
+    inline size_t PoolAllocation<Allocator>::DeAllocate(void* ptr)
     {
         PageType* page = m_allocator->PageFromAddress(ptr);
         if (page == nullptr)
         {
             AZ_Error("Memory", false, "Address 0x%08x is not in the ThreadPool!", ptr);
-            return;
+            return 0;
         }
 
         // (pageSize - info struct at the end) / (element size)
@@ -613,7 +613,9 @@ namespace AZ
             }
         }
 
-        m_numBytesAllocated -= page->m_elementSize;
+        size_t bytesDeallocated = page->m_elementSize;
+        m_numBytesAllocated -= bytesDeallocated;
+        return bytesDeallocated;
     }
 
     //=========================================================================
@@ -709,9 +711,7 @@ namespace AZ
     // Allocate
     // [9/15/2009]
     //=========================================================================
-    PoolSchema::pointer PoolSchema::allocate(
-        size_type byteSize,
-        size_type alignment)
+    AllocateAddress PoolSchema::allocate(size_type byteSize, size_type alignment)
     {
         return m_impl->Allocate(byteSize, alignment);
     }
@@ -720,32 +720,32 @@ namespace AZ
     // DeAllocate
     // [9/15/2009]
     //=========================================================================
-    void PoolSchema::deallocate(pointer ptr, size_type byteSize, size_type alignment)
+    auto PoolSchema::deallocate(pointer ptr, size_type byteSize, size_type alignment) -> size_type
     {
         (void)byteSize;
         (void)alignment;
-        m_impl->DeAllocate(ptr);
+        return m_impl->DeAllocate(ptr);
     }
 
     //=========================================================================
     // ReAllocate
     // [10/14/2018]
     //=========================================================================
-    PoolSchema::pointer PoolSchema::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
+    AllocateAddress PoolSchema::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
     {
         (void)ptr;
         (void)newSize;
         (void)newAlignment;
         AZ_Assert(false, "unsupported");
 
-        return ptr;
+        return AllocateAddress{ ptr, get_allocated_size(ptr, newAlignment) };
     }
 
     //=========================================================================
     // AllocationSize
     // [11/22/2010]
     //=========================================================================
-    PoolSchema::size_type PoolSchema::get_allocated_size(pointer ptr, align_type alignment) const
+    auto PoolSchema::get_allocated_size(pointer ptr, align_type alignment) const -> size_type
     {
         return m_impl->get_allocated_size(ptr, alignment);
     }
@@ -766,7 +766,7 @@ namespace AZ
     // NumAllocatedBytes
     // [11/1/2010]
     //=========================================================================
-    PoolSchema::size_type PoolSchema::NumAllocatedBytes() const
+    auto PoolSchema::NumAllocatedBytes() const -> size_type
     {
         return m_impl->m_allocator.m_numBytesAllocated;
     }
@@ -808,23 +808,22 @@ namespace AZ
     // Allocate
     // [9/15/2009]
     //=========================================================================
-    PoolSchema::pointer PoolSchemaImpl::Allocate(PoolSchema::size_type byteSize, PoolSchema::size_type alignment)
+    AllocateAddress PoolSchemaImpl::Allocate(PoolSchema::size_type byteSize, PoolSchema::size_type alignment)
     {
         // AZ_Warning("Memory",m_ownerThread==AZStd::this_thread::get_id(),"You can't allocation from a different context/thread, use
         // ThreadPoolAllocator!");
-        void* address = m_allocator.Allocate(byteSize, alignment);
-        return address;
+        return m_allocator.Allocate(byteSize, alignment);
     }
 
     //=========================================================================
     // DeAllocate
     // [9/15/2009]
     //=========================================================================
-    void PoolSchemaImpl::DeAllocate(PoolSchema::pointer ptr)
+    PoolSchema::size_type PoolSchemaImpl::DeAllocate(PoolSchema::pointer ptr)
     {
         // AZ_Warning("Memory",m_ownerThread==AZStd::this_thread::get_id(),"You can't deallocate from a different context/thread, use
         // ThreadPoolAllocator!");
-        m_allocator.DeAllocate(ptr);
+        return m_allocator.DeAllocate(ptr);
     }
 
     //=========================================================================
@@ -944,9 +943,7 @@ namespace AZ
     // Allocate
     // [9/15/2009]
     //=========================================================================
-    ThreadPoolSchema::pointer ThreadPoolSchema::allocate(
-        size_type byteSize,
-        size_type alignment)
+    AllocateAddress ThreadPoolSchema::allocate(size_type byteSize, size_type alignment)
     {
         return m_impl->Allocate(byteSize, alignment);
     }
@@ -955,25 +952,25 @@ namespace AZ
     // DeAllocate
     // [9/15/2009]
     //=========================================================================
-    void ThreadPoolSchema::deallocate(pointer ptr, size_type byteSize, size_type alignment)
+    auto ThreadPoolSchema::deallocate(pointer ptr, size_type byteSize, size_type alignment) -> size_type
     {
         (void)byteSize;
         (void)alignment;
-        m_impl->DeAllocate(ptr);
+        return m_impl->DeAllocate(ptr);
     }
 
     //=========================================================================
     // ReAllocate
     // [10/14/2018]
     //=========================================================================
-    ThreadPoolSchema::pointer ThreadPoolSchema::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
+    AllocateAddress ThreadPoolSchema::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
     {
         (void)ptr;
         (void)newSize;
         (void)newAlignment;
         AZ_Assert(false, "unsupported");
 
-        return ptr;
+        return AllocateAddress{ ptr, get_allocated_size(ptr, newAlignment) };
     }
 
     //=========================================================================
@@ -1082,7 +1079,7 @@ namespace AZ
     // Allocate
     // [9/15/2009]
     //=========================================================================
-    ThreadPoolSchema::pointer ThreadPoolSchemaImpl::Allocate(
+    AllocateAddress ThreadPoolSchemaImpl::Allocate(
         ThreadPoolSchema::size_type byteSize, ThreadPoolSchema::size_type alignment)
     {
         ThreadPoolData* threadData = m_threadPoolGetter();
@@ -1114,20 +1111,20 @@ namespace AZ
     // DeAllocate
     // [9/15/2009]
     //=========================================================================
-    void ThreadPoolSchemaImpl::DeAllocate(ThreadPoolSchema::pointer ptr)
+    ThreadPoolSchema::size_type ThreadPoolSchemaImpl::DeAllocate(ThreadPoolSchema::pointer ptr)
     {
         Page* page = PageFromAddress(ptr);
         if (page == nullptr)
         {
             AZ_Error("Memory", false, "Address 0x%08x is not in the ThreadPool!", ptr);
-            return;
+            return 0;
         }
         AZ_Assert(page->m_threadData != nullptr, ("We must have valid page thread data for the page!"));
         ThreadPoolData* threadData = m_threadPoolGetter();
         if (threadData == page->m_threadData)
         {
             // we can free here
-            threadData->m_allocator.DeAllocate(ptr);
+            return threadData->m_allocator.DeAllocate(ptr);
         }
         else
         {
@@ -1139,7 +1136,11 @@ namespace AZ
             // otherwise we will assert the node is in the list
             fakeLFNode->m_next = 0;
 #endif
+            // Query the allocated size of the ptr before pushing it on the freed element stack
+            const size_t allocatedSize = page->m_threadData->m_allocator.get_allocated_size(ptr);
             page->m_threadData->m_freedElements.push(*fakeLFNode);
+
+            return allocatedSize;
         }
     }
 

+ 10 - 14
Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h

@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <AzCore/Memory/SimpleSchemaAllocator.h>
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Memory/AllocationRecords.h>
 
@@ -35,9 +36,9 @@ namespace AZ
         bool Create(
             PoolSchema::size_type pageSize, PoolSchema::size_type minAllocationSize, PoolSchema::size_type maxAllocationSize);
 
-        pointer allocate(size_type byteSize, size_type alignment) override;
-        void deallocate(pointer ptr, size_type byteSize, size_type alignment) override;
-        pointer reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override;
+        size_type deallocate(pointer ptr, size_type byteSize, size_type alignment) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
         size_type get_allocated_size(pointer ptr, align_type alignment) const override;
 
         /// Return unused memory to the OS. Don't call this too often because you will force unnecessary allocations.
@@ -73,9 +74,9 @@ namespace AZ
         bool Create();
         bool Create(PoolSchema::size_type pageSize, PoolSchema::size_type minAllocationSize, PoolSchema::size_type maxAllocationSize);
 
-        pointer allocate(size_type byteSize, size_type alignment) override;
-        void deallocate(pointer ptr, size_type byteSize, size_type alignment) override;
-        pointer reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override;
+        size_type deallocate(pointer ptr, size_type byteSize, size_type alignment) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
         size_type get_allocated_size(pointer ptr, align_type alignment) const override;
         /// Return unused memory to the OS. Don't call this too often because you will force unnecessary allocations.
         void GarbageCollect() override;
@@ -155,29 +156,24 @@ namespace AZ::Internal
         PoolAllocatorHelper()
         {
             static_cast<Schema*>(this->m_schema)->Create();
-            this->PostCreate();
         }
 
         PoolAllocatorHelper(size_t pageSize, size_t minAllocationSize, size_t maxAllocationSize)
         {
             static_cast<Schema*>(this->m_schema)->Create(pageSize, minAllocationSize, maxAllocationSize);
-            this->PostCreate();
         }
 
-        ~PoolAllocatorHelper() override
-        {
-            this->PreDestroy();
-        }
+        ~PoolAllocatorHelper() override = default;
 
         //////////////////////////////////////////////////////////////////////////
         // IAllocator
-        pointer reallocate(pointer ptr, size_type newSize, size_type newAlignment) override
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override
         {
             (void)ptr;
             (void)newSize;
             (void)newAlignment;
             AZ_Assert(false, "Not supported!");
-            return nullptr;
+            return AllocateAddress{};
         }
 
         //////////////////////////////////////////////////////////////////////////

+ 60 - 23
Code/Framework/AzCore/AzCore/Memory/SimpleSchemaAllocator.h

@@ -12,19 +12,29 @@
 #include <AzCore/std/typetraits/aligned_storage.h>
 #include <AzCore/std/typetraits/alignment_of.h>
 #include <AzCore/Memory/AllocatorBase.h>
+#include <AzCore/Memory/AllocatorManager.h>
 #include <AzCore/Debug/MemoryProfiler.h>
 
 namespace AZ
 {
+    class SimpleSchemaAllocatorBase
+        : public AllocatorBase
+    {
+    public:
+        AZ_RTTI(SimpleSchemaAllocatorBase, "{6B1DB724-B861-41A0-9FCF-6A5943007CA0}", AllocatorBase);
+        using AllocatorBase::AllocatorBase;
+        virtual IAllocator* GetSchema() const = 0;
+    };
+
     /**
     * A basic, default allocator implementation using a custom schema.
     */
-    template <class Schema, bool ProfileAllocations=true, bool ReportOutOfMemory=true>
+    template<class Schema, bool ProfileAllocations = true, bool ReportOutOfMemory = true>
     class SimpleSchemaAllocator
-        : public AllocatorBase
+        : public SimpleSchemaAllocatorBase
     {
     public:
-        AZ_RTTI((SimpleSchemaAllocator, "{32019C72-6E33-4EF9-8ABA-748055D94EB2}", Schema), AllocatorBase);
+        AZ_RTTI((SimpleSchemaAllocator, "{32019C72-6E33-4EF9-8ABA-748055D94EB2}", Schema), SimpleSchemaAllocatorBase);
 
         using pointer = typename Schema::pointer;
         using size_type = typename Schema::size_type;
@@ -34,10 +44,14 @@ namespace AZ
         {
             SetProfilingActive(ProfileAllocations);
             Create();
+
+            // Register the SimpleSchemaAllocator with the Allocator Manager
+            PostCreate();
         }
 
         ~SimpleSchemaAllocator() override
         {
+            PreDestroy();
             if (m_schema)
             {
                 reinterpret_cast<Schema*>(&m_schemaStorage)->~Schema();
@@ -48,6 +62,11 @@ namespace AZ
         bool Create()
         {
             m_schema = new (&m_schemaStorage) Schema();
+
+            // As the Simple Schema Allocator is registered
+            // with the Allocator Manager, unregister the Schema from the manager
+            auto& allocatorManager = AllocatorManager::Instance();
+            allocatorManager.UnRegisterAllocator(m_schema);
             return m_schema != nullptr;
         }
 
@@ -59,61 +78,68 @@ namespace AZ
         //---------------------------------------------------------------------
         // IAllocator
         //---------------------------------------------------------------------
-        pointer allocate(size_type byteSize, size_type alignment) override
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override
         {
             byteSize = MemorySizeAdjustedUp(byteSize);
-            pointer ptr = m_schema->allocate(byteSize, alignment);
+            const AllocateAddress ptr = m_schema->allocate(byteSize, alignment);
+            m_totalAllocatedBytes += ptr.GetAllocatedBytes();
 
-            if (ProfileAllocations)
+            if constexpr (ProfileAllocations)
             {
                 AZ_MEMORY_PROFILE(ProfileAllocation(ptr, byteSize, alignment, 1));
             }
 
-            AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // conditional expression is constant
-            if (ReportOutOfMemory && !ptr)
-            AZ_POP_DISABLE_WARNING
+            if constexpr (ReportOutOfMemory)
             {
-                OnOutOfMemory(byteSize, alignment);
+                if (ptr == nullptr)
+                {
+                    OnOutOfMemory(byteSize, alignment);
+                }
             }
 
             return ptr;
         }
 
-        void deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override
+        size_type deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override
         {
             byteSize = MemorySizeAdjustedUp(byteSize);
 
-            if (ProfileAllocations)
+            if constexpr (ProfileAllocations)
             {
                 AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
                 AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
             }
 
-            m_schema->deallocate(ptr, byteSize, alignment);
+            const size_type bytesDeallocated = m_schema->deallocate(ptr, byteSize, alignment);
+            m_totalAllocatedBytes -= bytesDeallocated;
+            return bytesDeallocated;
         }
 
-        pointer reallocate(pointer ptr, size_type newSize, size_type newAlignment = 1) override
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment = 1) override
         {
-            if (ProfileAllocations)
+            if constexpr (ProfileAllocations)
             {
                 AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
             }
 
             newSize = MemorySizeAdjustedUp(newSize);
 
-            pointer newPtr = m_schema->reallocate(ptr, newSize, newAlignment);
+            const size_type oldAllocatedSize = get_allocated_size(ptr, 1);
+            AllocateAddress newPtr = m_schema->reallocate(ptr, newSize, newAlignment);
+            m_totalAllocatedBytes += newPtr.GetAllocatedBytes() - oldAllocatedSize;
 
-            if (ProfileAllocations)
+            if constexpr (ProfileAllocations)
             {
                 AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newPtr, newSize, GetName());
                 AZ_MEMORY_PROFILE(ProfileReallocation(ptr, newPtr, newSize, newAlignment));
             }
 
-            AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // conditional expression is constant
-            if (ReportOutOfMemory && newSize && !newPtr)
-            AZ_POP_DISABLE_WARNING
+            if constexpr (ReportOutOfMemory)
             {
-                OnOutOfMemory(newSize, newAlignment);
+                if (newSize && newPtr == nullptr)
+                {
+                    OnOutOfMemory(newSize, newAlignment);
+                }
             }
 
             return newPtr;
@@ -131,12 +157,23 @@ namespace AZ
 
         size_type NumAllocatedBytes() const override
         {
-            return m_schema->NumAllocatedBytes();
+            AZ_Assert(
+                m_totalAllocatedBytes >= 0,
+                "Total allocated bytes is less than zero with a value of %td. Was deallocate() invoked with an address "
+                "that is not associated with the allocator? This should never occur",
+                m_totalAllocatedBytes.load());
+            return static_cast<size_type>(m_totalAllocatedBytes);
+        }
+
+        IAllocator* GetSchema() const override
+        {
+            return m_schema;
         }
 
     protected:
         IAllocator* m_schema{};
     private:
-        typename AZStd::aligned_storage<sizeof(Schema), AZStd::alignment_of<Schema>::value>::type m_schemaStorage;
+        AZStd::aligned_storage_for_t<Schema> m_schemaStorage;
+        AZStd::atomic<ptrdiff_t> m_totalAllocatedBytes{};
     };
 }

+ 11 - 12
Code/Framework/AzCore/AzCore/Memory/SystemAllocator.cpp

@@ -7,13 +7,14 @@
  */
 
 #include <AzCore/Memory/SystemAllocator.h>
-#include <AzCore/Memory/AllocatorManager.h>
 
-#include <AzCore/Memory/OSAllocator.h>
+#include <AzCore/Memory/AllocatorManager.h>
 #include <AzCore/Memory/AllocationRecords.h>
+#include <AzCore/Memory/OSAllocator.h>
 
 #include <AzCore/std/functional.h>
 
+#include <AzCore/Debug/MemoryProfiler.h>
 #include <AzCore/Debug/Profiler.h>
 #include <memory>
 
@@ -67,19 +68,17 @@ namespace AZ
     // Allocate
     // [9/2/2009]
     //=========================================================================
-    SystemAllocator::pointer SystemAllocator::allocate(
-        size_type byteSize,
-        size_type alignment)
+    AllocateAddress SystemAllocator::allocate(size_type byteSize, size_type alignment)
     {
         if (byteSize == 0)
         {
-            return nullptr;
+            return AllocateAddress{};
         }
         AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
         AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
 
         byteSize = MemorySizeAdjustedUp(byteSize);
-        SystemAllocator::pointer address =
+        AllocateAddress address =
             m_subAllocator->allocate(byteSize, alignment);
 
         if (address == nullptr)
@@ -109,25 +108,25 @@ namespace AZ
     // DeAllocate
     // [9/2/2009]
     //=========================================================================
-    void SystemAllocator::deallocate(pointer ptr, size_type byteSize, size_type alignment)
+    auto SystemAllocator::deallocate(pointer ptr, size_type byteSize, size_type alignment) -> size_type
     {
         byteSize = MemorySizeAdjustedUp(byteSize);
         AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
         AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
-        m_subAllocator->deallocate(ptr, byteSize, alignment);
+        return m_subAllocator->deallocate(ptr, byteSize, alignment);
     }
 
     //=========================================================================
     // ReAllocate
     // [9/13/2011]
     //=========================================================================
-    SystemAllocator::pointer SystemAllocator::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
+    AllocateAddress SystemAllocator::reallocate(pointer ptr, size_type newSize, size_type newAlignment)
     {
         newSize = MemorySizeAdjustedUp(newSize);
 
         AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
 
-        pointer newAddress = m_subAllocator->reallocate(ptr, newSize, newAlignment);
+        AllocateAddress newAddress = m_subAllocator->reallocate(ptr, newSize, newAlignment);
 
 #if defined(AZ_ENABLE_TRACING)
         [[maybe_unused]] const size_type allocatedSize = get_allocated_size(newAddress, 1);
@@ -142,7 +141,7 @@ namespace AZ
     //
     // [8/12/2011]
     //=========================================================================
-    SystemAllocator::size_type SystemAllocator::get_allocated_size(pointer ptr, align_type alignment) const
+    auto SystemAllocator::get_allocated_size(pointer ptr, align_type alignment) const -> size_type
     {
         size_type allocSize = MemorySizeAdjustedDown(m_subAllocator->get_allocated_size(ptr, alignment));
 

+ 4 - 3
Code/Framework/AzCore/AzCore/Memory/SystemAllocator.h

@@ -7,6 +7,7 @@
  */
 #pragma once
 
+#include <AzCore/Memory/AllocatorBase.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/smart_ptr/unique_ptr.h>
 
@@ -42,9 +43,9 @@ namespace AZ
         //////////////////////////////////////////////////////////////////////////
         // IAllocator
 
-        pointer         allocate(size_type byteSize, size_type alignment) override;
-        void            deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
-        pointer         reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
+        AllocateAddress allocate(size_type byteSize, size_type alignment) override;
+        size_type       deallocate(pointer ptr, size_type byteSize = 0, size_type alignment = 0) override;
+        AllocateAddress reallocate(pointer ptr, size_type newSize, size_type newAlignment) override;
         size_type get_allocated_size(pointer ptr, size_type alignment) const override;
         void            GarbageCollect() override                 { m_subAllocator->GarbageCollect(); }
 

+ 2 - 2
Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp

@@ -7,7 +7,7 @@
  */
 #include <AzCore/Script/ScriptContext.h>
 #include <AzCore/Casting/numeric_cast.h>
-#include <AzCore/Memory/AllocatorWrappers.h>
+#include <AzCore/Memory/ChildAllocatorSchema.h>
 #include <AzCore/RTTI/BehaviorContext.h>
 #include <AzCore/RTTI/BehaviorContextUtilities.h>
 #include <AzCore/Script/ScriptContextDebug.h>
@@ -398,7 +398,7 @@ namespace AZ
         };
 
         ///////////////////////////////////////////////////////////////////////////////////////////////
-        AZ_ALLOCATOR_DEFAULT_GLOBAL_WRAPPER(LuaSystemAllocator, AZ::SystemAllocator, "{7BEFB496-76EC-43DB-AB82-5ABA524FEF7F}")
+        AZ_CHILD_ALLOCATOR_WITH_NAME(LuaSystemAllocator, "LuaSystemAllocator", "{7BEFB496-76EC-43DB-AB82-5ABA524FEF7F}", AZ::SystemAllocator);
 
         //=========================================================================
         // azlua_setglobal - raw setglobal function (no metamethods called)

+ 2 - 2
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp

@@ -740,7 +740,7 @@ namespace AZ::SettingsRegistryMergeUtils
     // check for a default write storage path, fall back to the <project-user-path> if not
     static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath)
     {
-        AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
+        AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDefaultDevWriteStoragePath();
         AZ::IO::FixedMaxPath devWriteStoragePath = devWriteStorage.has_value() ? *devWriteStorage : projectUserPath;
         if (devWriteStoragePath.IsRelative())
         {
@@ -1115,7 +1115,7 @@ namespace AZ::SettingsRegistryMergeUtils
             registry.Set(FilePathKey_CacheProjectRootFolder, projectPath.Native());
             registry.Set(FilePathKey_CacheRootFolder, projectPath.Native());
         }
-        if (AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
+        if (AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDefaultDevWriteStoragePath();
             devWriteStorage)
         {
             const auto devWriteStoragePath = AZ::IO::PathView(*devWriteStorage).LexicallyNormal();

+ 12 - 12
Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h

@@ -110,16 +110,16 @@ namespace UnitTest
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
             m_allocatedSizes = GetAllocatedSizes();
         }
 
         ~LeakDetectionFixture() override
         {
             CheckAllocatorsForLeaks();
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
             AZ::AllocatorManager::Instance().ExitProfilingMode();
         }
@@ -143,24 +143,24 @@ namespace UnitTest
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
             m_allocatedSizes = GetAllocatedSizes();
         }
         void SetUp(::benchmark::State&) override
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
             m_allocatedSizes = GetAllocatedSizes();
         }
 
         void TearDown(const ::benchmark::State&) override
         {
             CheckAllocatorsForLeaks();
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
             AZ::AllocatorManager::Instance().ExitProfilingMode();
         }
@@ -168,8 +168,8 @@ namespace UnitTest
         void TearDown(::benchmark::State&) override
         {
             CheckAllocatorsForLeaks();
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
             AZ::AllocatorManager::Instance().ExitProfilingMode();
         }

+ 18 - 0
Code/Framework/AzCore/AzCore/Utils/Utils.cpp

@@ -312,6 +312,24 @@ namespace AZ::Utils
         return {};
     }
 
+    AZ::IO::FixedMaxPathString GetDevWriteStoragePath(AZ::SettingsRegistryInterface* settingsRegistry)
+    {
+        if (settingsRegistry == nullptr)
+        {
+            settingsRegistry = AZ::SettingsRegistry::Get();
+        }
+
+        if (settingsRegistry != nullptr)
+        {
+            if (AZ::IO::FixedMaxPathString settingsValue;
+                settingsRegistry->Get(settingsValue, AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage))
+            {
+                return settingsValue;
+            }
+        }
+        return {};
+    }
+
     AZ::IO::FixedMaxPathString GetProjectProductPathForPlatform(AZ::SettingsRegistryInterface* settingsRegistry)
     {
         if (settingsRegistry == nullptr)

+ 10 - 5
Code/Framework/AzCore/AzCore/Utils/Utils.h

@@ -93,10 +93,10 @@ namespace AZ
         //! If nullptr, the AZ::Interface instance of the SettingsRegistry is used
         AZ::IO::FixedMaxPathString GetProjectLogPath(AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
 
-        //! Retrieves the full path to the project  path for the current asset platform from the settings registry
+        //! Retrieves the full path to the project path for the current asset platform from the settings registry
         //! This path is based on <project-cache-path>/<asset-platform>,
         //! where on Windows <asset-platform> = "pc", Linux  <asset-platform> = linux, etc...
-        //! The list of OS -> asst platforms is available in the `AZ::PlatformDefaults::OSPlatformToDefaultAssetPlatform` fuction
+        //! The list of OS -> asset platforms is available in the `AZ::PlatformDefaults::OSPlatformToDefaultAssetPlatform` function
         //! @param settingsRegistry pointer to the SettingsRegistry to use for lookup
         //! If nullptr, the AZ::Interface instance of the SettingsRegistry is used
         AZ::IO::FixedMaxPathString GetProjectProductPathForPlatform(AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
@@ -135,17 +135,22 @@ namespace AZ
         AZ::IO::FixedMaxPathString GetO3deManifestPath(AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
 
         //! Retrieves the full directory to the O3DE logs directory, i.e. "<userhome>/.o3de/Logs"
-        //! //! @param settingsRegistry pointer to the SettingsRegistry to use for lookup
+        //! @param settingsRegistry pointer to the SettingsRegistry to use for lookup
         //! If nullptr, the AZ::Interface instance of the SettingsRegistry is used
         AZ::IO::FixedMaxPathString GetO3deLogsDirectory(AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
 
+        //! Retrieves a full directory which can be used to write files into during development
+        //! @param settingsRegistry pointer to the SettingsRegistry to use for lookup
+        //! If nullptr, the AZ::Interface instance of the SettingsRegistry is used
+        AZ::IO::FixedMaxPathString GetDevWriteStoragePath(AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
+
         //! Retrieves the application root path for a non-host platform
         //! On host platforms this returns a nullopt
         AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath();
 
-        //! Retrieves the development write storage path to use on the current platform, may be considered
+        //! Retrieves the default development write storage path to use on the current platform, may be considered
         //! temporary or cache storage
-        AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath();
+        AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath();
 
         //! Attempts the supplied path to an absolute path.
         //! Returns nullopt if path cannot be converted to an absolute path

+ 10 - 10
Code/Framework/AzCore/AzCore/std/containers/deque.h

@@ -340,7 +340,7 @@ namespace AZStd
             size_type block = --newOffset / NumElementsPerBlock;
             if (m_map[block] == 0)
             {
-                m_map[block] = reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             // copy construct
@@ -384,11 +384,11 @@ namespace AZStd
             size_type block = newOffset / NumElementsPerBlock;
             if (m_mapSize <= block)
             {
-                block  -= m_mapSize;
+                block -= m_mapSize;
             }
             if (m_map[block] == 0)
             {
-                m_map[block] =  reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             // copy construct
@@ -649,7 +649,7 @@ namespace AZStd
             {   // free storage for a block and destroy pointer
                 if (*(m_map + --i) != 0)
                 {
-                    deallocate_memory(*(m_map + i), sizeof(block_node_type), alignment_of<block_node_type>::value);
+                    deallocate_memory(*(m_map + i), sizeof(block_node_type), alignof(block_node_type));
                 }
                 map_node_ptr_type toDestroy = m_map + i;
                 Internal::destroy<map_node_ptr_type>::single(toDestroy);
@@ -745,7 +745,7 @@ namespace AZStd
             size_type block = --newOffset / NumElementsPerBlock;
             if (m_map[block] == 0)
             {
-                m_map[block] = reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             map_node_type ptr = m_map[block] + newOffset % NumElementsPerBlock;
@@ -768,11 +768,11 @@ namespace AZStd
             size_type block = newOffset / NumElementsPerBlock;
             if (m_mapSize <= block)
             {
-                block  -= m_mapSize;
+                block -= m_mapSize;
             }
             if (m_map[block] == 0)
             {
-                m_map[block] =  reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             map_node_type ptr = m_map[block] + newOffset % NumElementsPerBlock;
@@ -794,7 +794,7 @@ namespace AZStd
             size_type block = --newOffset / NumElementsPerBlock;
             if (m_map[block] == 0)
             {
-                m_map[block] = reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             map_node_type ptr = m_map[block] + newOffset % NumElementsPerBlock;
@@ -818,11 +818,11 @@ namespace AZStd
             size_type block = newOffset / NumElementsPerBlock;
             if (m_mapSize <= block)
             {
-                block  -= m_mapSize;
+                block -= m_mapSize;
             }
             if (m_map[block] == 0)
             {
-                m_map[block] =  reinterpret_cast<pointer>(m_allocator.allocate(sizeof(block_node_type), alignment_of<block_node_type>::value));
+                m_map[block] = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(sizeof(block_node_type), alignof(block_node_type))));
             }
 
             map_node_type ptr = m_map[block] + newOffset % NumElementsPerBlock;

+ 2 - 2
Code/Framework/AzCore/AzCore/std/containers/forward_list.h

@@ -447,7 +447,7 @@ namespace AZStd
         template<class... Args>
         iterator emplace_after(const_iterator insertPos, Args&&... args)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignof(node_type)));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
             // forward to the constructor
             pointer ptr = &newNode->m_value;
@@ -487,7 +487,7 @@ namespace AZStd
 
             for (bool firstIteration = true; 0 < numElements; --numElements, firstIteration = false)
             {
-                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
                 AZSTD_CONTAINER_ASSERT(newNode != nullptr, "AZSTD::forward_list::insert - failed to allocate node!");
 
                 if (firstIteration)

+ 11 - 11
Code/Framework/AzCore/AzCore/std/containers/list.h

@@ -407,7 +407,7 @@ namespace AZStd
 
         inline iterator insert(const_iterator insertPos, const_reference value)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
             // copy construct
             pointer ptr = &newNode->m_value;
@@ -440,7 +440,7 @@ namespace AZStd
 
             for (; 0 < numElements; --numElements)
             {
-                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
                 // copy construct
                 pointer ptr = &newNode->m_value;
                 Internal::construct<pointer>::single(ptr, value);
@@ -581,7 +581,7 @@ namespace AZStd
                 this_type temp(m_allocator);
 
                 // Different allocators, move elements
-                for (auto& element : * this)
+                for (auto& element : *this)
                 {
                     temp.push_back(AZStd::move(element));
                 }
@@ -980,8 +980,8 @@ namespace AZStd
         * @{
         */
         // The only difference from the standard is that we return the allocator instance, not a copy.
-        AZ_FORCE_INLINE allocator_type&         get_allocator()         { return m_allocator; }
-        AZ_FORCE_INLINE const allocator_type&   get_allocator() const   { return m_allocator; }
+        AZ_FORCE_INLINE allocator_type& get_allocator() { return m_allocator; }
+        AZ_FORCE_INLINE const allocator_type& get_allocator() const { return m_allocator; }
         /// Set the vector allocator. If different than then current all elements will be reallocated.
         inline void                     set_allocator(const allocator_type& allocator)
         {
@@ -1050,7 +1050,7 @@ namespace AZStd
         */
         inline iterator insert(iterator insertPos)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
             // construct
             pointer ptr = &newNode->m_value;
@@ -1073,7 +1073,7 @@ namespace AZStd
 
         inline iterator insert_after(iterator insertPos, const_reference value)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
             // copy construct
             pointer ptr = &newNode->m_value;
@@ -1104,9 +1104,9 @@ namespace AZStd
             base_node_ptr_type insNode = insertPos.m_node;
 #endif
 
-            for (; 0  < numElements; --numElements)
+            for (; 0 < numElements; --numElements)
             {
-                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+                node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
                 // copy construct
                 pointer ptr = &newNode->m_value;
@@ -1169,7 +1169,7 @@ namespace AZStd
     protected:
         AZ_FORCE_INLINE void    deallocate_node(node_ptr_type node)
         {
-            m_allocator.deallocate(node, sizeof(node_type), alignment_of<node_type>::value);
+            m_allocator.deallocate(node, sizeof(node_type), alignof(node_type));
         }
 
         template <class InputIterator>
@@ -1226,7 +1226,7 @@ namespace AZStd
         template<class ... ArgumentsInputs>
         reference insert_element(const_iterator insertPos, ArgumentsInputs&& ... arguments)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
 
             // construct
             pointer ptr = &newNode->m_value;

+ 2 - 2
Code/Framework/AzCore/AzCore/std/containers/rbtree.h

@@ -1140,7 +1140,7 @@ namespace AZStd
     private:
         inline base_node_ptr_type create_node(const value_type& value)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
             AZSTD_CONTAINER_ASSERT(newNode != NULL, "AZStd::rb_tree::create_node - failed to allocate node!");
 
             // copy construct
@@ -1155,7 +1155,7 @@ namespace AZStd
         template<class ... InputArguments>
         inline base_node_ptr_type create_node(InputArguments&& ... arguments)
         {
-            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+            node_ptr_type newNode = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
             AZSTD_CONTAINER_ASSERT(newNode, "AZStd::rb_tree::create_node - failed to allocate node!");
 
             pointer ptr = &newNode->m_value;

+ 26 - 26
Code/Framework/AzCore/AzCore/std/containers/ring_buffer.h

@@ -289,7 +289,7 @@ namespace AZStd
             }
             clear();
             deallocate_memory();
-            m_buff = reinterpret_cast<pointer>(m_allocator.allocate(rhs.capacity() * sizeof(node_type), alignment_of<node_type>::value));
+            m_buff = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(rhs.capacity() * sizeof(node_type), alignof(node_type))));
             m_end = m_buff + rhs.capacity();
             m_first = m_buff;
             m_size = rhs.m_size;
@@ -301,25 +301,25 @@ namespace AZStd
             return *this;
         }
 
-        AZ_FORCE_INLINE iterator            begin()         { return iterator(AZSTD_CHECKED_ITERATOR_2(iterator, this, (m_size == 0) ? nullptr : m_first)); }
-        AZ_FORCE_INLINE const_iterator  begin() const   { return const_iterator(AZSTD_CHECKED_ITERATOR_2(const_iterator, this, (m_size == 0) ? nullptr : m_first)); }
-        AZ_FORCE_INLINE iterator            end()           { return iterator(AZSTD_CHECKED_ITERATOR_2(iterator, this, nullptr)); }
-        AZ_FORCE_INLINE const_iterator  end() const     { return const_iterator(AZSTD_CHECKED_ITERATOR_2(const_iterator, this, nullptr)); }
+        AZ_FORCE_INLINE iterator            begin() { return iterator(AZSTD_CHECKED_ITERATOR_2(iterator, this, (m_size == 0) ? nullptr : m_first)); }
+        AZ_FORCE_INLINE const_iterator  begin() const { return const_iterator(AZSTD_CHECKED_ITERATOR_2(const_iterator, this, (m_size == 0) ? nullptr : m_first)); }
+        AZ_FORCE_INLINE iterator            end() { return iterator(AZSTD_CHECKED_ITERATOR_2(iterator, this, nullptr)); }
+        AZ_FORCE_INLINE const_iterator  end() const { return const_iterator(AZSTD_CHECKED_ITERATOR_2(const_iterator, this, nullptr)); }
 
-        AZ_FORCE_INLINE reverse_iterator            rbegin()        { return reverse_iterator(end()); }
-        AZ_FORCE_INLINE const_reverse_iterator  rbegin() const  { return const_reverse_iterator(end()); }
-        AZ_FORCE_INLINE reverse_iterator            rend()          { return reverse_iterator(begin()); }
-        AZ_FORCE_INLINE const_reverse_iterator  rend() const    { return const_reverse_iterator(begin()); }
+        AZ_FORCE_INLINE reverse_iterator            rbegin() { return reverse_iterator(end()); }
+        AZ_FORCE_INLINE const_reverse_iterator  rbegin() const { return const_reverse_iterator(end()); }
+        AZ_FORCE_INLINE reverse_iterator            rend() { return reverse_iterator(begin()); }
+        AZ_FORCE_INLINE const_reverse_iterator  rend() const { return const_reverse_iterator(begin()); }
 
-        AZ_FORCE_INLINE reference operator[] (size_type index)              { return *add(m_first, index); }
-        AZ_FORCE_INLINE const_reference operator[] (size_type index) const  { return *add(m_first, index); }
-        AZ_FORCE_INLINE reference at(size_type index)                       { return *add(m_first, index); }
-        AZ_FORCE_INLINE const_reference at(size_type index) const           { return *add(m_first, index); }
+        AZ_FORCE_INLINE reference operator[] (size_type index) { return *add(m_first, index); }
+        AZ_FORCE_INLINE const_reference operator[] (size_type index) const { return *add(m_first, index); }
+        AZ_FORCE_INLINE reference at(size_type index) { return *add(m_first, index); }
+        AZ_FORCE_INLINE const_reference at(size_type index) const { return *add(m_first, index); }
 
         AZ_FORCE_INLINE reference front() { AZSTD_CONTAINER_ASSERT(m_size > 0, "AZStd::ring_buffer::front - container is empty!"); return *m_first; }
         AZ_FORCE_INLINE reference back() { AZSTD_CONTAINER_ASSERT(m_size > 0, "AZStd::ring_buffer::back - container is empty!"); return *((m_last == m_buff ? m_end : m_last) - 1); }
         AZ_FORCE_INLINE const_reference front() const { AZSTD_CONTAINER_ASSERT(m_size > 0, "AZStd::ring_buffer::front - container is empty!"); return *m_first; }
-        AZ_FORCE_INLINE const_reference back() const {  AZSTD_CONTAINER_ASSERT(m_size > 0, "AZStd::ring_buffer::back - container is empty!"); return *((m_last == m_buff ? m_end : m_last) - 1); }
+        AZ_FORCE_INLINE const_reference back() const { AZSTD_CONTAINER_ASSERT(m_size > 0, "AZStd::ring_buffer::back - container is empty!"); return *((m_last == m_buff ? m_end : m_last) - 1); }
 
         /// Get the first continuous array of the internal buffer.
         AZ_FORCE_INLINE array_range array_one() { return array_range(m_first, (m_last <= m_first && (m_size > 0) ? m_end : m_last) - m_first); }
@@ -416,12 +416,12 @@ namespace AZStd
             }
         }
 
-        AZ_FORCE_INLINE size_type size() const      { return m_size; }
-        AZ_FORCE_INLINE size_type max_size() const  { return AZStd::allocator_traits<allocator_type>::max_size(m_allocator) / sizeof(node_type); }
-        AZ_FORCE_INLINE bool empty() const          { return m_size == 0; }
-        AZ_FORCE_INLINE bool full() const           { return size_type(m_end - m_buff) == m_size; }
-        AZ_FORCE_INLINE size_type free() const      { return size_type(m_end - m_buff) - m_size; }
-        AZ_FORCE_INLINE size_type capacity() const  { return m_end - m_buff; }
+        AZ_FORCE_INLINE size_type size() const { return m_size; }
+        AZ_FORCE_INLINE size_type max_size() const { return AZStd::allocator_traits<allocator_type>::max_size(m_allocator) / sizeof(node_type); }
+        AZ_FORCE_INLINE bool empty() const { return m_size == 0; }
+        AZ_FORCE_INLINE bool full() const { return size_type(m_end - m_buff) == m_size; }
+        AZ_FORCE_INLINE size_type free() const { return size_type(m_end - m_buff) - m_size; }
+        AZ_FORCE_INLINE size_type capacity() const { return m_end - m_buff; }
 
         inline void resize(size_type new_size, const_reference value = value_type())
         {
@@ -464,9 +464,9 @@ namespace AZStd
         {
             if (m_allocator == rhs.m_allocator)
             {
-    #ifdef AZSTD_HAS_CHECKED_ITERATORS
+#ifdef AZSTD_HAS_CHECKED_ITERATORS
                 swap_all(rhs);
-    #endif
+#endif
                 AZStd::swap(m_buff, rhs.m_buff);
                 AZStd::swap(m_end, rhs.m_end);
                 AZStd::swap(m_first, rhs.m_first);
@@ -693,7 +693,7 @@ namespace AZStd
             {
                 return;
             }
-            pointer buff = reinterpret_cast<pointer>(m_allocator.allocate(new_capacity * sizeof(node_type), alignment_of<node_type>::value));
+            pointer buff = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(new_capacity * sizeof(node_type), alignof(node_type))));
             iterator b = begin();
             pointer last = AZStd::uninitialized_copy(b, b + AZStd::GetMin(new_capacity, m_size), buff, Internal::is_fast_copy<iterator, pointer>());
 
@@ -712,7 +712,7 @@ namespace AZStd
             {
                 return;
             }
-            pointer buff = reinterpret_cast<pointer>(m_allocator.allocate(new_capacity * sizeof(node_type), alignment_of<node_type>::value));
+            pointer buff = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(new_capacity * sizeof(node_type), alignof(node_type))));
             iterator e = end();
             pointer last = AZStd::uninitialized_copy(e - AZStd::GetMin(new_capacity, m_size), e, buff, Internal::is_fast_copy<iterator, pointer>());
 
@@ -815,7 +815,7 @@ namespace AZStd
             if (m_buff)
             {
                 size_type byteSize = sizeof(node_type) * capacity();
-                m_allocator.deallocate(m_buff, byteSize, alignment_of<node_type>::value);
+                m_allocator.deallocate(m_buff, byteSize, alignof(node_type));
             }
         }
         AZ_FORCE_INLINE void increment(pointer& p) const
@@ -870,7 +870,7 @@ namespace AZStd
         }
         AZ_FORCE_INLINE void initialize_buffer(size_type capacity)
         {
-            m_buff = reinterpret_cast<pointer>(m_allocator.allocate(capacity * sizeof(node_type), alignment_of<node_type>::value));
+            m_buff = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(capacity * sizeof(node_type), alignof(node_type))));
             m_end = m_buff + capacity;
         }
         AZ_FORCE_INLINE void initialize_buffer(size_type capacity, const_reference value)

+ 11 - 11
Code/Framework/AzCore/AzCore/std/containers/vector.h

@@ -96,7 +96,7 @@ namespace AZStd
             if (numElements > 0)
             {
                 size_type byteSize = sizeof(node_type) * numElements;
-                m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 m_end = m_start + numElements;
                 Internal::construct<pointer, value_type, false>::range(m_start, m_end);
                 m_last = m_end;
@@ -111,7 +111,7 @@ namespace AZStd
             if (numElements > 0)
             {
                 size_type byteSize = sizeof(node_type) * numElements;
-                m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 m_end   = m_start + numElements;
                 AZStd::uninitialized_fill_n(m_start, numElements, value);
                 m_last  = m_end;
@@ -126,7 +126,7 @@ namespace AZStd
             if (numElements > 0)
             {
                 size_type byteSize = sizeof(node_type) * numElements;
-                m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 m_end   = m_start + numElements;
                 AZStd::uninitialized_fill_n(m_start, numElements, value);
                 m_last  = m_end;
@@ -160,7 +160,7 @@ namespace AZStd
             size_type byteSize = sizeof(node_type) * (rhs.m_last - rhs.m_start);
             if (byteSize)
             {
-                m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 m_last  = AZStd::uninitialized_copy(rhs.m_start, rhs.m_last, m_start, is_trivially_copy_constructible<value_type>());
             }
             else
@@ -365,7 +365,7 @@ namespace AZStd
 
                 // allocate and copy new
                 size_type byteSize = sizeof(node_type) * newSize;
-                m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 m_last  = AZStd::uninitialized_copy(rhs.m_start, rhs.m_last, m_start, is_trivially_copy_constructible<value_type>());
                 m_end   = m_last;
             }
@@ -422,7 +422,7 @@ namespace AZStd
                 // need more capacity - reallocate
                 size_type byteSize = sizeof(node_type) * numElements;
                 // TODO: here we can use reallocate, if possible, reallocate will extend the current allocation
-                pointer newStart = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                pointer newStart = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 pointer newLast = AZStd::uninitialized_move(m_start, m_last, newStart);
 
                 // Destroy old array
@@ -676,7 +676,7 @@ namespace AZStd
 
                 size_type byteSize = capacity * sizeof(node_type);
 
-                pointer newStart = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                pointer newStart = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                 // Copy the elements before insert position.
                 pointer newLast = AZStd::uninitialized_move(m_start, insertPosPtr, newStart);
                 // add new data
@@ -913,7 +913,7 @@ namespace AZStd
 
                     size_type byteSize = sizeof(node_type) * size;
 
-                    pointer newStart = reinterpret_cast<pointer>(newAllocator.allocate(byteSize, alignment_of<node_type>::value));
+                    pointer newStart = reinterpret_cast<pointer>(static_cast<void*>(newAllocator.allocate(byteSize, alignof(node_type))));
                     pointer newLast = AZStd::uninitialized_move(m_start, m_last, newStart);
 
                     // destroy objects
@@ -1006,7 +1006,7 @@ namespace AZStd
                     size_type byteSize = sizeof(node_type) * numElements;
 
                     // TODO: here we can use reallocate, if possible, reallocate will extend the current allocation
-                    newStart = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                    newStart = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
 
                     if (numMoved > 0)
                     {
@@ -1116,7 +1116,7 @@ namespace AZStd
 
                     size_type byteSize = capacity * sizeof(node_type);
 
-                    pointer newStart = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+                    pointer newStart = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
                     // Copy the elements before insert position.
                     pointer newLast = AZStd::uninitialized_move(m_start, insertPosPtr, newStart);
                     // add new data (just copy no move)
@@ -1211,7 +1211,7 @@ namespace AZStd
             // ok so we did not really mean iterators when the called this function.
             size_type byteSize = sizeof(node_type) * numElements;
 
-            m_start = reinterpret_cast<pointer>(m_allocator.allocate(byteSize, alignment_of<node_type>::value));
+            m_start = reinterpret_cast<pointer>(static_cast<void*>(m_allocator.allocate(byteSize, alignof(node_type))));
             m_end   = m_start + numElements;
             AZStd::uninitialized_fill_n(m_start, numElements, value);
             m_last  = m_end;

+ 1 - 1
Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_queue.h

@@ -173,7 +173,7 @@ namespace AZStd
     template<typename T, typename Allocator>
     inline typename lock_free_queue<T, Allocator>::node_ptr_type lock_free_queue<T, Allocator>::create_node()
     {
-        node_type* node = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+        node_type* node = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
         new(node) node_type;
         return node;
     }

+ 1 - 1
Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stack.h

@@ -149,7 +149,7 @@ namespace AZStd
     template<typename T, typename Allocator>
     inline typename lock_free_stack<T, Allocator>::node_ptr_type lock_free_stack<T, Allocator>::create_node()
     {
-        node_type* node = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+        node_type* node = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
         new(node) node_type;
         return node;
     }

+ 1 - 1
Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stamped_queue.h

@@ -206,7 +206,7 @@ namespace AZStd
     inline typename lock_free_stamped_queue<T, Allocator>::node_ptr_type
     lock_free_stamped_queue<T, Allocator>::create_node()
     {
-        node_type* node = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+        node_type* node = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
         new(node) node_type;
         return node;
     }

+ 1 - 1
Code/Framework/AzCore/AzCore/std/parallel/containers/lock_free_stamped_stack.h

@@ -150,7 +150,7 @@ namespace AZStd
     inline typename lock_free_stamped_stack<T, Allocator>::node_ptr_type
     lock_free_stamped_stack<T, Allocator>::create_node()
     {
-        node_type* node = reinterpret_cast<node_ptr_type>(m_allocator.allocate(sizeof(node_type), alignment_of<node_type>::value));
+        node_type* node = reinterpret_cast<node_ptr_type>(static_cast<void*>(m_allocator.allocate(sizeof(node_type), alignof(node_type))));
         new(node) node_type;
         return node;
     }

+ 4 - 4
Code/Framework/AzCore/AzCore/std/smart_ptr/shared_count.h

@@ -218,7 +218,7 @@ namespace AZStd
             {
                 typedef sp_counted_impl_pa<Y, A> impl_type;
                 A a2(a);
-                pi_ = reinterpret_cast<impl_type*>(a2.allocate(sizeof(impl_type), AZStd::alignment_of<impl_type>::value));
+                pi_ = reinterpret_cast<impl_type*>(static_cast<void*>(a2.allocate(sizeof(impl_type), alignof(impl_type))));
                 if (pi_ != 0)
                 {
                     new(static_cast<void*>(pi_))impl_type(p, a);
@@ -234,7 +234,7 @@ namespace AZStd
             {
                 typedef sp_counted_impl_pda<P, D, A> impl_type;
                 A a2(a);
-                pi_ = reinterpret_cast<impl_type*>(a2.allocate(sizeof(impl_type), AZStd::alignment_of<impl_type>::value));
+                pi_ = reinterpret_cast<impl_type*>(static_cast<void*>(a2.allocate(sizeof(impl_type), alignof(impl_type))));
                 if (pi_ != 0)
                 {
                     new(static_cast< void* >(pi_))impl_type(p, d, a);
@@ -251,7 +251,7 @@ namespace AZStd
             {
                 typedef sp_counted_impl_pda<P, D, A> impl_type;
                 A a2(a);
-                pi_ = reinterpret_cast<impl_type*>(a2.allocate(sizeof(impl_type), AZStd::alignment_of<impl_type>::value));
+                pi_ = reinterpret_cast<impl_type*>(static_cast<void*>(a2.allocate(sizeof(impl_type), alignof(impl_type))));
                 if (pi_ != 0)
                 {
                     new(static_cast<void*>(pi_))impl_type(p, a);
@@ -273,7 +273,7 @@ namespace AZStd
             {
                 typedef sp_counted_impl_pa<Y, A> impl_type;
                 A a2(a);
-                pi_ = reinterpret_cast<impl_type*>(a2.allocate(sizeof(impl_type), AZStd::alignment_of<impl_type>::value));
+                pi_ = reinterpret_cast<impl_type*>(static_cast<void*>(a2.allocate(sizeof(impl_type), alignof(impl_type))));
                 if (pi_ != 0)
                 {
                     new(static_cast< void* >(pi_))impl_type(r.get(), a);

+ 2 - 2
Code/Framework/AzCore/AzCore/std/sort.h

@@ -51,7 +51,7 @@ namespace AZStd
                 , m_numConstructed(0)
                 , m_size(0)
             {
-                m_data = reinterpret_cast<pointer_type>(m_allocator.allocate(sizeof(T) * m_capacity, alignment_of<T>::value));
+                m_data = reinterpret_cast<pointer_type>(static_cast<void*>(m_allocator.allocate(sizeof(T) * m_capacity, alignof(T))));
             }
 
             AZ_FORCE_INLINE TemporaryBuffer(size_type capacity, const Allocator& allocator)
@@ -60,7 +60,7 @@ namespace AZStd
                 , m_numConstructed(0)
                 , m_size(0)
             {
-                m_data = reinterpret_cast<pointer_type>(m_allocator.allocate(sizeof(T) * m_capacity, alignment_of<T>::value));
+                m_data = reinterpret_cast<pointer_type>(static_cast<void*>(m_allocator.allocate(sizeof(T) * m_capacity, alignof(T))));
             }
 
             AZ_FORCE_INLINE ~TemporaryBuffer()

+ 4 - 4
Code/Framework/AzCore/AzCore/std/string/string.h

@@ -730,7 +730,7 @@ namespace AZStd
                     // If the input string is a sub-string and it would cause
                     // this string to need to re-allocated as it doesn't fit in the capacity
                     // Then the  input string is needs to be copied into a local buffer
-                    inputStringCopy = reinterpret_cast<pointer>(get_allocator().allocate(ptrCount * sizeof(value_type), alignof(value_type)));
+                    inputStringCopy = reinterpret_cast<pointer>(static_cast<void*>(get_allocator().allocate(ptrCount * sizeof(value_type), alignof(value_type))));
                     Traits::copy(inputStringCopy, ptr, ptrCount);
                     // Updated the input string pointer to point to the local buffer
                     ptr = inputStringCopy;
@@ -1332,7 +1332,7 @@ namespace AZStd
                     allocator_type newAllocator = allocator;
                     pointer data = m_storage.first().GetData();
 
-                    pointer newData = reinterpret_cast<pointer>(newAllocator.allocate(sizeof(node_type) * (capacity() + 1), alignof(node_type)));
+                    pointer newData = reinterpret_cast<pointer>(static_cast<void*>(newAllocator.allocate(sizeof(node_type) * (capacity() + 1), alignof(node_type))));
 
                     Traits::copy(newData, data, size() + 1);  // copy elements and terminator
 
@@ -1439,7 +1439,7 @@ namespace AZStd
                 }
                 else
                 {
-                    pointer newData = reinterpret_cast<pointer>(m_storage.second().allocate(sizeof(node_type) * (numElements + 1), alignof(node_type)));
+                    pointer newData = reinterpret_cast<pointer>(static_cast<void*>(m_storage.second().allocate(sizeof(node_type) * (numElements + 1), alignof(node_type))));
                     AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!");
 
                     size_type newSize = numElements < m_storage.first().GetSize() ? numElements : m_storage.first().GetSize();
@@ -1659,7 +1659,7 @@ namespace AZStd
             }
             if (newCapacity >= ShortStringData::Capacity)
             {
-                pointer newData = reinterpret_cast<pointer>(m_storage.second().allocate(sizeof(node_type) * (newCapacity + 1), alignof(node_type)));
+                pointer newData = reinterpret_cast<pointer>(static_cast<void*>(m_storage.second().allocate(sizeof(node_type) * (newCapacity + 1), alignof(node_type))));
                 AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!");
                 if (newData)
                 {

+ 19 - 0
Code/Framework/AzCore/CMakeLists.txt

@@ -37,6 +37,24 @@ set(ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES "" CACHE STRING
 # whether or not to allow the Settings Registry development overrides.
 set(ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES_FLAG $<$<NOT:$<STREQUAL:"${ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES}","">>:ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES=${ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES}>)
 
+# If empty, the allocator initialization startup config file will be checked for on startup in development (non-release) builds
+# This behavior can be overridden by passing -DO3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE=0,1 to either prevent/force checking for the startup config file.
+# The override can be removed by reconfiguring with CMake using the -UO3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE option to undefine the cache variable.
+set(O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE "" CACHE STRING
+"If unset, the default behavior is to check for a startup config file in development builds only.
+  When set to a falsey value(0, FALSE, OFF, etc...), no build configuration will check for a startup config file.
+  When set to any other value all build configurations will check for a startup config file.
+  NOTE: The startup config file is able to initialize O3DE allocator settings using a Windows-style INI file by specifying
+  the path to the file in 1 of 4 locations
+  1. ~/.o3de/Registry/startup.cfg
+  2. <executable-directory>/Registry/startup.cfg
+  3. The value of the O3DE_STARTUP_CFG_FILE environment variable if set
+  4. The value of the --startup-cfg-file command line argument")
+
+# If there's a value in ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES, the compiler flag will get set to it.
+# Otherwise, the variable will be empty and no compiler flag will be set, leaving it to the code to decide
+# whether or not to allow the Settings Registry development overrides.
+set(STARTUP_CFG_FILE_CHECK_OVERRIDE_FLAG $<$<NOT:$<STREQUAL:"${O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE}","">>:O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE=$<BOOL:${O3DE_STARTUP_CFG_FILE_CHECK_OVERRIDE}>>)
 
 
 ly_add_target(
@@ -91,6 +109,7 @@ ly_add_source_properties(
         AzCore/Component/ComponentApplication.cpp
     PROPERTY COMPILE_DEFINITIONS
     VALUES ${ALLOW_SETTINGS_REGISTRY_DEVELOPMENT_OVERRIDES_FLAG}
+        ${STARTUP_CFG_FILE_CHECK_OVERRIDE_FLAG}
 )
 
 # Add the O3DE_STACK_CAPTURE_DEPTH define only to the cpp files for the following allocators

+ 1 - 1
Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp

@@ -87,7 +87,7 @@ namespace AZ::Utils
         return appRoot ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(appRoot) : AZStd::nullopt;
     }
 
-    AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
+    AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath()
     {
         const char* writeStorage = AZ::Android::Utils::GetAppPublicStoragePath();
         return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;

+ 1 - 1
Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp

@@ -62,7 +62,7 @@ namespace AZ
             return AZStd::nullopt;
         }
 
-        AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
+        AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath()
         {
             return AZStd::nullopt;
         }

+ 1 - 1
Code/Framework/AzCore/Platform/Linux/AzCore/Utils/Utils_Linux.cpp

@@ -44,7 +44,7 @@ namespace AZ::Utils
         return AZStd::nullopt;
     }
 
-    AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
+    AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath()
     {
         return AZStd::nullopt;
     }

+ 1 - 1
Code/Framework/AzCore/Platform/Mac/AzCore/Utils/Utils_Mac.cpp

@@ -17,7 +17,7 @@ namespace AZ::Utils
         return AZStd::nullopt;
     }
 
-    AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
+    AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath()
     {
         return AZStd::nullopt;
     }

+ 1 - 1
Code/Framework/AzCore/Platform/iOS/AzCore/Utils/Utils_iOS.mm

@@ -21,7 +21,7 @@ namespace AZ::Utils
         return AZ::IO::FixedMaxPathString::format("%s/assets", pathToResources);
     }
 
-    AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
+    AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultDevWriteStoragePath()
     {
         NSArray* appSupportDirectoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
         if ([appSupportDirectoryPaths count] == 0)

+ 5 - 6
Code/Framework/AzCore/Tests/AZStd/Hashed.cpp

@@ -16,7 +16,6 @@
 #include <AzCore/std/ranges/transform_view.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/typetraits/has_member_function.h>
-#include <AzCore/Memory/AllocatorWrappers.h>
 
 #if defined(HAVE_BENCHMARK)
 #include <benchmark/benchmark.h>
@@ -1404,11 +1403,11 @@ namespace UnitTest
     template<template <typename, typename, typename, typename> class ContainerTemplate>
     struct HashedSetWithCustomAllocatorConfig
     {
-        using ContainerType = ContainerTemplate<int32_t, AZStd::hash<int32_t>, AZStd::equal_to<int32_t>, AZ::AllocatorPointerWrapper>;
+        using ContainerType = ContainerTemplate<int32_t, AZStd::hash<int32_t>, AZStd::equal_to<int32_t>, AZ::AZStdIAllocator>;
 
         static ContainerType Create(std::initializer_list<typename ContainerType::value_type> intList, AZ::IAllocator* allocatorInstance)
         {
-            ContainerType allocatorSet(intList, 0, AZStd::hash<int32_t>{}, AZStd::equal_to<int32_t>{}, AZ::AllocatorPointerWrapper{ allocatorInstance });
+            ContainerType allocatorSet(intList, 0, AZStd::hash<int32_t>{}, AZStd::equal_to<int32_t>{}, AZ::AZStdIAllocator{ allocatorInstance });
             return allocatorSet;
         }
     };
@@ -1416,7 +1415,7 @@ namespace UnitTest
     using SetTemplateConfigs = ::testing::Types<
         HashedSetWithCustomAllocatorConfig<AZStd::unordered_set>
         , HashedSetWithCustomAllocatorConfig<AZStd::unordered_multiset>
-        >;
+    >;
     TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs);
 
 #if GTEST_HAS_DEATH_TEST
@@ -1904,11 +1903,11 @@ namespace UnitTest
     template<template <typename, typename, typename, typename, typename> class ContainerTemplate>
     struct HashedMapWithCustomAllocatorConfig
     {
-        using ContainerType = ContainerTemplate<int32_t, int32_t, AZStd::hash<int32_t>, AZStd::equal_to<int32_t>, AZ::AllocatorPointerWrapper>;
+        using ContainerType = ContainerTemplate<int32_t, int32_t, AZStd::hash<int32_t>, AZStd::equal_to<int32_t>, AZ::AZStdIAllocator>;
 
         static ContainerType Create(std::initializer_list<typename ContainerType::value_type> intList, AZ::IAllocator* allocatorInstance)
         {
-            ContainerType allocatorMap(intList, 0, AZStd::hash<int32_t>{}, AZStd::equal_to<int32_t>{}, AZ::AllocatorPointerWrapper{ allocatorInstance });
+            ContainerType allocatorMap(intList, 0, AZStd::hash<int32_t>{}, AZStd::equal_to<int32_t>{}, AZ::AZStdIAllocator{ allocatorInstance });
             return allocatorMap;
         }
     };

+ 12 - 13
Code/Framework/AzCore/Tests/AZStd/Ordered.cpp

@@ -16,7 +16,6 @@
 #include <AzCore/std/containers/fixed_vector.h>
 #include <AzCore/std/containers/span.h>
 #include <AzCore/std/ranges/transform_view.h>
-#include <AzCore/Memory/AllocatorWrappers.h>
 
 #define AZ_TEST_VALIDATE_EMPTY_TREE(_Tree_) \
     EXPECT_EQ(0, _Tree_.size());     \
@@ -400,7 +399,7 @@ namespace UnitTest
     TEST_F(Tree_Set, ExplicitAllocatorSucceeds)
     {
         AZ::OSAllocator customAllocator;
-        AZStd::set<int, AZStd::less<int>, AZ::AllocatorPointerWrapper> setWithCustomAllocator{ AZ::AllocatorPointerWrapper(&customAllocator) };
+        AZStd::set<int, AZStd::less<int>, AZ::AZStdIAllocator> setWithCustomAllocator{ AZ::AZStdIAllocator(&customAllocator) };
         auto insertIter = setWithCustomAllocator.emplace(1);
         EXPECT_TRUE(insertIter.second);
         insertIter = setWithCustomAllocator.emplace(1);
@@ -524,7 +523,7 @@ namespace UnitTest
     TEST_F(Tree_MultiSet, ExplicitAllocatorSucceeds)
     {
         AZ::OSAllocator customAllocator;
-        AZStd::multiset<int, AZStd::less<int>, AZ::AllocatorPointerWrapper> setWithCustomAllocator{ AZ::AllocatorPointerWrapper(&customAllocator) };
+        AZStd::multiset<int, AZStd::less<int>, AZ::AZStdIAllocator> setWithCustomAllocator{ AZ::AZStdIAllocator(&customAllocator) };
         setWithCustomAllocator.emplace(1);
         setWithCustomAllocator.emplace(1);
         EXPECT_EQ(2, setWithCustomAllocator.size());
@@ -671,7 +670,7 @@ namespace UnitTest
     TEST_F(Tree_Map, ExplicitAllocatorSucceeds)
     {
         AZ::OSAllocator customAllocator;
-        AZStd::map<int, int, AZStd::less<int>, AZ::AllocatorPointerWrapper> mapWithCustomAllocator{ AZ::AllocatorPointerWrapper(&customAllocator) };
+        AZStd::map<int, int, AZStd::less<int>, AZ::AZStdIAllocator> mapWithCustomAllocator{ AZ::AZStdIAllocator(&customAllocator) };
         auto insertIter = mapWithCustomAllocator.emplace(1, 1);
         EXPECT_TRUE(insertIter.second);
         insertIter = mapWithCustomAllocator.emplace(1, 2);
@@ -826,7 +825,7 @@ namespace UnitTest
     TEST_F(Tree_MultiMap, ExplicitAllocatorSucceeds)
     {
         AZ::OSAllocator customAllocator;
-        AZStd::multimap<int, int, AZStd::less<int>, AZ::AllocatorPointerWrapper> mapWithCustomAllocator{ AZ::AllocatorPointerWrapper(&customAllocator) };
+        AZStd::multimap<int, int, AZStd::less<int>, AZ::AZStdIAllocator> mapWithCustomAllocator{ AZ::AZStdIAllocator(&customAllocator) };
         mapWithCustomAllocator.emplace(1, 1);
         mapWithCustomAllocator.emplace(1, 2);
         EXPECT_EQ(2, mapWithCustomAllocator.size());
@@ -1150,11 +1149,11 @@ namespace UnitTest
     template<template <typename, typename, typename> class ContainerTemplate>
     struct TreeSetWithCustomAllocatorConfig
     {
-        using ContainerType = ContainerTemplate<int32_t, AZStd::less<int32_t>, AZ::AllocatorPointerWrapper>;
+        using ContainerType = ContainerTemplate<int32_t, AZStd::less<int32_t>, AZ::AZStdIAllocator>;
 
         static ContainerType Create(std::initializer_list<typename ContainerType::value_type> intList, AZ::IAllocator* allocatorInstance)
         {
-            ContainerType allocatorSet(intList, AZStd::less<int32_t>{}, AZ::AllocatorPointerWrapper{ allocatorInstance });
+            ContainerType allocatorSet(intList, AZStd::less<int32_t>{}, AZ::AZStdIAllocator{ allocatorInstance });
             return allocatorSet;
         }
     };
@@ -1204,13 +1203,13 @@ namespace UnitTest
 
         EXPECT_EQ(1, systemAllocatorMap.size());
         EXPECT_EQ(1, systemAllocatorMap.count(2));
-        EXPECT_EQ(AZ::AllocatorPointerWrapper(&AZ::AllocatorInstance<AZ::SystemAllocator>::Get()), systemAllocatorMap.get_allocator());
+        EXPECT_EQ(AZ::AZStdIAllocator(&AZ::AllocatorInstance<AZ::SystemAllocator>::Get()), systemAllocatorMap.get_allocator());
 
         EXPECT_EQ(3, osAllocatorMap.size());
         EXPECT_EQ(1, osAllocatorMap.count(1));
         EXPECT_EQ(1, osAllocatorMap.count(2));
         EXPECT_EQ(1, osAllocatorMap.count(3));
-        EXPECT_EQ(AZ::AllocatorPointerWrapper(&AZ::AllocatorInstance<AZ::OSAllocator>::Get()), osAllocatorMap.get_allocator());
+        EXPECT_EQ(AZ::AZStdIAllocator(&AZ::AllocatorInstance<AZ::OSAllocator>::Get()), osAllocatorMap.get_allocator());
     }
 
     template<typename ContainerType>
@@ -1631,11 +1630,11 @@ namespace UnitTest
     template<template <typename, typename, typename, typename> class ContainerTemplate>
     struct TreeMapWithCustomAllocatorConfig
     {
-        using ContainerType = ContainerTemplate<int32_t, int32_t, AZStd::less<int32_t>, AZ::AllocatorPointerWrapper>;
+        using ContainerType = ContainerTemplate<int32_t, int32_t, AZStd::less<int32_t>, AZ::AZStdIAllocator>;
 
         static ContainerType Create(std::initializer_list<typename ContainerType::value_type> intList, AZ::IAllocator* allocatorInstance)
         {
-            ContainerType allocatorMap(intList, AZStd::less<int32_t>{}, AZ::AllocatorPointerWrapper{ allocatorInstance });
+            ContainerType allocatorMap(intList, AZStd::less<int32_t>{}, AZ::AZStdIAllocator{ allocatorInstance });
             return allocatorMap;
         }
     };
@@ -1685,13 +1684,13 @@ namespace UnitTest
 
         EXPECT_EQ(1, systemAllocatorMap.size());
         EXPECT_EQ(1, systemAllocatorMap.count(2));
-        EXPECT_EQ(AZ::AllocatorPointerWrapper(&AZ::AllocatorInstance<AZ::SystemAllocator>::Get()), systemAllocatorMap.get_allocator());
+        EXPECT_EQ(AZ::AZStdIAllocator(&AZ::AllocatorInstance<AZ::SystemAllocator>::Get()), systemAllocatorMap.get_allocator());
 
         EXPECT_EQ(3, osAllocatorMap.size());
         EXPECT_EQ(1, osAllocatorMap.count(1));
         EXPECT_EQ(1, osAllocatorMap.count(2));
         EXPECT_EQ(1, osAllocatorMap.count(3));
-        EXPECT_EQ(AZ::AllocatorPointerWrapper(&AZ::AllocatorInstance<AZ::OSAllocator>::Get()), osAllocatorMap.get_allocator());
+        EXPECT_EQ(AZ::AZStdIAllocator(&AZ::AllocatorInstance<AZ::OSAllocator>::Get()), osAllocatorMap.get_allocator());
     }
 
     namespace TreeContainerTransparentTestInternal

+ 7 - 7
Code/Framework/AzCore/Tests/Components.cpp

@@ -56,7 +56,7 @@ namespace UnitTest
         // Create application environment code driven
         ComponentApplication::Descriptor appDesc;
         appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
-        appDesc.m_recordingMode = AllocationRecords::RECORD_FULL;
+        appDesc.m_recordingMode = AllocationRecords::Mode::RECORD_FULL;
         AZ::ComponentApplication::StartupParameters startupParameters;
         startupParameters.m_loadSettingsRegistry = false;
         Entity* systemEntity = app.Create(appDesc, startupParameters);
@@ -804,7 +804,7 @@ namespace UnitTest
 
         EXPECT_EQ(Entity::DependencySortResult::Success, m_entity->EvaluateDependencies());
 
-        const AZStd::vector<Component*>& components = m_entity->GetComponents();
+        const AZ::Entity::ComponentArrayType& components = m_entity->GetComponents();
         auto locationB = AZStd::find(components.begin(), components.end(), b);
         auto locationE = AZStd::find(components.begin(), components.end(), e);
         auto locationE2 = AZStd::find(components.begin(), components.end(), e2);
@@ -829,7 +829,7 @@ namespace UnitTest
 
         EXPECT_EQ(Entity::DependencySortResult::DSR_OK, m_entity->EvaluateDependencies());
 
-        const AZStd::vector<Component*>& components = m_entity->GetComponents();
+        const AZ::Entity::ComponentArrayType& components = m_entity->GetComponents();
         const ptrdiff_t numComponents = m_entity->GetComponents().size();
 
         ptrdiff_t maxIndexOfComponentProvidingServices = PTRDIFF_MIN;
@@ -939,13 +939,13 @@ namespace UnitTest
         // perform initial sort
         EXPECT_EQ(Entity::DependencySortResult::Success, m_entity->EvaluateDependencies());
 
-        const AZStd::vector<Component*> originalSortedOrder = m_entity->GetComponents();
+        const AZ::Entity::ComponentArrayType originalSortedOrder = m_entity->GetComponents();
 
         // try shuffling the components a bunch of times
         // we should always get the same sorted results
         for (int iteration = 0; iteration < 50; ++iteration)
         {
-            AZStd::vector<Component*> componentsToShuffle = m_entity->GetComponents();
+            AZ::Entity::ComponentArrayType componentsToShuffle = m_entity->GetComponents();
 
             // remove all components from entity
             for (Component* component : componentsToShuffle)
@@ -972,7 +972,7 @@ namespace UnitTest
             }
 
             EXPECT_EQ(Entity::DependencySortResult::Success, m_entity->EvaluateDependencies());
-            const AZStd::vector<Component*>& sorted = m_entity->GetComponents();
+            const AZ::Entity::ComponentArrayType& sorted = m_entity->GetComponents();
             EXPECT_EQ(originalSortedOrder, sorted);
 
             if (HasFailure())
@@ -2024,7 +2024,7 @@ namespace Benchmark
         {
             // create components to sort
             state.PauseTiming();
-            AZStd::vector<Component*> components;
+            AZ::Entity::ComponentArrayType components;
             AZ_Assert((state.range(0) % 6) == 0, "Multiple of 6 required");
             while ((int)components.size() < state.range(0))
             {

+ 4 - 4
Code/Framework/AzCore/Tests/Memory.cpp

@@ -39,8 +39,8 @@ namespace UnitTest
     public:
         void SetUp() override
         {
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
         }
@@ -49,8 +49,8 @@ namespace UnitTest
             AZ::AllocatorManager::Instance().GarbageCollect();
             AZ::AllocatorManager::Instance().ExitProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
         }
     };
 

+ 1 - 5
Code/Framework/AzCore/Tests/Memory/HphaAllocator.cpp

@@ -22,13 +22,9 @@ namespace UnitTest
         HphaSchema_TestAllocator()
         {
             Create();
-            PostCreate();
         }
 
-        ~HphaSchema_TestAllocator() override
-        {
-            PreDestroy();
-        }
+        ~HphaSchema_TestAllocator() override = default;
     };
 
     static const size_t s_kiloByte = 1024;

+ 7 - 7
Code/Framework/AzCore/Tests/Memory/LeakDetection.cpp

@@ -105,7 +105,7 @@ namespace UnitTest
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
     // Create a dummy allocator so unit tests can leak it
-    AZ_ALLOCATOR_DEFAULT_GLOBAL_WRAPPER(LeakDetection_TestAllocator, AZ::SystemAllocator, "{186B6E32-344D-4322-820A-4C3E4F30650B}")
+    AZ_CHILD_ALLOCATOR_WITH_NAME(LeakDetection_TestAllocator, "LeakDetection_TestAllocator", "{186B6E32-344D-4322-820A-4C3E4F30650B}", AZ::SystemAllocator);
 
     // Dummy test class
     class TestClassLeakDetection_TestAllocator
@@ -123,8 +123,8 @@ namespace UnitTest
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
             [[maybe_unused]] TestClassLeakDetection_TestAllocator* object = new TestClassLeakDetection_TestAllocator();
 
             // In regular unit test operation, the environment will be teardown at the end and thats where the validation will happen. Here, we need
@@ -205,8 +205,8 @@ namespace UnitTest
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
         }
 
         void TearDown() override
@@ -220,8 +220,8 @@ namespace UnitTest
                 AZ::AllocatorManager::Instance().GarbageCollect();
             }
 
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
             AZ::AllocatorManager::Instance().ExitProfilingMode();
         }

+ 1 - 1
Code/Framework/AzCore/Tests/Module.cpp

@@ -134,7 +134,7 @@ namespace UnitTest
             // Create application descriptor
             ComponentApplication::Descriptor appDesc;
             appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
-            appDesc.m_recordingMode = Debug::AllocationRecords::RECORD_FULL;
+            appDesc.m_recordingMode = Debug::AllocationRecords::Mode::RECORD_FULL;
 
             // AZCoreTestDLL will load as a dynamic module
             DynamicModuleDescriptor& dynamicModuleDescriptor = appDesc.m_modules.emplace_back();

+ 1 - 1
Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp

@@ -298,7 +298,7 @@ namespace AzFramework
         }
         
         // don't create component if an incompatible component exists on the entity
-        AZStd::vector<AZ::Component*> incompatibleComponents;
+        AZ::Entity::ComponentArrayType incompatibleComponents;
         entity->IsComponentReadyToAdd(componentTypeId, nullptr, &incompatibleComponents);
         if (!incompatibleComponents.empty())
         {

+ 2 - 2
Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.h

@@ -26,7 +26,7 @@ namespace AzFramework
     {
     public:
         AZ_TYPE_INFO(BehaviorComponentId, "{60A9A069-9C3D-465A-B7AD-0D6CC803990A}");
-        AZ_CLASS_ALLOCATOR(BehaviorComponentId, AZ::SystemAllocator);
+        AZ_CLASS_ALLOCATOR(BehaviorComponentId, AZ::ComponentAllocator);
         static void Reflect(AZ::ReflectContext* context);
 
         BehaviorComponentId() = default;
@@ -49,7 +49,7 @@ namespace AzFramework
     {
     public:
         AZ_RTTI(BehaviorEntity, "{41CC88A4-FE07-48E6-943D-998DE68AFF5C}");
-        AZ_CLASS_ALLOCATOR(BehaviorEntity, AZ::SystemAllocator);
+        AZ_CLASS_ALLOCATOR(BehaviorEntity, AZ::EntityAllocator);
         static void Reflect(AZ::ReflectContext* context);
 
         virtual ~BehaviorEntity() = default;

+ 2 - 2
Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp

@@ -300,8 +300,8 @@ namespace AZ
                 const AZ::IO::PathView pathView(path);
 
                 const auto devWriteStoragePath = AZ::Utils::GetDevWriteStoragePath();
-                if (devWriteStoragePath.has_value() &&
-                    pathView.IsRelativeTo(AZStd::string_view(*devWriteStoragePath)))
+                if (!devWriteStoragePath.empty() &&
+                    pathView.IsRelativeTo(AZStd::string_view(devWriteStoragePath)))
                 {
                     return;
                 }

+ 4 - 4
Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp

@@ -94,8 +94,8 @@ namespace AZ
         {
             AZ::AllocatorManager::Instance().EnterProfilingMode();
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(true);
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_FULL);
 
             UnitTest::TraceBusHook::SetupEnvironment();
 
@@ -196,8 +196,8 @@ namespace AZ
 
             UnitTest::TraceBusHook::TeardownEnvironment();
 
-            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
-            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
+            AZ::AllocatorManager::Instance().SetTrackingMode(AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS);
             AZ::AllocatorManager::Instance().SetDefaultProfilingState(false);
             AZ::AllocatorManager::Instance().ExitProfilingMode();
         }

+ 9 - 10
Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionRequestBus.h

@@ -81,7 +81,7 @@ namespace AzToolsFramework
         *          If the operation could not be completed then the failed
         *          outcome contains a string describing what went wrong.
         */
-        virtual AddExistingComponentsOutcome AddExistingComponentsToEntityById(const AZ::EntityId& entityId, const AZStd::vector<AZ::Component*>& componentsToAdd) = 0;
+        virtual AddExistingComponentsOutcome AddExistingComponentsToEntityById(const AZ::EntityId& entityId, AZStd::span<AZ::Component* const> componentsToAdd) = 0;
 
         // Removing a component can only cause the following to occur:
         // 1) Invalidate other components by removing missing services
@@ -104,7 +104,7 @@ namespace AzToolsFramework
         * \param componentsToRemove List of component pointers to remove (from their respective entities).
         * \return true if the components were successfully removed or false otherwise.
         */
-        virtual RemoveComponentsOutcome RemoveComponents(const AZStd::vector<AZ::Component*>& componentsToRemove) = 0;
+        virtual RemoveComponentsOutcome RemoveComponents(AZStd::span<AZ::Component* const> componentsToRemove) = 0;
 
         using ScrubEntityResults = RemoveComponentsResults;
         using EntityToScrubEntityResultsMap = AZStd::unordered_map<AZ::EntityId, ScrubEntityResults>;
@@ -139,13 +139,13 @@ namespace AzToolsFramework
         * Removes the given components from their respective entities (currently only single entity is supported) and copies the data to the clipboard if successful
         * \param components vector of components to cut (this method will delete the components provided on successful removal)
         */
-        virtual void CutComponents(const AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void CutComponents(AZStd::span<AZ::Component* const> components) = 0;
 
         /*!
         * Copies the given components from their respective entities (multiple source entities are supported) into mime data on the clipboard for pasting elsewhere
         * \param components vector of components to copy
         */
-        virtual void CopyComponents(const AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void CopyComponents(AZStd::span<AZ::Component* const> components) = 0;
 
         /*!
         * Pastes components from the mime data on the clipboard (assuming it is component data) to the given entity
@@ -163,15 +163,14 @@ namespace AzToolsFramework
         * Enables the given components
         * \param components vector of components to enable
         */
-        virtual void EnableComponents(const AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void EnableComponents(AZStd::span<AZ::Component* const> components) = 0;
 
         /*!
         * Disables the given components
         * \param components vector of components to disable
         */
-        virtual void DisableComponents(const AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void DisableComponents(AZStd::span<AZ::Component* const> components) = 0;
 
-        using ComponentServicesList = AZStd::vector<AZ::ComponentServiceType>;
 
         /*!
          * Info detailing why a pending component cannot be activated.
@@ -180,9 +179,9 @@ namespace AzToolsFramework
         {
             AZ::Entity::ComponentArrayType m_validComponentsThatAreIncompatible;
             AZ::Entity::ComponentArrayType m_pendingComponentsWithRequiredServices;
-            AZ::Entity::StringWarningArray m_warnings;
-            ComponentServicesList m_missingRequiredServices;
-            ComponentServicesList m_incompatibleServices;
+            AZ::ComponentDescriptor::StringWarningArray m_warnings;
+            AZ::ComponentDescriptor::DependencyArrayType m_missingRequiredServices;
+            AZ::ComponentDescriptor::DependencyArrayType m_incompatibleServices;
         };
 
         /*

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIBus.h

@@ -38,7 +38,7 @@ namespace AzToolsFramework
 
         //! Return a list of type ids for components that match the required services filter,
         //! and don't conflict with any of the incompatible services filter
-        virtual AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) = 0;
+        virtual AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(AZStd::span<const AZ::ComponentServiceType> serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) = 0;
 
         //! Finds the component names from their type ids
         virtual AZStd::vector<AZStd::string> FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) = 0;

+ 21 - 21
Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.cpp

@@ -215,7 +215,7 @@ namespace AzToolsFramework
             return foundTypeIds;
         }
 
-        AZStd::vector<AZ::Uuid> EditorComponentAPIComponent::FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter)
+        AZStd::vector<AZ::Uuid> EditorComponentAPIComponent::FindComponentTypeIdsByService(AZStd::span<const AZ::ComponentServiceType> serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter)
         {
             AZStd::vector<AZ::Uuid> foundTypeIds;
 
@@ -376,7 +376,7 @@ namespace AzToolsFramework
 
         size_t EditorComponentAPIComponent::CountComponentsOfType(AZ::EntityId entityId, AZ::Uuid componentTypeId)
         {
-            AZStd::vector<AZ::Component*> components = FindComponents(entityId, componentTypeId);
+            AZ::Entity::ComponentArrayType components = FindComponents(entityId, componentTypeId);
             return components.size();
         }
 
@@ -385,7 +385,7 @@ namespace AzToolsFramework
             AZ::Component* component = FindComponent(entityId, componentTypeId);
             if (component)
             {
-                return GetComponentOutcome( AZ::EntityComponentIdPair(entityId, component->GetId()) );
+                return GetComponentOutcome(AZ::EntityComponentIdPair(entityId, component->GetId()));
             }
             else
             {
@@ -395,7 +395,7 @@ namespace AzToolsFramework
 
         EditorComponentAPIRequests::GetComponentsOutcome EditorComponentAPIComponent::GetComponentsOfType(AZ::EntityId entityId, AZ::Uuid componentTypeId)
         {
-            AZStd::vector<AZ::Component*> components = FindComponents(entityId, componentTypeId);
+            AZ::Entity::ComponentArrayType components = FindComponents(entityId, componentTypeId);
 
             if (components.empty())
             {
@@ -410,18 +410,18 @@ namespace AzToolsFramework
                 componentIds.push_back(AZ::EntityComponentIdPair(entityId, component->GetId()));
             }
 
-            return {componentIds};
+            return { componentIds };
         }
 
         bool EditorComponentAPIComponent::IsValid(AZ::EntityComponentIdPair componentInstance)
         {
-            AZ::Component* component = FindComponent(componentInstance.GetEntityId() , componentInstance.GetComponentId());
+            AZ::Component* component = FindComponent(componentInstance.GetEntityId(), componentInstance.GetComponentId());
             return component != nullptr;
         }
 
         bool EditorComponentAPIComponent::EnableComponents(const AZStd::vector<AZ::EntityComponentIdPair>& componentInstances)
         {
-            AZStd::vector<AZ::Component*> components;
+            AZ::Entity::ComponentArrayType components;
             for (const AZ::EntityComponentIdPair& componentInstance : componentInstances)
             {
                 AZ::Component* component = FindComponent(componentInstance.GetEntityId(), componentInstance.GetComponentId());
@@ -480,7 +480,7 @@ namespace AzToolsFramework
 
         bool EditorComponentAPIComponent::DisableComponents(const AZStd::vector<AZ::EntityComponentIdPair>& componentInstances)
         {
-            AZStd::vector<AZ::Component*> components;
+            AZ::Entity::ComponentArrayType components;
             for (const AZ::EntityComponentIdPair& componentInstance : componentInstances)
             {
                 AZ::Component* component = FindComponent(componentInstance.GetEntityId(), componentInstance.GetComponentId());
@@ -512,7 +512,7 @@ namespace AzToolsFramework
         {
             bool cumulativeSuccess = true;
 
-            AZStd::vector<AZ::Component*> components;
+            AZ::Entity::ComponentArrayType components;
             for (const AZ::EntityComponentIdPair& componentInstance : componentInstances)
             {
                 AZ::Component* component = FindComponent(componentInstance.GetEntityId(), componentInstance.GetComponentId());
@@ -546,10 +546,10 @@ namespace AzToolsFramework
             if (!component)
             {
                 AZ_Error("EditorComponentAPIComponent", false, "BuildComponentPropertyTreeEditor - Component Instance is Invalid.");
-                return EditorComponentAPIRequests::PropertyTreeOutcome{AZStd::unexpect, PropertyTreeOutcome::ErrorType("BuildComponentPropertyTreeEditor - Component Instance is Invalid.") };
+                return EditorComponentAPIRequests::PropertyTreeOutcome{ AZStd::unexpect, PropertyTreeOutcome::ErrorType("BuildComponentPropertyTreeEditor - Component Instance is Invalid.") };
             }
 
-            return {PropertyTreeOutcome::ValueType(reinterpret_cast<void*>(component), component->GetUnderlyingComponentType())};
+            return { PropertyTreeOutcome::ValueType(reinterpret_cast<void*>(component), component->GetUnderlyingComponentType()) };
         }
 
         EditorComponentAPIRequests::PropertyOutcome EditorComponentAPIComponent::GetComponentProperty(const AZ::EntityComponentIdPair& componentInstance, const AZStd::string_view propertyPath)
@@ -559,7 +559,7 @@ namespace AzToolsFramework
             if (!component)
             {
                 AZ_Error("EditorComponentAPIComponent", false, "GetComponentProperty - Component Instance is Invalid.");
-                return EditorComponentAPIRequests::PropertyOutcome{AZStd::unexpect, PropertyOutcome::ErrorType("GetComponentProperty - Component Instance is Invalid.") };
+                return EditorComponentAPIRequests::PropertyOutcome{ AZStd::unexpect, PropertyOutcome::ErrorType("GetComponentProperty - Component Instance is Invalid.") };
             }
 
             PropertyTreeEditor pte = PropertyTreeEditor(reinterpret_cast<void*>(component), component->GetUnderlyingComponentType());
@@ -579,7 +579,7 @@ namespace AzToolsFramework
             if (!component)
             {
                 AZ_Error("EditorComponentAPIComponent", false, "SetComponentProperty - Component Instance is Invalid.");
-                return EditorComponentAPIRequests::PropertyOutcome{AZStd::unexpect, PropertyOutcome::ErrorType("SetComponentProperty - Component Instance is Invalid.") };
+                return EditorComponentAPIRequests::PropertyOutcome{ AZStd::unexpect, PropertyOutcome::ErrorType("SetComponentProperty - Component Instance is Invalid.") };
             }
 
             PropertyTreeEditor pte = PropertyTreeEditor(reinterpret_cast<void*>(component), component->GetUnderlyingComponentType());
@@ -675,7 +675,7 @@ namespace AzToolsFramework
             }
 
             // Check for pending components
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(entityPtr->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
             for (AZ::Component* component : pendingComponents)
             {
@@ -686,7 +686,7 @@ namespace AzToolsFramework
             }
 
             // Check for disabled components
-            AZStd::vector<AZ::Component*> disabledComponents;
+            AZ::Entity::ComponentArrayType disabledComponents;
             AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entityPtr->GetId(), &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
             for (AZ::Component* component : disabledComponents)
             {
@@ -721,7 +721,7 @@ namespace AzToolsFramework
             }
 
             // Check for pending components
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(entityPtr->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
             for (AZ::Component* component : pendingComponents)
             {
@@ -732,7 +732,7 @@ namespace AzToolsFramework
             }
 
             // Check for disabled components
-            AZStd::vector<AZ::Component*> disabledComponents;
+            AZ::Entity::ComponentArrayType disabledComponents;
             AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entityPtr->GetId(), &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
             for (AZ::Component* component : disabledComponents)
             {
@@ -745,9 +745,9 @@ namespace AzToolsFramework
             return nullptr;
         }
 
-        AZStd::vector<AZ::Component*> EditorComponentAPIComponent::FindComponents(AZ::EntityId entityId, AZ::Uuid componentTypeId)
+        AZ::Entity::ComponentArrayType EditorComponentAPIComponent::FindComponents(AZ::EntityId entityId, AZ::Uuid componentTypeId)
         {
-            AZStd::vector<AZ::Component*> components;
+            AZ::Entity::ComponentArrayType components;
 
             // Get AZ::Entity*
             AZ::Entity* entityPtr = FindEntity(entityId);
@@ -769,7 +769,7 @@ namespace AzToolsFramework
             }
 
             // Check for pending components
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(entityId, &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
             for (AZ::Component* component : pendingComponents)
             {
@@ -780,7 +780,7 @@ namespace AzToolsFramework
             }
 
             // Check for disabled components
-            AZStd::vector<AZ::Component*> disabledComponents;
+            AZ::Entity::ComponentArrayType disabledComponents;
             AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entityId, &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
             for (AZ::Component* component : disabledComponents)
             {

+ 2 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/Component/EditorComponentAPIComponent.h

@@ -35,7 +35,7 @@ namespace AzToolsFramework
 
             // EditorComponentAPIBus ...
             AZStd::vector<AZ::Uuid> FindComponentTypeIdsByEntityType(const AZStd::vector<AZStd::string>& componentTypeNames, EditorComponentAPIRequests::EntityType entityType) override;
-            AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) override;
+            AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(AZStd::span<const AZ::ComponentServiceType> serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) override;
             AZStd::vector<AZStd::string> FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) override;
             AZStd::vector<AZStd::string> BuildComponentTypeNameListByEntityType(EditorComponentAPIRequests::EntityType entityType) override;
 
@@ -63,7 +63,7 @@ namespace AzToolsFramework
             AZ::Entity* FindEntity(AZ::EntityId entityId);
             AZ::Component* FindComponent(AZ::EntityId entityId, AZ::ComponentId componentId);
             AZ::Component* FindComponent(AZ::EntityId entityId, AZ::Uuid componentType);
-            AZStd::vector<AZ::Component*> FindComponents(AZ::EntityId entityId, AZ::Uuid componentType);
+            AZ::Entity::ComponentArrayType FindComponents(AZ::EntityId entityId, AZ::Uuid componentType);
 
             bool m_usePropertyVisibility = false;
             AZ::SerializeContext* m_serializeContext = nullptr;

+ 14 - 14
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityActionComponent.cpp

@@ -247,7 +247,7 @@ namespace AzToolsFramework
         }
 
 
-        void EditorEntityActionComponent::CutComponents(const AZStd::vector<AZ::Component*>& components)
+        void EditorEntityActionComponent::CutComponents(AZStd::span<AZ::Component* const> components)
         {
             // Create the mime data object which will have a serialized copy of the components
             AZStd::unique_ptr<QMimeData> mimeData = ComponentMimeData::Create(components);
@@ -266,7 +266,7 @@ namespace AzToolsFramework
             }
         }
 
-        void EditorEntityActionComponent::CopyComponents(const AZStd::vector<AZ::Component*>& components)
+        void EditorEntityActionComponent::CopyComponents(AZStd::span<AZ::Component* const> components)
         {
             // Create the mime data object
             AZStd::unique_ptr<QMimeData> mimeData = ComponentMimeData::Create(components);
@@ -293,7 +293,7 @@ namespace AzToolsFramework
             }
 
             // Create components from mime data
-            AZStd::vector<AZ::Component*> components;
+            AZ::Entity::ComponentArrayType components;
             ComponentMimeData::GetComponentDataFromMimeData(mimeData, components);
 
             auto addComponentsOutcome = AddExistingComponentsToEntityById(entityId, components);
@@ -324,7 +324,7 @@ namespace AzToolsFramework
             return ComponentMimeData::GetComponentMimeDataFromClipboard() != nullptr;
         }
 
-        void EditorEntityActionComponent::EnableComponents(const AZStd::vector<AZ::Component*>& components)
+        void EditorEntityActionComponent::EnableComponents(AZStd::span<AZ::Component* const> components)
         {
             ScopedUndoBatch undoBatch("Enable Component(s)");
 
@@ -381,7 +381,7 @@ namespace AzToolsFramework
             }
         }
 
-        void EditorEntityActionComponent::DisableComponents(const AZStd::vector<AZ::Component*>& components)
+        void EditorEntityActionComponent::DisableComponents(AZStd::span<AZ::Component* const> components)
         {
             ScopedUndoBatch undoBatch("Disable Component(s)");
 
@@ -438,14 +438,14 @@ namespace AzToolsFramework
             }
         }
 
-        EditorEntityActionComponent::RemoveComponentsOutcome EditorEntityActionComponent::RemoveComponents(const AZStd::vector<AZ::Component*>& componentsToRemove)
+        EditorEntityActionComponent::RemoveComponentsOutcome EditorEntityActionComponent::RemoveComponents(AZStd::span<AZ::Component* const> componentsToRemove)
         {
             EntityToRemoveComponentsResultMap resultMap;
             {
                 ScopedUndoBatch undoBatch("Remove Component(s)");
 
                 // Only remove, do not delete components until we know it was successful
-                AZStd::vector<AZ::Component*> removedComponents;
+                AZ::Entity::ComponentArrayType removedComponents;
                 AZStd::vector<AZ::EntityId> entityIds;
                 for (auto component : componentsToRemove)
                 {
@@ -526,7 +526,7 @@ namespace AzToolsFramework
             }
 
             // Check for pending component
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
             if (AZStd::find(pendingComponents.begin(), pendingComponents.end(), componentToRemove) != pendingComponents.end())
             {
@@ -538,7 +538,7 @@ namespace AzToolsFramework
             }
 
             // Check for disabled component
-            AZStd::vector<AZ::Component*> disabledComponents;
+            AZ::Entity::ComponentArrayType disabledComponents;
             AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
             if (AZStd::find(disabledComponents.begin(), disabledComponents.end(), componentToRemove) != disabledComponents.end())
             {
@@ -593,7 +593,7 @@ namespace AzToolsFramework
 
                     auto& entityComponentsResult = entityToAddedComponentsMap[entityId];
 
-                    AZStd::vector<AZ::Component*> componentsToAddToEntity;
+                    AZ::Entity::ComponentArrayType componentsToAddToEntity;
                     for (auto& componentClassData : componentsToAddClassData)
                     {
                         AZ_Assert(componentClassData, "null component class data provided to AddComponentsToEntities");
@@ -636,7 +636,7 @@ namespace AzToolsFramework
             return AZ::Success(AZStd::move(entityToAddedComponentsMap));
         }
 
-        EditorEntityActionComponent::AddExistingComponentsOutcome EditorEntityActionComponent::AddExistingComponentsToEntityById(const AZ::EntityId& entityId, const AZStd::vector<AZ::Component*>& componentsToAdd)
+        EditorEntityActionComponent::AddExistingComponentsOutcome EditorEntityActionComponent::AddExistingComponentsToEntityById(const AZ::EntityId& entityId, AZStd::span<AZ::Component* const> componentsToAdd)
         {
             AZ::Entity* entity = GetEntityById(entityId);
             if (!entity)
@@ -952,12 +952,12 @@ namespace AzToolsFramework
             }
 
             // Same looping algorithm as the scrubber, but we'll also get the list of added components so we can clean up the pending list if we were successful
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             pendingCompositionHandler->GetPendingComponents(pendingComponents);
 
             AZ::Entity::ComponentArrayType addedPendingComponents;
             AZ::Entity::ComponentArrayType currentComponents = entity->GetComponents();
-            while (AddAnyValidComponentsToList(currentComponents, pendingComponents, &addedPendingComponents)); // <- Intential semicolon
+            while (AddAnyValidComponentsToList(currentComponents, pendingComponents, &addedPendingComponents)); // <- Intentional semicolon
 
             if (!addedPendingComponents.empty())
             {
@@ -994,7 +994,7 @@ namespace AzToolsFramework
             }
 
             ObtainComponentWarnings(component, pendingComponentInfo.m_warnings);
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(component->GetEntityId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
             auto iterPendingComponent = AZStd::find(pendingComponents.begin(), pendingComponents.end(), component);
 

+ 6 - 6
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityActionComponent.h

@@ -40,17 +40,17 @@ namespace AzToolsFramework
             //////////////////////////////////////////////////////////////////////////
             // EntityCompositionRequestBus::Handler
             AddComponentsOutcome AddComponentsToEntities(const EntityIdList& entityIds, const AZ::ComponentTypeList& componentsToAdd) override;
-            AddExistingComponentsOutcome AddExistingComponentsToEntityById(const AZ::EntityId& entityId, const AZStd::vector<AZ::Component*>& componentsToAdd) override;
-            RemoveComponentsOutcome RemoveComponents(const AZStd::vector<AZ::Component*>& componentsToRemove) override;
+            AddExistingComponentsOutcome AddExistingComponentsToEntityById(const AZ::EntityId& entityId, AZStd::span<AZ::Component* const> componentsToAdd) override;
+            RemoveComponentsOutcome RemoveComponents(AZStd::span<AZ::Component* const> componentsToRemove) override;
             ScrubEntitiesOutcome ScrubEntities(const EntityList& entities) override;
 
-            void CutComponents(const AZStd::vector<AZ::Component*>& components) override;
-            void CopyComponents(const AZStd::vector<AZ::Component*>& components) override;
+            void CutComponents(AZStd::span<AZ::Component* const> components) override;
+            void CopyComponents(AZStd::span<AZ::Component* const> components) override;
             void PasteComponentsToEntity(AZ::EntityId entityId) override;
             bool HasComponentsToPaste() override;
 
-            void EnableComponents(const AZStd::vector<AZ::Component*>& components) override;
-            void DisableComponents(const AZStd::vector<AZ::Component*>& components) override;
+            void EnableComponents(AZStd::span<AZ::Component* const> components) override;
+            void DisableComponents(AZStd::span<AZ::Component* const> components) override;
 
             PendingComponentInfo GetPendingComponentInfo(const AZ::Component* component) override;
 

+ 39 - 5
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp

@@ -179,12 +179,46 @@ namespace AzToolsFramework
         return entities;
     }
 
+    EntityCompositionRequests::RemoveComponentsOutcome RemoveComponents(AZStd::span<AZ::Component* const> components)
+    {
+        EntityCompositionRequests::RemoveComponentsOutcome outcome = AZ::Failure(AZStd::string(""));
+        EntityCompositionRequestBus::BroadcastResult(outcome, &EntityCompositionRequests::RemoveComponents, components);
+        return outcome;
+    }
+
+    EntityCompositionRequests::RemoveComponentsOutcome RemoveComponents(AZStd::initializer_list<AZ::Component* const> components)
+    {
+        EntityCompositionRequests::RemoveComponentsOutcome outcome = AZ::Failure(AZStd::string(""));
+        EntityCompositionRequestBus::BroadcastResult(outcome, &EntityCompositionRequests::RemoveComponents, components);
+        return outcome;
+    }
+
+    void EnableComponents(AZStd::span<AZ::Component* const> components)
+    {
+        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::EnableComponents, components);
+    }
+
+    void EnableComponents(AZStd::initializer_list<AZ::Component* const> components)
+    {
+        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::EnableComponents, components);
+    }
+
+    void DisableComponents(AZStd::span<AZ::Component* const> components)
+    {
+        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::DisableComponents, components);
+    }
+
+    void DisableComponents(AZStd::initializer_list<AZ::Component* const> components)
+    {
+        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::DisableComponents, components);
+    }
+
     void GetAllComponentsForEntity(const AZ::Entity* entity, AZ::Entity::ComponentArrayType& componentsOnEntity)
     {
         if (entity)
         {
             //build a set of all active and pending components associated with the entity
-            componentsOnEntity.insert(componentsOnEntity.end(), entity->GetComponents().begin(), entity->GetComponents().end());
+            componentsOnEntity.append_range(entity->GetComponents());
             EditorPendingCompositionRequestBus::Event(entity->GetId(), &EditorPendingCompositionRequests::GetPendingComponents, componentsOnEntity);
             EditorDisabledCompositionRequestBus::Event(entity->GetId(), &EditorDisabledCompositionRequests::GetDisabledComponents, componentsOnEntity);
         }
@@ -288,8 +322,8 @@ namespace AzToolsFramework
 
     bool OffersRequiredServices(
         const AZ::SerializeContext::ClassData* componentClass,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
+        const AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+        const AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter
     )
     {
         AZ_Assert(componentClass, "Component class must not be null");
@@ -341,10 +375,10 @@ namespace AzToolsFramework
 
     bool OffersRequiredServices(
         const AZ::SerializeContext::ClassData* componentClass,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
+        const AZStd::span<const AZ::ComponentServiceType> serviceFilter
     )
     {
-        const AZStd::vector<AZ::ComponentServiceType> incompatibleServices;
+        const AZ::ComponentDescriptor::DependencyArrayType incompatibleServices;
         return OffersRequiredServices(componentClass, serviceFilter, incompatibleServices);
     }
 

+ 9 - 20
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h

@@ -73,25 +73,14 @@ namespace AzToolsFramework
         }
     };
 
-    template <typename... ComponentType>
-    EntityCompositionRequests::RemoveComponentsOutcome RemoveComponents(ComponentType... components)
-    {
-        EntityCompositionRequests::RemoveComponentsOutcome outcome = AZ::Failure(AZStd::string(""));
-        EntityCompositionRequestBus::BroadcastResult(outcome, &EntityCompositionRequests::RemoveComponents, AZ::Entity::ComponentArrayType{ components... });
-        return outcome;
-    }
+    EntityCompositionRequests::RemoveComponentsOutcome RemoveComponents(AZStd::span<AZ::Component* const> components);
+    EntityCompositionRequests::RemoveComponentsOutcome RemoveComponents(AZStd::initializer_list<AZ::Component* const> components);
 
-    template <typename... ComponentType>
-    void EnableComponents(ComponentType... components)
-    {
-        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::EnableComponents, AZ::Entity::ComponentArrayType{ components... });
-    }
+    void EnableComponents(AZStd::span<AZ::Component* const> components);
 
-    template <typename... ComponentType>
-    void DisableComponents(ComponentType... components)
-    {
-        EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::DisableComponents, AZ::Entity::ComponentArrayType{ components... });
-    }
+    void EnableComponents(AZStd::initializer_list<AZ::Component* const> components);
+    void DisableComponents(AZStd::span<AZ::Component* const> components);
+    void DisableComponents(AZStd::initializer_list<AZ::Component* const> components);
 
     void GetAllComponentsForEntity(const AZ::Entity* entity, AZ::Entity::ComponentArrayType& componentsOnEntity);
     void GetAllComponentsForEntity(const AZ::EntityId& entityId, AZ::Entity::ComponentArrayType& componentsOnEntity);
@@ -109,12 +98,12 @@ namespace AzToolsFramework
     // Returns true if the given component provides at least one of the services specified or no services are provided
     bool OffersRequiredServices(
         const AZ::SerializeContext::ClassData* componentClass,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
+        AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+        AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter
     );
     bool OffersRequiredServices(
         const AZ::SerializeContext::ClassData* componentClass,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
+        AZStd::span<const AZ::ComponentServiceType> serviceFilter
     );
 
     /// Return true if the editor should show this component to users,

+ 2 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ComponentMimeData.cpp

@@ -98,7 +98,7 @@ namespace AzToolsFramework
         return "application/x-amazon-o3de-editorcomponentdata";
     }
 
-    AZStd::unique_ptr<QMimeData> ComponentMimeData::Create(const ComponentDataContainer& components)
+    AZStd::unique_ptr<QMimeData> ComponentMimeData::Create(AZStd::span<AZ::Component* const> components)
     {
         AZ::SerializeContext* context;
         AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
@@ -126,7 +126,7 @@ namespace AzToolsFramework
 
         // Pack the components into a helper class for serialization
         ComponentMimeData componentMimeData;
-        componentMimeData.m_components = components;
+        componentMimeData.m_components.assign(components.begin(), components.end());
 
         // Save the helper class into a buffer to pack into mime data
         AZStd::vector<char> buffer;

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/ComponentMimeData.h

@@ -56,7 +56,7 @@ namespace AzToolsFramework
 
         static QString GetMimeType();
 
-        static AZStd::unique_ptr<QMimeData> Create(const ComponentDataContainer& components);
+        static AZStd::unique_ptr<QMimeData> Create(AZStd::span<AZ::Component* const> components);
         static void GetComponentDataFromMimeData(const QMimeData* mimeData, ComponentDataContainer& componentData);
 
         static const QMimeData* GetComponentMimeDataFromClipboard();

+ 16 - 16
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.cpp

@@ -82,34 +82,34 @@ namespace AzToolsFramework
 
             // Defines the function to add existing serialized identifiers from a given component list.
             auto captureExistingIdentifiersFunc =
-                [&existingSerializedIdentifiers, this](const AZStd::vector<AZ::Component*>& componentsToCapture)
-            {
-                for (const AZ::Component* component : componentsToCapture)
-                {                    
-                    AZ_Assert(
-                        component != this,
-                        "GenerateComponentSerializedIdentifier - "
-                        "Attempting to generate an identifier for a component that is already owned by the entity. ");
+                [&existingSerializedIdentifiers, this](AZStd::span<AZ::Component* const> componentsToCapture)
+                {
+                    for (const AZ::Component* component : componentsToCapture)
+                    {
+                        AZ_Assert(
+                            component != this,
+                            "GenerateComponentSerializedIdentifier - "
+                            "Attempting to generate an identifier for a component that is already owned by the entity. ");
 
-                    AZStd::string existingIdentifier = component->GetSerializedIdentifier();
+                        AZStd::string existingIdentifier = component->GetSerializedIdentifier();
 
-                    if (!existingIdentifier.empty() && RTTI_GetType() == component->RTTI_GetType())
-                    {
-                        existingSerializedIdentifiers.emplace(AZStd::move(existingIdentifier));
+                        if (!existingIdentifier.empty() && RTTI_GetType() == component->RTTI_GetType())
+                        {
+                            existingSerializedIdentifiers.emplace(AZStd::move(existingIdentifier));
+                        }
                     }
-                }
-            };
+                };
 
             // Checks all components of the entity, including pending components and disabled components.
             captureExistingIdentifiersFunc(m_entity->GetComponents());
 
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(
                 m_entity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
 
             captureExistingIdentifiersFunc(pendingComponents);
 
-            AZStd::vector<AZ::Component*> disabledComponents;
+            AZ::Entity::ComponentArrayType disabledComponents;
             AzToolsFramework::EditorDisabledCompositionRequestBus::Event(
                 m_entity->GetId(), &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
 

+ 2 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorComponentBase.h

@@ -412,7 +412,8 @@ namespace AzToolsFramework
         #define AZ_EDITOR_COMPONENT(_ComponentClass, ...)                                       \
         AZ_RTTI(_ComponentClass, __VA_ARGS__, AzToolsFramework::Components::EditorComponentBase)\
         AZ_EDITOR_COMPONENT_INTRUSIVE_DESCRIPTOR_TYPE(_ComponentClass)                          \
-        AZ_COMPONENT_BASE(_ComponentClass, __VA_ARGS__);
+        AZ_COMPONENT_BASE(_ComponentClass)                                                      \
+        AZ_CLASS_ALLOCATOR(_ComponentClass, AZ::ComponentAllocator);
         /// @endcond
 
     } // namespace Components

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h

@@ -16,7 +16,7 @@ namespace AzToolsFramework
         : public AZ::ComponentBus
     {
     public:
-        virtual void GetDisabledComponents(AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void GetDisabledComponents(AZ::Entity::ComponentArrayType& components) = 0;
         virtual void AddDisabledComponent(AZ::Component* componentToAdd) = 0;
         virtual void RemoveDisabledComponent(AZ::Component* componentToRemove) = 0;
         virtual bool IsComponentDisabled(const AZ::Component* component) = 0;

+ 2 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.cpp

@@ -46,7 +46,7 @@ namespace AzToolsFramework
                     if (disabledComponentsIter->value.IsArray())
                     {
                         // If the serialized data is an array type, then convert the data to a map.
-                        AZStd::vector<AZ::Component*> componentVector;
+                        AZ::Entity::ComponentArrayType componentVector;
                         disabledComponentsResult = ContinueLoadingFromJsonObjectField(
                             &componentVector, azrtti_typeid<decltype(componentVector)>(), inputValue, "DisabledComponents", context);
 
@@ -123,7 +123,7 @@ namespace AzToolsFramework
             services.push_back(AZ_CRC("EditorDisabledCompositionService", 0x277e3445));
         }
 
-        void EditorDisabledCompositionComponent::GetDisabledComponents(AZStd::vector<AZ::Component*>& components)
+        void EditorDisabledCompositionComponent::GetDisabledComponents(AZ::Entity::ComponentArrayType& components)
         {
             for (auto const& pair : m_disabledComponents)
             {

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.h

@@ -45,7 +45,7 @@ namespace AzToolsFramework
             static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
             ////////////////////////////////////////////////////////////////////
             // EditorDisabledCompositionRequestBus
-            void GetDisabledComponents(AZStd::vector<AZ::Component*>& components) override;
+            void GetDisabledComponents(AZ::Entity::ComponentArrayType& components) override;
             void AddDisabledComponent(AZ::Component* componentToAdd) override;
             void RemoveDisabledComponent(AZ::Component* componentToRemove) override;
             bool IsComponentDisabled(const AZ::Component* componentToCheck) override;

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp

@@ -1398,7 +1398,7 @@ namespace AzToolsFramework
             newLayer->SetSaveFormat(saveFormat);
             newLayer->SetOverwriteFlag(true);
 
-            AZStd::vector<AZ::Component*> newComponents;
+            AZ::Entity::ComponentArrayType newComponents;
             newComponents.push_back(newLayer);
 
             AzToolsFramework::Layers::EditorLayerCreationBus::Broadcast(

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h

@@ -200,7 +200,7 @@ namespace AzToolsFramework
              *                        allow all components to be added at once, instead of deactivating and re-activating
              *                        the layer for each listener on this bus adding components.
              */
-            virtual void OnNewLayerEntity(const AZ::EntityId& entityId, AZStd::vector<AZ::Component*>& componentsToAdd) = 0;
+            virtual void OnNewLayerEntity(const AZ::EntityId& entityId, AZ::Entity::ComponentArrayType& componentsToAdd) = 0;
         };
         using EditorLayerCreationBus = AZ::EBus<EditorLayerCreationNotification>;
 

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h

@@ -16,7 +16,7 @@ namespace AzToolsFramework
         : public AZ::ComponentBus
     {
     public:
-        virtual void GetPendingComponents(AZStd::vector<AZ::Component*>& components) = 0;
+        virtual void GetPendingComponents(AZ::Entity::ComponentArrayType& components) = 0;
         virtual void AddPendingComponent(AZ::Component* componentToAdd) = 0;
         virtual void RemovePendingComponent(AZ::Component* componentToRemove) = 0;
         virtual bool IsComponentPending(const AZ::Component* component) = 0;

+ 2 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.cpp

@@ -47,7 +47,7 @@ namespace AzToolsFramework
                     if (pendingComponentsIter->value.IsArray())
                     {
                         // If the serialized data is an array type, then convert the data to a map.
-                        AZStd::vector<AZ::Component*> componentVector;
+                        AZ::Entity::ComponentArrayType componentVector;
                         pendingComponentsResult = ContinueLoadingFromJsonObjectField(
                             &componentVector, azrtti_typeid<decltype(componentVector)>(), inputValue, "PendingComponents", context);
 
@@ -124,7 +124,7 @@ namespace AzToolsFramework
             services.push_back(AZ_CRC("EditorPendingCompositionService", 0x6b5b794f));
         }
 
-        void EditorPendingCompositionComponent::GetPendingComponents(AZStd::vector<AZ::Component*>& components)
+        void EditorPendingCompositionComponent::GetPendingComponents(AZ::Entity::ComponentArrayType& components)
         {
             for (auto const& pair : m_pendingComponents)
             {

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h

@@ -45,7 +45,7 @@ namespace AzToolsFramework
             static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
             ////////////////////////////////////////////////////////////////////
             // EditorPendingCompositionRequestBus
-            void GetPendingComponents(AZStd::vector<AZ::Component*>& components) override;
+            void GetPendingComponents(AZ::Entity::ComponentArrayType& components) override;
             void AddPendingComponent(AZ::Component* componentToAdd) override;
             void RemovePendingComponent(AZ::Component* componentToRemove) override;
             bool IsComponentPending(const AZ::Component* componentToCheck) override;

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp

@@ -1149,7 +1149,7 @@ namespace AzToolsFramework
             }
 
             // then check to see if there's a component pending because it's in an invalid state
-            AZStd::vector<AZ::Component*> pendingComponents;
+            AZ::Entity::ComponentArrayType pendingComponents;
             AzToolsFramework::EditorPendingCompositionRequestBus::Event(GetEntityId(),
                 &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
 

+ 11 - 11
Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp

@@ -56,10 +56,10 @@ namespace AzToolsFramework
         void BuildComponentTables(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter,
-            ComponentDataTable &componentDataTable,
-            ComponentIconTable &componentIconTable)
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter,
+            ComponentDataTable& componentDataTable,
+            ComponentIconTable& componentIconTable)
         {
             AZ_PROFILE_FUNCTION(AzToolsFramework);
             serializeContext->EnumerateDerived<AZ::Component>(
@@ -104,23 +104,23 @@ namespace AzToolsFramework
         void BuildComponentTables(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
             ComponentDataTable& componentDataTable,
             ComponentIconTable& componentIconTable)
         {
-            const AZStd::vector<AZ::ComponentServiceType> incompatibleServices;
+            const AZ::ComponentDescriptor::DependencyArrayType incompatibleServices;
             BuildComponentTables(serializeContext, componentFilter, serviceFilter, incompatibleServices, componentDataTable, componentIconTable);
         }
 
         bool ContainsEditableComponents(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter
         )
         {
             AZ_PROFILE_FUNCTION(AzToolsFramework);
-            
+
             bool containsEditable = false;
 
             serializeContext->EnumerateDerived<AZ::Component>(
@@ -150,10 +150,10 @@ namespace AzToolsFramework
         bool ContainsEditableComponents(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter
         )
         {
-            const AZStd::vector<AZ::ComponentServiceType> incompatibleServices;
+            const AZ::ComponentDescriptor::DependencyArrayType incompatibleServices;
             return ContainsEditableComponents(serializeContext, componentFilter, serviceFilter, incompatibleServices);
         }
 

+ 10 - 10
Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx

@@ -32,18 +32,18 @@ namespace AzToolsFramework
         void BuildComponentTables(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter,
-            ComponentDataTable &componentDataTable,
-            ComponentIconTable &componentIconTable
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter,
+            ComponentDataTable& componentDataTable,
+            ComponentIconTable& componentIconTable
         );
 
         void BuildComponentTables(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            ComponentDataTable &componentDataTable,
-            ComponentIconTable &componentIconTable
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            ComponentDataTable& componentDataTable,
+            ComponentIconTable& componentIconTable
         );
 
         // Returns true if any components in the given filter provide any of the services
@@ -51,14 +51,14 @@ namespace AzToolsFramework
         bool ContainsEditableComponents(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter
         );
 
         bool ContainsEditableComponents(
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter
         );
 
         QRegExp BuildFilterRegExp(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator);

+ 4 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteWidget.cpp

@@ -115,14 +115,14 @@ namespace AzToolsFramework
         AZ::SerializeContext* serializeContext,
         const AzToolsFramework::EntityIdList& selectedEntityIds,
         const AzToolsFramework::ComponentFilter& componentFilter,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter)
+        AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+        AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter)
     {
         m_serializeContext = serializeContext;
         m_selectedEntityIds = selectedEntityIds;
         m_componentFilter = componentFilter;
-        m_serviceFilter = serviceFilter;
-        m_incompatibleServiceFilter = incompatibleServiceFilter;
+        m_serviceFilter.assign_range(serviceFilter);
+        m_incompatibleServiceFilter.assign_range(incompatibleServiceFilter);
 
         UpdateContent();
         Present();

+ 5 - 5
Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteWidget.hxx

@@ -47,8 +47,8 @@ namespace AzToolsFramework
             AZ::SerializeContext* serializeContext,
             const AzToolsFramework::EntityIdList& selectedEntityIds,
             const AzToolsFramework::ComponentFilter& componentFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter);
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter);
 
         void Present();
 
@@ -58,7 +58,7 @@ namespace AzToolsFramework
         void OnAddComponentCancel();
 
     protected:
-        void focusOutEvent(QFocusEvent *event) override;
+        void focusOutEvent(QFocusEvent* event) override;
 
     private slots:
         void UpdateContent();
@@ -84,8 +84,8 @@ namespace AzToolsFramework
         AZ::SerializeContext* m_serializeContext = nullptr;
         EntityIdList m_selectedEntityIds;
         ComponentFilter m_componentFilter;
-        AZStd::vector<AZ::ComponentServiceType> m_serviceFilter;
-        AZStd::vector<AZ::ComponentServiceType> m_incompatibleServiceFilter;
+        AZ::ComponentDescriptor::DependencyArrayType m_serviceFilter;
+        AZ::ComponentDescriptor::DependencyArrayType m_incompatibleServiceFilter;
         AZStd::map<QString, bool> m_categoryExpandedState;
     };
 }

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp

@@ -281,7 +281,7 @@ namespace AzToolsFramework
         {
             ComponentPaletteUtil::ComponentDataTable componentDataTable;
             ComponentPaletteUtil::ComponentIconTable componentIconTable;
-            AZStd::vector<AZ::ComponentServiceType> serviceFilter;
+            AZ::ComponentDescriptor::DependencyArrayType serviceFilter;
 
             ComponentPaletteUtil::BuildComponentTables(serializeContext, AppearsInGameComponentMenu, serviceFilter, componentDataTable, componentIconTable);
 

+ 7 - 7
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp

@@ -54,8 +54,8 @@ namespace AzToolsFramework
         static const int kAddComponentMenuHeight = 150;
     }
 
-    template<typename T>
-    void AddUniqueItemToContainer(AZStd::vector<T>& container, const T& element)
+    template<typename T, typename Alloc>
+    void AddUniqueItemToContainer(AZStd::vector<T, Alloc>& container, const T& element)
     {
         if (AZStd::find(container.begin(), container.end(), element) == container.end())
         {
@@ -131,7 +131,7 @@ namespace AzToolsFramework
 
 
     //generate a list of all valid or pending sibling components that are service-incompatible with the specified set of component
-    AZStd::unordered_set<AZ::Component*> GetRelatedIncompatibleComponents(const AZStd::vector<AZ::Component*>& components)
+    AZStd::unordered_set<AZ::Component*> GetRelatedIncompatibleComponents(AZStd::span<AZ::Component* const> components)
     {
         AZStd::unordered_set<AZ::Component*> allIncompatibleComponents;
         for (auto component : components)
@@ -335,7 +335,7 @@ namespace AzToolsFramework
             return;
         }
 
-        AZStd::map<QString, AZStd::vector<AZ::Component*> > uniqueMessages;
+        AZStd::map<QString, AZ::Entity::ComponentArrayType > uniqueMessages;
         //Go through all of the warnings and printing them as notifications.
         for (const auto& warning : forwardPendingComponentInfo.m_warnings)
         {
@@ -433,8 +433,8 @@ namespace AzToolsFramework
 
     AzQtComponents::CardNotification* ComponentEditor::CreateNotificationForMissingComponents(
         const QString& message, 
-        const AZStd::vector<AZ::ComponentServiceType>& services, 
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServices)
+        AZStd::span<const AZ::ComponentServiceType> services,
+        AZStd::span<const AZ::ComponentServiceType> incompatibleServices)
     {
         auto notification = CreateNotification(message);
         auto featureButton = notification->addButtonFeature(tr("Add Required Component \342\226\276"));
@@ -727,7 +727,7 @@ namespace AzToolsFramework
             return QString();
         }
 
-        const AZStd::vector<AZ::Component*>& components = entity->GetComponents();
+        const auto& components = entity->GetComponents();
 
         // Gather required services for the component.
         AZ::ComponentDescriptor::DependencyArrayType requiredServices;

+ 7 - 7
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx

@@ -128,13 +128,13 @@ namespace AzToolsFramework
         void OnExpansionContractionDone();
         void OnSizeUpdateRequested();
         void OnDisplayComponentEditorMenu(const QPoint& position);
-        void OnRequestRemoveComponents(const AZStd::vector<AZ::Component*>& components);
-        void OnRequestDisableComponents(const AZStd::vector<AZ::Component*>& components);
+        void OnRequestRemoveComponents(AZStd::span<AZ::Component* const> components);
+        void OnRequestDisableComponents(AZStd::span<AZ::Component* const> components);
         void OnRequestRequiredComponents(
             const QPoint& position,
             const QSize& size,
-            const AZStd::vector<AZ::ComponentServiceType>& services,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServices);
+            AZStd::span<const AZ::ComponentServiceType> services,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServices);
         void OnRequestSelectionChange(const QPoint& position);
         void OnComponentIconClicked(const QPoint& position);
     private:
@@ -155,8 +155,8 @@ namespace AzToolsFramework
         AzQtComponents::CardNotification* CreateNotificationForConflictingComponents(const QString& message, const AZ::Entity::ComponentArrayType& conflictingComponents);
         AzQtComponents::CardNotification* CreateNotificationForMissingComponents(
             const QString& message,
-            const AZStd::vector<AZ::ComponentServiceType>& services,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServices);
+            AZStd::span<const AZ::ComponentServiceType> services,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServices);
 
         AzQtComponents::CardNotification* CreateNotificationForWarningComponents(const QString& message);
 
@@ -178,7 +178,7 @@ namespace AzToolsFramework
         /// Type of component being shown
         AZ::Uuid m_componentType = AZ::Uuid::CreateNull();
 
-        AZStd::vector<AZ::Component*> m_components;
+        AZ::Entity::ComponentArrayType m_components;
         AZ::Crc32 m_savedKeySeed;
 
         AZ::DocumentPropertyEditor::ReflectionAdapter::PropertyChangeEvent::Handler m_propertyChangeHandler;

+ 101 - 101
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp

@@ -955,7 +955,7 @@ namespace AzToolsFramework
         auto componentEditor = qobject_cast<ComponentEditor*>(sender());
         AZ_Assert(componentEditor, "sender() was not convertible to a ComponentEditor*");
 
-        const AZStd::vector<AZ::Component*>& components = componentEditor->GetComponents();
+        AZStd::span<AZ::Component* const> components = componentEditor->GetComponents();
         AZ_Assert(!components.empty() && components[0], "ComponentEditor should have at least one component.");
         QMenu menu;
 
@@ -1217,7 +1217,7 @@ namespace AzToolsFramework
         }
 
         // This follows the pattern in OnAddComponent, which also uses an empty filter.
-        AZStd::vector<AZ::ComponentServiceType> serviceFilter;
+        AZ::ComponentDescriptor::DependencyArrayType serviceFilter;
         // Components can't be added if there are none available to be added.
         return ComponentPaletteUtil::ContainsEditableComponents(m_serializeContext, m_componentFilter, serviceFilter);
     }
@@ -1355,8 +1355,8 @@ namespace AzToolsFramework
             SharedComponentArray sharedComponentArray;
             BuildSharedComponentArray(sharedComponentArray,
                 !(selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyStandardEntities ||
-                  selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities) ||
-                  selectionEntityTypeInfo == SelectionEntityTypeInfo::ContainerEntityOfFocusedPrefab);
+                    selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities) ||
+                selectionEntityTypeInfo == SelectionEntityTypeInfo::ContainerEntityOfFocusedPrefab);
 
             if (sharedComponentArray.size() == 0)
             {
@@ -1501,7 +1501,7 @@ namespace AzToolsFramework
             });
     }
 
-    void SaveComponentOrder(const AZ::EntityId entityId, const AZ::Entity::ComponentArrayType& componentsInOrder)
+    void SaveComponentOrder(const AZ::EntityId entityId, AZStd::span<AZ::Component* const> componentsInOrder)
     {
         ComponentOrderArray componentOrder;
         componentOrder.clear();
@@ -1560,7 +1560,7 @@ namespace AzToolsFramework
         return false;
     }
 
-    bool EntityPropertyEditor::AreComponentsRemovable(const AZ::Entity::ComponentArrayType& components) const
+    bool EntityPropertyEditor::AreComponentsRemovable(AZStd::span<AZ::Component* const> components) const
     {
         for (auto component : components)
         {
@@ -1596,18 +1596,18 @@ namespace AzToolsFramework
         return !GetFixedComponentListIndex(component).has_value();
     }
 
-    bool EntityPropertyEditor::AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const
+    bool EntityPropertyEditor::AreComponentsDraggable(AZStd::span<AZ::Component* const> components) const
     {
         return AZStd::all_of(
             components.begin(), components.end(), [](AZ::Component* component) { return IsComponentDraggable(component); });
     }
 
-    bool EntityPropertyEditor::AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const
+    bool EntityPropertyEditor::AreComponentsCopyable(AZStd::span<AZ::Component* const> components) const
     {
         return AreComponentsCopyable(components, m_componentFilter);
     }
 
-    bool EntityPropertyEditor::AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components, const ComponentFilter& filter)
+    bool EntityPropertyEditor::AreComponentsCopyable(AZStd::span<AZ::Component* const> components, const ComponentFilter& filter)
     {
         for (auto component : components)
         {
@@ -1673,7 +1673,7 @@ namespace AzToolsFramework
             {
                 AZ::SliceComponent::EntityAncestorList ancestors;
                 sliceReference->GetInstanceEntityAncestry(id, ancestors, 1);
-                if ( ancestors.empty() || !ancestors[0].m_entity)
+                if (ancestors.empty() || !ancestors[0].m_entity)
                 {
                     return false;
                 }
@@ -1879,7 +1879,7 @@ namespace AzToolsFramework
     {
         if (auto prefabOverridePublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabOverridePublicInterface>::Get())
         {
-            const AZStd::vector<AZ::Component*>& components = componentEditor.GetComponents();
+            AZStd::span<AZ::Component* const> components = componentEditor.GetComponents();
             AZ_Assert(!components.empty() && components[0], "ComponentEditor should have at least one component.");
 
             // Overrides on container entities are for internal use only and should not be exposed
@@ -1926,16 +1926,16 @@ namespace AzToolsFramework
              * depending on the preferences state, create the correct type of adapter, or none at all */
             ComponentEditor::ComponentAdapterFactory adapterFactory =
                 [&]() -> AZStd::shared_ptr<AZ::DocumentPropertyEditor::ComponentAdapter>
-            {
-                if (ShouldUseDPE())
                 {
-                    return (
-                        Prefab::IsInspectorOverrideManagementEnabled()
+                    if (ShouldUseDPE())
+                    {
+                        return (
+                            Prefab::IsInspectorOverrideManagementEnabled()
                             ? AZStd::make_shared<Prefab::PrefabComponentAdapter>()
                             : AZStd::make_shared<AZ::DocumentPropertyEditor::ComponentAdapter>());
-                }
-                return nullptr;
-            };
+                    }
+                    return nullptr;
+                };
             auto componentEditor = new ComponentEditor(m_serializeContext, this, this, adapterFactory);
             componentEditor->setAcceptDrops(true);
 
@@ -1953,8 +1953,8 @@ namespace AzToolsFramework
 
             connect(componentEditor, &ComponentEditor::OnDisplayComponentEditorMenu, this, &EntityPropertyEditor::OnDisplayComponentEditorMenu);
             connect(componentEditor, &ComponentEditor::OnRequestRequiredComponents, this, &EntityPropertyEditor::OnRequestRequiredComponents);
-            connect(componentEditor, &ComponentEditor::OnRequestRemoveComponents, this, [this](const AZ::Entity::ComponentArrayType& components) {DeleteComponents(components); });
-            connect(componentEditor, &ComponentEditor::OnRequestDisableComponents, this, [this](const AZ::Entity::ComponentArrayType& components) {DisableComponents(components); });
+            connect(componentEditor, &ComponentEditor::OnRequestRemoveComponents, this, [this](AZStd::span<AZ::Component* const> components) {DeleteComponents(components); });
+            connect(componentEditor, &ComponentEditor::OnRequestDisableComponents, this, [this](AZStd::span<AZ::Component* const> components) {DisableComponents(components); });
             componentEditor->GetPropertyEditor()->SetValueComparisonFunction([this](const InstanceDataNode* source, const InstanceDataNode* target) { return InstanceNodeValueHasNoPushableChange(source, target); });
             componentEditor->GetPropertyEditor()->SetReadOnlyQueryFunction([this](const InstanceDataNode* node) { return QueryInstanceDataNodeReadOnlyStatus(node); });
             componentEditor->GetPropertyEditor()->SetHiddenQueryFunction([this](const InstanceDataNode* node) { return QueryInstanceDataNodeHiddenStatus(node); });
@@ -1970,7 +1970,7 @@ namespace AzToolsFramework
                     &EntityPropertyEditor::OnComponentOverrideContextMenu);
 
                 // Subscribe to DPE property changes to keep the component icon updated based on override state
-                auto propertyChanged = 
+                auto propertyChanged =
                     [this, componentEditor](const AZ::DocumentPropertyEditor::ReflectionAdapter::PropertyChangeInfo& changeInfo)
                     {
                         // Check if the component editor is currently in use
@@ -2139,7 +2139,7 @@ namespace AzToolsFramework
         for (size_t instanceIdx = 0; instanceIdx < componentNode->GetNumInstances(); ++instanceIdx)
         {
             AZ::Component* componentInstance = m_serializeContext->Cast<AZ::Component*>(
-                    componentNode->GetInstance(instanceIdx), componentNode->GetClassMetadata()->m_typeId);
+                componentNode->GetInstance(instanceIdx), componentNode->GetClassMetadata()->m_typeId);
             if (componentInstance)
             {
                 PropertyEditorEntityChangeNotificationBus::Event(
@@ -2299,8 +2299,8 @@ namespace AzToolsFramework
 
     void EntityPropertyEditor::OnAddComponent()
     {
-        AZStd::vector<AZ::ComponentServiceType> serviceFilter;
-        AZStd::vector<AZ::ComponentServiceType> incompatibleServiceFilter;
+        AZ::ComponentDescriptor::DependencyArrayType serviceFilter;
+        AZ::ComponentDescriptor::DependencyArrayType incompatibleServiceFilter;
         QRect globalRect = GetWidgetGlobalRect(m_gui->m_addComponentButton) | GetWidgetGlobalRect(m_gui->m_componentList);
         ShowComponentPalette(m_componentPalette, globalRect.topLeft(), globalRect.size(), serviceFilter, incompatibleServiceFilter);
     }
@@ -2398,7 +2398,7 @@ namespace AzToolsFramework
 
         // Get inner class data for the clicked component.
         AZ::Component* component = m_serializeContext->Cast<AZ::Component*>(
-                componentNode->FirstInstance(), componentNode->GetClassMetadata()->m_typeId);
+            componentNode->FirstInstance(), componentNode->GetClassMetadata()->m_typeId);
         if (component)
         {
             auto componentClassData = GetComponentClassData(component);
@@ -2464,7 +2464,7 @@ namespace AzToolsFramework
 
         // Generate slice data push/pull options.
         AZ::Component* componentInstance = m_serializeContext->Cast<AZ::Component*>(
-                componentNode->FirstInstance(), componentClassData->m_typeId);
+            componentNode->FirstInstance(), componentClassData->m_typeId);
         AZ_Assert(componentInstance, "Failed to cast component instance.");
 
         // With the entity the property ultimately belongs to, we can look up slice ancestry for push/pull options.
@@ -2684,11 +2684,11 @@ namespace AzToolsFramework
         QMenu* revertMenu = nullptr;
 
         auto addRevertMenu = [&menu]()
-        {
-            QMenu* revertOverridesMenu = menu.addMenu(tr("Revert overrides"));
-            revertOverridesMenu->setToolTipsVisible(true);
-            return revertOverridesMenu;
-        };
+            {
+                QMenu* revertOverridesMenu = menu.addMenu(tr("Revert overrides"));
+                revertOverridesMenu->setToolTipsVisible(true);
+                return revertOverridesMenu;
+            };
 
         //check for changes on selected property
         if (componentClassData)
@@ -2742,9 +2742,9 @@ namespace AzToolsFramework
                             revertAction->setToolTip(tr("Revert the value for this property to the last saved state."));
                             revertAction->setEnabled(true);
                             connect(revertAction, &QAction::triggered, this, [this, componentInstance, fieldNode]()
-                            {
-                                ContextMenuActionPullFieldData(componentInstance, fieldNode);
-                            }
+                                {
+                                    ContextMenuActionPullFieldData(componentInstance, fieldNode);
+                                }
                             );
                         }
                     }
@@ -2793,9 +2793,9 @@ namespace AzToolsFramework
             QAction* revertComponentAction = revertMenu->addAction(tr("Component"));
             revertComponentAction->setToolTip(tr("Revert all properties for this component to their last saved state."));
             connect(revertComponentAction, &QAction::triggered, this, [this]()
-            {
-                ResetToSlice();
-            });
+                {
+                    ResetToSlice();
+                });
         }
 
         //check for changes on selected entities
@@ -2837,10 +2837,10 @@ namespace AzToolsFramework
             revertAction->setToolTip(QObject::tr("This will revert all component properties on this entity to the last saved."));
 
             QObject::connect(revertAction, &QAction::triggered, [relevantEntities]
-            {
-                SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
-                    &SliceEditorEntityOwnershipServiceRequests::ResetEntitiesToSliceDefaults, relevantEntities);
-            });
+                {
+                    SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
+                        &SliceEditorEntityOwnershipServiceRequests::ResetEntitiesToSliceDefaults, relevantEntities);
+                });
         }
     }
 
@@ -2874,7 +2874,7 @@ namespace AzToolsFramework
 
                 targetContainerNodesWithNewElements.insert(targetNodeParent);
             }
-            );
+        );
 
         // Invoke Add notifiers
         for (InstanceDataNode* targetContainerNode : targetContainerNodesWithNewElements)
@@ -3083,19 +3083,19 @@ namespace AzToolsFramework
 
             switch (rootType)
             {
-                case AddressRootType::RootAtComponentsContainer:
-                {
-                    // Insert a node to represent the Components container.
-                    outAddress.push_back(AZ_CRC("Components", 0xee48f5fd));
-                }
-                break;
-                case AddressRootType::RootAtEntity:
-                {
-                    // Insert a node to represent the Components container, and another to represent the entity root.
-                    outAddress.push_back(AZ_CRC("Components", 0xee48f5fd));
-                    outAddress.push_back(AZ_CRC("AZ::Entity", 0x1fd61e11));
-                }
-                break;
+            case AddressRootType::RootAtComponentsContainer:
+            {
+                // Insert a node to represent the Components container.
+                outAddress.push_back(AZ_CRC("Components", 0xee48f5fd));
+            }
+            break;
+            case AddressRootType::RootAtEntity:
+            {
+                // Insert a node to represent the Components container, and another to represent the entity root.
+                outAddress.push_back(AZ_CRC("Components", 0xee48f5fd));
+                outAddress.push_back(AZ_CRC("AZ::Entity", 0x1fd61e11));
+            }
+            break;
             }
         }
     }
@@ -3105,7 +3105,7 @@ namespace AzToolsFramework
         if (InstanceDataNode* componentNode = componentFieldNode ? componentFieldNode->GetRoot() : nullptr)
         {
             AZ::Component* component = m_serializeContext->Cast<AZ::Component*>(
-                    componentNode->FirstInstance(), componentNode->GetClassMetadata()->m_typeId);
+                componentNode->FirstInstance(), componentNode->GetClassMetadata()->m_typeId);
             if (component)
             {
                 AZ::EntityId entityId = component->GetEntityId();
@@ -3269,7 +3269,7 @@ namespace AzToolsFramework
         }
 
         size_t comboItemCount = StatusTypeToIndex(StatusItems);
-        for (size_t i=0; i<comboItemCount; ++i)
+        for (size_t i = 0; i < comboItemCount; ++i)
         {
             m_comboItems[i]->setCheckState(Qt::Unchecked);
         }
@@ -3334,36 +3334,36 @@ namespace AzToolsFramework
             m_comboItems[StatusTypeToIndex(StatusType::StatusStartActive)]->setCheckState(Qt::Checked);
         }
         else
-        if (allInactive)
-        {
-            m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[static_cast<int>(StatusTypeToIndex(StatusType::StatusStartInactive))]);
-            m_gui->m_statusComboBox->setCurrentIndex(static_cast<int>(StatusTypeToIndex(StatusType::StatusStartInactive)));
-            m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::Checked);
-        }
-        else
-        if (allEditorOnly)
-        {
-            m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[static_cast<int>(StatusTypeToIndex(StatusType::StatusEditorOnly))]);
-            m_gui->m_statusComboBox->setCurrentIndex(static_cast<int>(StatusTypeToIndex(StatusType::StatusEditorOnly)));
-            m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::Checked);
-        }
-        else // Some marked active, some not
-        {
-            m_gui->m_statusComboBox->setItalic(true);
-            m_gui->m_statusComboBox->setHeaderOverride("- Multiple selected -");
-            if (someActive)
-            {
-                m_comboItems[StatusTypeToIndex(StatusType::StatusStartActive)]->setCheckState(Qt::PartiallyChecked);
-            }
-            if (someInactive)
-            {
-                m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::PartiallyChecked);
-            }
-            if (someEditorOnly)
+            if (allInactive)
             {
-                m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::PartiallyChecked);
+                m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[static_cast<int>(StatusTypeToIndex(StatusType::StatusStartInactive))]);
+                m_gui->m_statusComboBox->setCurrentIndex(static_cast<int>(StatusTypeToIndex(StatusType::StatusStartInactive)));
+                m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::Checked);
             }
-        }
+            else
+                if (allEditorOnly)
+                {
+                    m_gui->m_statusComboBox->setHeaderOverride(m_itemNames[static_cast<int>(StatusTypeToIndex(StatusType::StatusEditorOnly))]);
+                    m_gui->m_statusComboBox->setCurrentIndex(static_cast<int>(StatusTypeToIndex(StatusType::StatusEditorOnly)));
+                    m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::Checked);
+                }
+                else // Some marked active, some not
+                {
+                    m_gui->m_statusComboBox->setItalic(true);
+                    m_gui->m_statusComboBox->setHeaderOverride("- Multiple selected -");
+                    if (someActive)
+                    {
+                        m_comboItems[StatusTypeToIndex(StatusType::StatusStartActive)]->setCheckState(Qt::PartiallyChecked);
+                    }
+                    if (someInactive)
+                    {
+                        m_comboItems[StatusTypeToIndex(StatusType::StatusStartInactive)]->setCheckState(Qt::PartiallyChecked);
+                    }
+                    if (someEditorOnly)
+                    {
+                        m_comboItems[StatusTypeToIndex(StatusType::StatusEditorOnly)]->setCheckState(Qt::PartiallyChecked);
+                    }
+                }
 
         m_gui->m_statusComboBox->setDisabled(m_selectionContainsReadOnlyEntity);
         m_gui->m_statusComboBox->setVisible(!m_isSystemEntityEditor && !m_isLevelEntityEditor);
@@ -3455,10 +3455,10 @@ namespace AzToolsFramework
     }
 
     void EntityPropertyEditor::OnRequestRequiredComponents(
-        const QPoint& position, 
-        const QSize& size, 
-        const AZStd::vector<AZ::ComponentServiceType>& services, 
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServices)
+        const QPoint& position,
+        const QSize& size,
+        AZStd::span<const AZ::ComponentServiceType> services,
+        AZStd::span<const AZ::ComponentServiceType> incompatibleServices)
     {
         ShowComponentPalette(m_componentPalette, position, size, services, incompatibleServices);
     }
@@ -3473,8 +3473,8 @@ namespace AzToolsFramework
         ComponentPaletteWidget* componentPalette,
         const QPoint& position,
         const QSize& size,
-        const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-        const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter)
+        AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+        AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter)
     {
         HideComponentPalette();
 
@@ -3747,7 +3747,7 @@ namespace AzToolsFramework
         return copyableComponents;
     }
 
-    void EntityPropertyEditor::DeleteComponents(const AZ::Entity::ComponentArrayType& components)
+    void EntityPropertyEditor::DeleteComponents(AZStd::span<AZ::Component* const> components)
     {
         if (!components.empty() && AreComponentsRemovable(components))
         {
@@ -3794,7 +3794,7 @@ namespace AzToolsFramework
         {
             ScopedUndoBatch undoBatch("Paste Component(s)");
 
-            AZ::Entity::ComponentArrayType selectedComponents = GetSelectedComponents();
+            AZStd::span<AZ::Component* const> selectedComponents = GetSelectedComponents();
 
             AZ::Entity::ComponentArrayType componentsAfterPaste;
             AZ::Entity::ComponentArrayType componentsInOrder;
@@ -3840,7 +3840,7 @@ namespace AzToolsFramework
         }
     }
 
-    void EntityPropertyEditor::EnableComponents(const AZ::Entity::ComponentArrayType& components)
+    void EntityPropertyEditor::EnableComponents(AZStd::span<AZ::Component* const> components)
     {
         EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::EnableComponents, components);
         QueuePropertyRefresh();
@@ -3851,7 +3851,7 @@ namespace AzToolsFramework
         EnableComponents(GetSelectedComponents());
     }
 
-    void EntityPropertyEditor::DisableComponents(const AZ::Entity::ComponentArrayType& components)
+    void EntityPropertyEditor::DisableComponents(AZStd::span<AZ::Component* const> components)
     {
         EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::DisableComponents, components);
         QueuePropertyRefresh();
@@ -3941,7 +3941,7 @@ namespace AzToolsFramework
                 componentsOnEntity.clear();
                 GetAllComponentsForEntityInOrder(AzToolsFramework::GetEntity(entityId), componentsOnEntity);
 
-                const AZ::Entity::ComponentArrayType& componentsToMove = componentsToEditByEntityIdItr->second;
+                AZStd::span<AZ::Component* const> componentsToMove = componentsToEditByEntityIdItr->second;
                 AZStd::sort(
                     componentsOnEntity.begin(),
                     componentsOnEntity.end(),
@@ -3983,7 +3983,7 @@ namespace AzToolsFramework
                 componentsOnEntity.clear();
                 GetAllComponentsForEntityInOrder(AzToolsFramework::GetEntity(entityId), componentsOnEntity);
 
-                const AZ::Entity::ComponentArrayType& componentsToMove = componentsToEditByEntityIdItr->second;
+                AZStd::span<AZ::Component* const> componentsToMove = componentsToEditByEntityIdItr->second;
                 AZStd::sort(
                     componentsOnEntity.begin(),
                     componentsOnEntity.end(),
@@ -4047,7 +4047,7 @@ namespace AzToolsFramework
         }
     }
 
-    void EntityPropertyEditor::MoveComponentRowBefore(const AZ::Entity::ComponentArrayType& sourceComponents, const AZ::Entity::ComponentArrayType& targetComponents, ScopedUndoBatch& undo)
+    void EntityPropertyEditor::MoveComponentRowBefore(AZStd::span<AZ::Component* const> sourceComponents, AZStd::span<AZ::Component* const> targetComponents, ScopedUndoBatch& undo)
     {
         //determine if both sets of components have the same number of elements
         //this should always be the case because component editors are only shown for components with equivalents on all selected entities
@@ -4065,7 +4065,7 @@ namespace AzToolsFramework
         }
     }
 
-    void EntityPropertyEditor::MoveComponentRowAfter(const AZ::Entity::ComponentArrayType& sourceComponents, const AZ::Entity::ComponentArrayType& targetComponents, ScopedUndoBatch& undo)
+    void EntityPropertyEditor::MoveComponentRowAfter(AZStd::span<AZ::Component* const> sourceComponents, AZStd::span<AZ::Component* const> targetComponents, ScopedUndoBatch& undo)
     {
         //determine if both sets of components have the same number of elements
         //this should always be the case because component editors are only shown for components with equivalents on all selected entities
@@ -4396,7 +4396,7 @@ namespace AzToolsFramework
         return m_selectedComponentEditors;
     }
 
-    const AZ::Entity::ComponentArrayType& EntityPropertyEditor::GetSelectedComponents() const
+    AZStd::span<AZ::Component* const> EntityPropertyEditor::GetSelectedComponents() const
     {
         return m_selectedComponents;
     }
@@ -5766,7 +5766,7 @@ namespace AzToolsFramework
 
             for (const ComponentEditor* sourceComponentEditor : sourceComponentEditors)
             {
-                const AZ::Entity::ComponentArrayType& sourceComponents = sourceComponentEditor->GetComponents();
+                AZStd::span<AZ::Component* const> sourceComponents = sourceComponentEditor->GetComponents();
                 MoveComponentRowBefore(sourceComponents, targetComponents, undo);
             }
 

+ 15 - 15
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx

@@ -186,7 +186,7 @@ namespace AzToolsFramework
 
         bool IsLockedToSpecificEntities() const { return !m_overrideSelectedEntityIds.empty(); }
 
-        static bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components, const ComponentFilter& filter);
+        static bool AreComponentsCopyable(AZStd::span<AZ::Component* const> components, const ComponentFilter& filter);
 
         ReorderState GetReorderState() const;
         ComponentEditor* GetEditorForCurrentReorderRowWidget() const;
@@ -297,11 +297,11 @@ namespace AzToolsFramework
         void UpdateEntityDisplay();
         static bool DoesComponentPassFilter(const AZ::Component* component, const ComponentFilter& filter);
         static bool IsComponentRemovable(const AZ::Component* component);
-        bool AreComponentsRemovable(const AZ::Entity::ComponentArrayType& components) const;
+        bool AreComponentsRemovable(AZStd::span<AZ::Component* const> components) const;
         static AZStd::optional<int> GetFixedComponentListIndex(const AZ::Component* component);
         static bool IsComponentDraggable(const AZ::Component* component);
-        bool AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const;
-        bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const;
+        bool AreComponentsDraggable(AZStd::span<AZ::Component* const> components) const;
+        bool AreComponentsCopyable(AZStd::span<AZ::Component* const> components) const;
 
         void AddMenuOptionsForComponents(QMenu& menu, const QPoint& position);
         void AddMenuOptionsForFields(InstanceDataNode* fieldNode, InstanceDataNode* componentNode, const AZ::SerializeContext::ClassData* componentClassData, QMenu& menu);
@@ -351,8 +351,8 @@ namespace AzToolsFramework
         void OnDisplayComponentEditorMenu(const QPoint& position);
         void OnRequestRequiredComponents(const QPoint& position,
             const QSize& size,
-            const AZStd::vector<AZ::ComponentServiceType>& services,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServices);
+            AZStd::span<const AZ::ComponentServiceType> services,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServices);
 
         AZ::Component* ExtractMatchingComponent(AZ::Component* component, AZ::Entity::ComponentArrayType& availableComponents);
 
@@ -364,8 +364,8 @@ namespace AzToolsFramework
             ComponentPaletteWidget* componentPalette,
             const QPoint& position,
             const QSize& size,
-            const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
-            const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter);
+            AZStd::span<const AZ::ComponentServiceType> serviceFilter,
+            AZStd::span<const AZ::ComponentServiceType> incompatibleServiceFilter);
 
         enum class SelectionEntityTypeInfo
         {
@@ -413,14 +413,14 @@ namespace AzToolsFramework
         bool CanPasteComponentsOnEntity(const ComponentTypeMimeData::ClassDataContainer& classDataForComponentsToPaste, const AZ::Entity* entity) const;
 
         AZ::Entity::ComponentArrayType GetCopyableComponents() const;
-        void DeleteComponents(const AZ::Entity::ComponentArrayType& components);
+        void DeleteComponents(AZStd::span<AZ::Component* const> components);
         void DeleteComponents();
         void CutComponents();
         void CopyComponents();
         void PasteComponents();
-        void EnableComponents(const AZ::Entity::ComponentArrayType& components);
+        void EnableComponents(AZStd::span<AZ::Component* const> components);
         void EnableComponents();
-        void DisableComponents(const AZ::Entity::ComponentArrayType& components);
+        void DisableComponents(AZStd::span<AZ::Component* const> components);
         void DisableComponents();
         void MoveComponentsUp();
         void MoveComponentsDown();
@@ -436,10 +436,10 @@ namespace AzToolsFramework
         void MoveComponentAfter(const AZ::Component* sourceComponent, const AZ::Component* targetComponent, ScopedUndoBatch &undo);
 
         //reorder each element of source before corresponding element of target
-        void MoveComponentRowBefore(const AZ::Entity::ComponentArrayType &sourceComponents, const AZ::Entity::ComponentArrayType &targetComponents, ScopedUndoBatch &undo);
+        void MoveComponentRowBefore(AZStd::span<AZ::Component* const> sourceComponents, AZStd::span<AZ::Component* const> targetComponents, ScopedUndoBatch &undo);
 
         //reorder each element of source after corresponding element of target
-        void MoveComponentRowAfter(const AZ::Entity::ComponentArrayType &sourceComponents, const AZ::Entity::ComponentArrayType &targetComponents, ScopedUndoBatch &undo);
+        void MoveComponentRowAfter(AZStd::span<AZ::Component* const> sourceComponents, AZStd::span<AZ::Component* const> targetComponents, ScopedUndoBatch &undo);
 
         //determine if any neighboring component editor rows can be exchanged
         bool IsMoveAllowed(const ComponentEditorVector& componentEditors) const;
@@ -473,7 +473,7 @@ namespace AzToolsFramework
         ComponentEditorVector GetIntersectingComponentEditors(const QRect& globalRect) const;
 
         const ComponentEditorVector& GetSelectedComponentEditors() const;
-        const AZ::Entity::ComponentArrayType& GetSelectedComponents() const;
+        AZStd::span<AZ::Component* const> GetSelectedComponents() const;
         const AZStd::unordered_map<AZ::EntityId, AZ::Entity::ComponentArrayType>& GetSelectedComponentsByEntityId() const;
         void UpdateSelectionCache();
 
@@ -706,7 +706,7 @@ namespace AzToolsFramework
     };
 
     void SortComponentsByOrder(AZ::EntityId entityId, AZ::Entity::ComponentArrayType& componentsOnEntity);
-    void SaveComponentOrder(AZ::EntityId entityId, const AZ::Entity::ComponentArrayType& componentsInOrder);
+    void SaveComponentOrder(AZ::EntityId entityId, AZStd::span<AZ::Component* const> componentsInOrder);
 
 } // namespace AzToolsFramework
 

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