Sfoglia il codice sorgente

NameDictionary non-singleton support (#10986)

* NameDictionary non-singleton support

Add support to the NameDictionary class to allow for local instances to
be created in addition to a global instance.

Switched the NameDictionary to register an instance pointer with an
`AZ::Interface<AZ::NameDictionary>`

The NameDictionary::IsReady and NameDictionary::Instance function now
returns the instance registered with the AZ::Interface

Changed the NameDictionary::Create function to not create a static
instance if it a NameDictionary instance is associated with an
AZ::Interface. Also simplified the logic to just invoke
`LoadDeferredNames` to link static Names into that NameDictionary instance
deferred head

Updated the Internal::NameData class to store a reference to the
NameDictionary that created it.
This allows the Internal::NameData class to avoid accessing the global
instance of the NameDictionary when releasing the Name.
This also has a second benefit in that the NameDictionary destructor can
now invoke `UnloadDeferredNames` as it doesn't need to be registered
with global instance in order for the NameData to access it.

Added a ScopedNameDataWrapper as a private member of the NameDictionary
class which updates any Internal::NameData that it creates with a
reference to itself. When the NameDictionary release it's reference to
the Internal::NameData object, it resets the Internal::NameData
reference to the NameDictionary to nullptr

Rollback the change to have the IsReady function to create the
NameDictionary
automatically(https://github.com/o3de/o3de/blame/205bb2d1fe351471c03843e0c2f4487ea279a9b0/Code/Framework/AzCore/AzCore/Name/NameDictionary.h#L60)
It is completely illogical as by default the act of checking if the
NameDictionary is ready would create it.

Moved the creation and destruction of the NameDictionary in the ComponentApplication, to the ComponentApplication constructor and Destructor so that it is available as early as possible.
The SettingsRegistryOriginTracker uses AZ::Dom::Path which requires a
NameDictionary to exist.

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

* Refactored the Name class to support multiple name dictionaries

Added constructors and functions to the Name class which accept an
AZ::NameDictionary reference which allows initializing a name using that
dictionary.

Updated the RecastNavigation NavigationMeshTest to create a
NameDictionary.

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

* Updated the Name FromStringLiteral function to require a NameDictionary*
argument.

The AZ_NAME_LITERAL macro has been updated to support passing in the
global instance of the NameDictionary.

This allows linking the Name into the Dictionary optionally if a
non-nullptr NameDictionary is supplied.

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 3 anni fa
parent
commit
efe6a2a9fc

+ 29 - 20
Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp

@@ -398,10 +398,6 @@ namespace AZ
     ComponentApplication::ComponentApplication()
         : ComponentApplication(0, nullptr)
     {
-        if (Interface<ComponentApplicationRequests>::Get() == nullptr)
-        {
-            Interface<ComponentApplicationRequests>::Register(this);
-        }
     }
 
     ComponentApplication::ComponentApplication(int argC, char** argV)
@@ -420,11 +416,11 @@ namespace AZ
         }
         else
         {
-             azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
+            azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
             // use a "valid" value here.  This is because Qt and potentially other third party libraries require
             // that ArgC be 'at least 1' and that (*argV)[0] be a valid pointer to a real null terminated string.
-             m_argC = 1;
-             m_argV = &m_commandLineBufferAddress;
+            m_argC = 1;
+            m_argV = &m_commandLineBufferAddress;
         }
 
         // Create the Event logger if it doesn't exist, otherwise reuse the one registered
@@ -438,7 +434,7 @@ namespace AZ
             m_eventLogger = EventLoggerPtr(static_cast<AZ::Debug::LocalFileEventLogger*>(AZ::Interface<AZ::Debug::IEventLogger>::Get()),
                 EventLoggerDeleter{ true });
         }
-        
+
         // 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();
@@ -449,6 +445,18 @@ namespace AZ
 
         // Now that the Allocators are initialized, the Command Line parameters can be parsed
         m_commandLine.Parse(m_argC, m_argV);
+
+
+        m_nameDictionary = AZStd::make_unique<NameDictionary>();
+
+        // Register the Name Dictionary with the AZ Interface system
+        if (AZ::Interface<AZ::NameDictionary>::Get() == nullptr)
+        {
+            AZ::Interface<AZ::NameDictionary>::Register(m_nameDictionary.get());
+            // Link the deferred names into this Name Dictionary
+            m_nameDictionary->LoadDeferredNames(AZ::Name::GetDeferredHead());
+        }
+
         SettingsRegistryMergeUtils::ParseCommandLine(m_commandLine);
 
         // Create the settings registry and register it with the AZ interface system
@@ -502,12 +510,10 @@ namespace AZ
         // Az Console initialization..
         // note that tests destroy and construct the application over and over, which is not a desirable pattern
         // so we allow the console to construct once and skip destruction / construction on consecutive runs
-        m_console = AZ::Interface<AZ::IConsole>::Get();
-        if (m_console == nullptr)
+        m_console = AZStd::make_unique<AZ::Console>(*m_settingsRegistry);
+        if (AZ::Interface<AZ::IConsole>::Get() == nullptr)
         {
-            m_console = aznew AZ::Console(*m_settingsRegistry);
-            AZ::Interface<AZ::IConsole>::Register(m_console);
-            m_ownsConsole = true;
+            AZ::Interface<AZ::IConsole>::Register(m_console.get());
             m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
             m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console);
             ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})");
@@ -538,12 +544,12 @@ namespace AZ
         m_projectPathChangedHandler = {};
 
         // Delete the AZ::IConsole if it was created by this application instance
-        if (m_ownsConsole)
+        if (AZ::Interface<AZ::IConsole>::Get() == m_console.get())
         {
-            AZ::Interface<AZ::IConsole>::Unregister(m_console);
-            delete m_console;
+            AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
             ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
         }
+        m_console.reset();
 
         m_moduleManager.reset();
         // Unregister the global Settings Registry if it is owned by this application instance
@@ -555,6 +561,13 @@ namespace AZ
         }
         m_settingsRegistry.reset();
 
+        // Unregister the Name Dictionary with the AZ Interface system and reset it
+        if (AZ::Interface<AZ::NameDictionary>::Get() == m_nameDictionary.get())
+        {
+            AZ::Interface<AZ::NameDictionary>::Unregister(m_nameDictionary.get());
+        }
+        m_nameDictionary.reset();
+
         // Set AZ::CommandLine to an empty object to clear out allocated memory before the allocators
         // are destroyed
         m_commandLine = {};
@@ -656,8 +669,6 @@ namespace AZ
             GetSerializeContext()->CreateEditContext();
         }
 
-        NameDictionary::Create();
-
         // Call this and child class's reflects
         ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); });
 
@@ -763,8 +774,6 @@ namespace AZ
         m_moduleManager->UnloadModules();
         ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
 
-        NameDictionary::Destroy();
-
         m_systemEntity.reset();
 
         Sfmt::Destroy();

+ 2 - 2
Code/Framework/AzCore/AzCore/Component/ComponentApplication.h

@@ -356,17 +356,17 @@ namespace AZ
         }
 
         AZStd::unique_ptr<ModuleManager>            m_moduleManager;
+        AZStd::unique_ptr<NameDictionary>           m_nameDictionary;
         AZStd::unique_ptr<SettingsRegistryInterface> m_settingsRegistry;
+        AZStd::unique_ptr<AZ::IConsole>             m_console;
         EntityAddedEvent                            m_entityAddedEvent;
         EntityRemovedEvent                          m_entityRemovedEvent;
         EntityAddedEvent                            m_entityActivatedEvent;
         EntityRemovedEvent                          m_entityDeactivatedEvent;
-        AZ::IConsole*                               m_console{};
         Descriptor                                  m_descriptor;
         bool                                        m_isStarted{ false };
         bool                                        m_isSystemAllocatorOwner{ false };
         bool                                        m_isOSAllocatorOwner{ false };
-        bool                                        m_ownsConsole{};
         void*                                       m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
         IAllocator*                                 m_osAllocator{ nullptr };
         EntitySetType                               m_entities;

+ 5 - 5
Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp

@@ -7,16 +7,16 @@
  */
 
 #include <AzCore/DOM/DomUtils.h>
-
 #include <AzCore/IO/ByteContainerStream.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
 
 namespace AZ::Dom::Utils
 {
-    const AZ::Name TypeFieldName = AZ::Name::FromStringLiteral("$type");
-    const AZ::Name PointerTypeName = AZ::Name::FromStringLiteral("pointer");
-    const AZ::Name PointerValueFieldName = AZ::Name::FromStringLiteral("value");
-    const AZ::Name PointerTypeFieldName = AZ::Name::FromStringLiteral("pointerType");
+    const AZ::Name TypeFieldName = AZ::Name::FromStringLiteral("$type", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name PointerTypeName = AZ::Name::FromStringLiteral("pointer", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name PointerValueFieldName = AZ::Name::FromStringLiteral("value", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name PointerTypeFieldName = AZ::Name::FromStringLiteral("pointerType", AZ::Interface<AZ::NameDictionary>::Get());
 
     Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor)
     {

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

@@ -112,7 +112,7 @@ namespace AZ
              console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());           \
              console->ExecuteDeferredConsoleCommands();                                          \
         }                                                                                        \
-        if (AZ::NameDictionary::IsReady(false))                                                  \
+        if (AZ::NameDictionary::IsReady())                                                       \
         {                                                                                        \
             AZ::NameDictionary::Instance().LoadDeferredNames(AZ::Name::GetDeferredHead());       \
         }                                                                                        \

+ 2 - 2
Code/Framework/AzCore/AzCore/Name/Internal/NameData.cpp

@@ -40,9 +40,9 @@ namespace AZ::Internal
         AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
         if (m_useCount.fetch_sub(1) == 1)
         {
-            if (AZ::NameDictionary::IsReady())
+            if (m_nameDictionary)
             {
-                AZ::NameDictionary::Instance().TryReleaseName(hash);
+                m_nameDictionary->TryReleaseName(hash);
             }
         }
     }

+ 7 - 2
Code/Framework/AzCore/AzCore/Name/Internal/NameData.h

@@ -16,6 +16,7 @@
 
 namespace AZ
 {
+    class Name;
     class NameDictionary;
 
     namespace Internal
@@ -23,6 +24,7 @@ namespace AZ
         class NameData final
         {
             friend NameDictionary;
+            friend Name;
         public:
             AZ_CLASS_ALLOCATOR(NameData, AZ::SystemAllocator, 0);
 
@@ -43,12 +45,15 @@ namespace AZ
             template <typename T>
             friend struct AZStd::IntrusivePtrCountPolicy;
 
-            AZStd::atomic_int m_useCount = {0};
+            AZStd::atomic_int m_useCount{ 0 };
             AZStd::string m_name;
             Hash m_hash;
 
             // TODO: We should be able to change this to a normal bool after introducing name dictionary garbage collection
-            AZStd::atomic<bool> m_hashCollision = false; // Tracks whether the hash has been involved in a collision
+            AZStd::atomic<bool> m_hashCollision{ false }; // Tracks whether the hash has been involved in a collision
+            //! Stores a pointer to the name dictionary that created the NameData
+            //! if the the name dictionary is destroyed, set back to nullptr
+            NameDictionary* m_nameDictionary{};
         };
     }
 }

+ 74 - 53
Code/Framework/AzCore/AzCore/Name/Name.cpp

@@ -6,6 +6,7 @@
  *
  */
 
+#include <AzCore/Interface/Interface.h>
 #include <AzCore/Name/Name.h>
 #include <AzCore/Name/NameDictionary.h>
 #include <AzCore/Name/NameSerializer.h>
@@ -18,7 +19,7 @@
 
 namespace AZ
 {
-    AZStd::thread::id Name::s_staticNameListThread = 0;
+    AZStd::thread::id Name::s_staticNameListThread{};
     Name* Name::s_staticNameBegin = nullptr;
 
     NameRef::NameRef(Name name)
@@ -77,19 +78,28 @@ namespace AZ
         return m_data == nullptr ? 0 : m_data->GetHash();
     }
 
-    Name::Name()
-        : m_view("")
-    {
-    }
+    Name::Name() = default;
 
     Name::Name(AZStd::string_view name)
     {
         SetName(name);
     }
+    Name::Name(AZStd::string_view name, NameDictionary& nameDictionary)
+    {
+        SetName(name, nameDictionary);
+    }
 
     Name::Name(Hash hash)
     {
-        *this = NameDictionary::Instance().FindName(hash);
+        auto nameDictionary = AZ::Interface<NameDictionary>::Get();
+        AZ_Assert(nameDictionary != nullptr, "hash value %u cannot be looked up before the global before the NameDictionary is ready.\n"
+            "If an explicit name dictionary is available, it can be passed to the Name(Hash, NameDictionary&) overload instead.", hash);
+        *this = nameDictionary->FindName(hash);
+    }
+
+    Name::Name(Hash hash, NameDictionary& nameDictionary)
+    {
+        *this = nameDictionary.FindName(hash);
     }
 
     Name::Name(Internal::NameData* data)
@@ -110,10 +120,10 @@ namespace AZ
     {
     }
 
-    Name Name::FromStringLiteral(AZStd::string_view name)
+    Name Name::FromStringLiteral(AZStd::string_view name, NameDictionary* nameDictionary)
     {
         Name literalName;
-        literalName.SetNameLiteral(name);
+        literalName.SetNameLiteral(name, nameDictionary);
         return literalName;
     }
 
@@ -121,17 +131,21 @@ namespace AZ
     {
         // If we're copying a string literal and it's not yet initialized,
         // we need to flag ourselves as a string literal
-        if (rhs.m_supportsDeferredLoad && rhs.m_data == nullptr)
-        {
-            m_hash = rhs.m_hash;
-            m_data = rhs.m_data;
-            SetNameLiteral(rhs.GetStringView());
-        }
-        else
+        if (this != &rhs)
         {
-            m_data = rhs.m_data;
-            m_hash = rhs.m_hash;
-            m_view = rhs.m_view;
+            if (rhs.m_supportsDeferredLoad && rhs.m_data == nullptr)
+            {
+                m_hash = rhs.m_hash;
+                m_data = rhs.m_data;
+                AZ::NameDictionary* nameDictionary = m_data != nullptr ? m_data->m_nameDictionary : nullptr;
+                SetNameLiteral(rhs.m_view, nameDictionary);
+            }
+            else
+            {
+                m_data = rhs.m_data;
+                m_hash = rhs.m_hash;
+                m_view = rhs.m_view;
+            }
         }
         return *this;
     }
@@ -139,7 +153,7 @@ namespace AZ
     Name& Name::operator=(Name&& rhs)
     {
         // If we're moving a string literal (generally from FromStringLiteral)
-        // we promote outselves to a string literal so that we respect all deferred initialization
+        // we promote this instance to a string literal so that we respect all deferred initialization
         // This covers cases like a static Name being created at function scope while the dictionary is
         // active, when a test then destroys and recreates the name dictionary, meaning the Name needs to be
         // restored via the deferred initialization logic.
@@ -147,7 +161,8 @@ namespace AZ
         {
             m_hash = rhs.m_hash;
             m_data = rhs.m_data;
-            SetNameLiteral(rhs.m_view);
+            AZ::NameDictionary* nameDictionary = m_data != nullptr ? m_data->m_nameDictionary : nullptr;
+            SetNameLiteral(rhs.m_view, nameDictionary);
         }
         else
         {
@@ -181,49 +196,54 @@ namespace AZ
 
     void Name::SetName(AZStd::string_view name)
     {
-        if (!name.empty())
-        {
-            AZ_Assert(NameDictionary::IsReady(), "Attempted to initialize Name '%.*s' before the NameDictionary is ready.\nIf this name is being constructed from a string literal, consider using AZ::Name::FromStringLiteral instead.", AZ_STRING_ARG(name));
-
-            *this = AZStd::move(NameDictionary::Instance().MakeName(name));
-        }
-        else
+        if (name.empty())
         {
             *this = Name();
+            return;
         }
+
+        auto nameDictionary = AZ::Interface<NameDictionary>::Get();
+        AZ_Assert(nameDictionary != nullptr, "Attempted to initialize Name '%.*s' using the global NameDictionary before it is ready.\n"
+            "If this name is being constructed from a string literal, consider using AZ::Name::FromStringLiteral instead.\n"
+            "Alternatively the SetName(string_view, NameDictionary&) reference overload can be used to supply an explicit name dictionary reference", AZ_STRING_ARG(name));
+
+        SetName(name, *nameDictionary);
+    }
+
+    void Name::SetName(AZStd::string_view name, NameDictionary& nameDictionary)
+    {
+        *this = nameDictionary.MakeName(name);
     }
 
-    void Name::SetNameLiteral(AZStd::string_view name)
+
+    void Name::SetNameLiteral(AZStd::string_view name, NameDictionary* nameDictionary)
     {
-        if (!name.empty())
+        if (name.empty())
         {
-            if (s_staticNameListThread == 0)
-            {
-                s_staticNameListThread = AZStd::this_thread::get_id();
-            }
-            AZ_Assert(s_staticNameListThread == AZStd::this_thread::get_id(), "Attempted to construct a name literal on a different thread from the first initialized static name, this is unsafe");
-            m_view = name;
-            if (!NameDictionary::IsReady(false))
-            {
-                // Link ourselves into the deferred list if we're not already in there
-                if (!m_supportsDeferredLoad)
-                {
-                    LinkStaticName(&s_staticNameBegin);
-                }
-            }
-            else
-            {
-                NameDictionary::Instance().LoadDeferredName(*this);
-            }
+            *this = Name();
+            return;
+        }
 
-            m_supportsDeferredLoad = true;
+        if (s_staticNameListThread == AZStd::thread::id{})
+        {
+            s_staticNameListThread = AZStd::this_thread::get_id();
         }
-        else
+        AZ_Assert(s_staticNameListThread == AZStd::this_thread::get_id(), "Attempted to construct a name literal on a different thread from the first initialized static name, this is unsafe");
+        m_view = name;
+        if (nameDictionary != nullptr)
         {
-            *this = Name();
+            nameDictionary->LoadDeferredName(*this);
         }
+        else if (!m_supportsDeferredLoad)
+        {
+            // Link ourselves into the deferred list if we're not already in there
+            LinkStaticName(&s_staticNameBegin);
+        }
+
+        m_supportsDeferredLoad = true;
     }
-    
+
+
     AZStd::string_view Name::GetStringView() const
     {
         return m_view;
@@ -278,6 +298,7 @@ namespace AZ
             m_previousName->m_nextName = m_nextName;
         }
         m_nextName = m_previousName = nullptr;
+        m_linkedToDictionary = false;
     }
 
     void Name::ScriptConstructor(Name* thisPtr, ScriptDataContext& dc)
@@ -326,7 +347,7 @@ namespace AZ
                 ->Constructor()
                 ->Constructor<AZStd::string_view>()
                 ->Method("ToString", &Name::GetCStr)
-                ->Method("Set", &Name::SetName)
+                ->Method("Set", [](Name* thisPtr, AZStd::string_view name) { thisPtr->SetName(name); })
                 ->Method("IsEmpty", &Name::IsEmpty)
                 ->Method("Equal", static_cast<bool(Name::*)(const Name&)const>(&Name::operator==))
                 ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
@@ -338,6 +359,6 @@ namespace AZ
             jsonContext->Serializer<NameJsonSerializer>()->HandlesType<Name>();
         }
     }
-    
+
 } // namespace AZ
 

+ 12 - 5
Code/Framework/AzCore/AzCore/Name/Name.h

@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <AzCore/Interface/Interface.h>
 #include <AzCore/Name/Internal/NameData.h>
 #include <AzCore/std/parallel/thread.h>
 
@@ -98,7 +99,7 @@ namespace AZ
         //!
         //! \warning FromStringLiteral is not thread-safe and should only be called from the
         //! main thread.
-        static Name FromStringLiteral(AZStd::string_view name);
+        static Name FromStringLiteral(AZStd::string_view name,  NameDictionary* nameDictionary);
 
         Name& operator=(const Name&);
         Name& operator=(Name&&);
@@ -109,12 +110,17 @@ namespace AZ
         //! The name string is used as a key to lookup an entry in the dictionary, and is not
         //! internally held after the call.
         explicit Name(AZStd::string_view name);
+        Name(AZStd::string_view name, NameDictionary& nameDictionary);
 
         //! Creates an instance of a name from a hash.
         //! The hash will be used to find an existing name in the dictionary. If there is no
         //! name with this hash, the resulting name will be empty.
         explicit Name(Hash hash);
 
+        //! Lookup an instance of a name from a hash.
+        //! This overload uses the supplied nameDictionary
+        Name(Hash hash, NameDictionary& nameDictionary);
+
         //! Creates a name from a NameRef, an already existent name within the name dictionary.
         Name(NameRef name);
 
@@ -179,12 +185,13 @@ namespace AZ
         // internally held after the call.
         // This is needed for reflection into behavior context.
         void SetName(AZStd::string_view name);
+        void SetName(AZStd::string_view name, NameDictionary& nameDictionary);
 
         // Assigns a new name.
         // The name string is stored persistently and used as a key to look up an entry in the dictionary.
         // If this is called before the dictionary is available, the key will be used when the name dictionary
         // becomes available.
-        void SetNameLiteral(AZStd::string_view name);
+        void SetNameLiteral(AZStd::string_view name, NameDictionary* nameDictionary);
 
         // This constructor is used by NameDictionary to construct from a dictionary-held NameData instance.
         Name(Internal::NameData* nameData);
@@ -211,7 +218,7 @@ namespace AZ
         // Most of the time this same information is available in m_data, but keeping it here too...
         // - Removes an indirection when accessing the name value.
         // - Allows functions like data() to return an empty string instead of null for empty Name objects.
-        AZStd::string_view m_view;
+        AZStd::string_view m_view{ "" };
 
         //! Pointer to NameData in the NameDictionary. This holds both the hash and string pair.
         AZStd::intrusive_ptr<Internal::NameData> m_data;
@@ -228,12 +235,12 @@ namespace AZ
 } // namespace AZ
 
 //! Defines a cached name literal that describes an AZ::Name. Subsequent calls to this macro will retrieve the cached name from the
-//! dictionary.
+//! global dictionary.
 #define AZ_NAME_LITERAL(str)                                                                                                               \
     (                                                                                                                                      \
         []() -> const AZ::Name&                                                                                                            \
         {                                                                                                                                  \
-            static const AZ::Name nameLiteral(AZ::Name::FromStringLiteral(str));                                                           \
+            static const AZ::Name nameLiteral(AZ::Name::FromStringLiteral(str, AZ::Interface<AZ::NameDictionary>::Get()));                 \
             return nameLiteral;                                                                                                            \
         })()
 

+ 80 - 82
Code/Framework/AzCore/AzCore/Name/NameDictionary.cpp

@@ -6,6 +6,7 @@
  *
  */
 
+#include <AzCore/Interface/Interface.h>
 #include <AzCore/Name/NameDictionary.h>
 #include <AzCore/Name/Internal/NameData.h>
 #include <AzCore/std/hash.h>
@@ -21,99 +22,65 @@ namespace AZ
 
     namespace NameDictionaryInternal
     {
-        static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
+        // Pointer which indicated that the NameDictonary associated with the AZ::Interface
+        // was created by the Create function below
+        static AZ::EnvironmentVariable<AZStd::unique_ptr<AZ::NameDictionary>> s_staticNameDictionary;
     }
 
     void NameDictionary::Create()
     {
         using namespace NameDictionaryInternal;
 
-        AZ_Assert(!s_instance, "NameDictionary already created!");
-
-        if (!s_instance)
+        if (AZ::Interface<AZ::NameDictionary>::Get() != nullptr)
         {
-            // Because the NameDictionary allocates memory using the AZ::Allocator and it is created
-            // in the executable memory space, it's ownership cannot be transferred to other module memory spaces
-            // Otherwise this could cause the the NameDictionary to be destroyed in static de-init
-            // after the AZ::Allocators have been destroyed
-            // Therefore we supply the isTransferOwnership value of false using CreateVariableEx
-            s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
-
-            // Load any deferred names stored in our module's deferred name list, if it isn't already pointing at the dictionary's list
-            // If Name::s_staticNameBegin is equal to m_deferredHead we can skip this check, as that would make the freshly created name the only
-            // entry in the list.
-            if (Name::s_staticNameBegin != nullptr && &s_instance->m_deferredHead != Name::s_staticNameBegin)
-            {
-                s_instance->m_deferredHead.m_nextName = Name::s_staticNameBegin;
-                Name::s_staticNameBegin->m_previousName = &s_instance->m_deferredHead;
-            }
-            Name* current = s_instance->m_deferredHead.m_nextName;
-            while (current != nullptr)
-            {
-                current->m_linkedToDictionary = true;
-                current = current->m_nextName;
-            }
-            s_instance->LoadDeferredNames(&s_instance->m_deferredHead);
+            AZ_Warning("NameDictionary", false, "NameDictionary already registered! Must unregister existing instance to register another one");
+            return;
         }
-    }
-
-    void NameDictionary::Destroy()
-    {
-        using namespace NameDictionaryInternal;
 
-        AZ_Assert(s_instance, "NameDictionary not created!");
+        auto nameDictionary = aznew NameDictionary();
+        AZ::Interface<AZ::NameDictionary>::Register(nameDictionary);
+        // Store pointer to the newly created NameDictionary
+        s_staticNameDictionary = AZ::Environment::CreateVariable<AZStd::unique_ptr<AZ::NameDictionary>>(NameDictionaryInstanceName, nameDictionary);
 
-        // Unload deferred names before destroying the name dictionary
-        // We need to do this because they may release their NameRefs, which require s_instance to be set
-        s_instance->UnloadDeferredNames();
-        s_instance.Reset();
+        // Load any deferred names stored in our module's deferred name list, if it isn't already pointing at the dictionary's list
+        // If Name::s_staticNameBegin is equal to m_deferredHead we can skip this check, as that would make the freshly created name the only
+        // entry in the list.
+        nameDictionary->LoadDeferredNames(AZ::Name::GetDeferredHead());
     }
 
-    bool NameDictionary::IsReady(bool tryCreate)
+    void NameDictionary::Destroy()
     {
         using namespace NameDictionaryInternal;
 
-        if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
+        if (s_staticNameDictionary && *s_staticNameDictionary)
         {
-            return false;
-        }
-
-        if (!s_instance)
-        {
-            if (tryCreate)
-            {
-                // Because the NameDictionary allocates memory using the AZ::Allocator and it is created
-                // in the executable memory space, it's ownership cannot be transferred to other module memory spaces
-                // Otherwise this could cause the the NameDictionary to be destroyed in static de-init
-                // after the AZ::Allocators have been destroyed
-                // Therefore we supply the isTransferOwnership value of false using CreateVariableEx
-                s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
-            }
-            else
+            // If the static name dictionary is registered with the AZ::Interface<AZ::NameDictionary> unregister it
+            if (AZ::Interface<AZ::NameDictionary>::Get() == s_staticNameDictionary->get())
             {
-                return false;
+                AZ::Interface<AZ::NameDictionary>::Unregister(s_staticNameDictionary->get());
             }
+
+            s_staticNameDictionary = {};
         }
+    }
 
-        return s_instance.IsConstructed();
+    bool NameDictionary::IsReady()
+    {
+        return AZ::Interface<AZ::NameDictionary>::Get();
     }
 
     NameDictionary& NameDictionary::Instance()
     {
-        using namespace NameDictionaryInternal;
-
-        if (!s_instance)
-        {
-            s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
-        }
-
-        AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
+        auto nameDictionary = AZ::Interface<AZ::NameDictionary>::Get();
+        AZ_Assert(nameDictionary != nullptr, "A NameDictionary has not been globally registered with the AZ::Interface<AZ::NameDictionary>.");
 
-        return *s_instance;
+        return *nameDictionary;
     }
 
+    // The deferred head passes in a nullptr NameDictionary as the nameDictionary isn't available
+    // until the constructor completes
     NameDictionary::NameDictionary()
-        : m_deferredHead(Name::FromStringLiteral("-fixed name dictionary deferred head-"))
+        : m_deferredHead(Name::FromStringLiteral("-fixed name dictionary deferred head-", nullptr))
     {
         // Ensure a Name that is valid for the life-cycle of this dictionary is the head of our literal linked list
         // This prevents our list head from being destroyed from a module that has shut down its AZ::Environment and
@@ -123,6 +90,9 @@ namespace AZ
     
     NameDictionary::~NameDictionary()
     {
+        // Unload deferred names
+        UnloadDeferredNames();
+
         // Ensure our module's static name list is up-to-date with what's in our deferred list.
         // This allows the NameDictionary to be recreated and restore and name literals that still exist
         // in e.g. unit tests.
@@ -132,9 +102,8 @@ namespace AZ
 
         for (const auto& keyValue : m_dictionary)
         {
-            Internal::NameData* nameData = keyValue.second;
-            const int useCount = keyValue.second->m_useCount;
-            [[maybe_unused]] const bool hadCollision = keyValue.second->m_hashCollision;
+            Internal::NameData* nameData = keyValue.second.m_nameData;
+            const int useCount = nameData->m_useCount;
 
             if (useCount == 0)
             {
@@ -143,7 +112,7 @@ namespace AZ
             else
             {
                 leaksDetected = true;
-                AZ_TracePrintf("NameDictionary", "\tLeaked Name [%3d reference(s)]: hash 0x%08X, '%.*s'\n", useCount, keyValue.first, AZ_STRING_ARG(keyValue.second->GetName()));
+                AZ_TracePrintf("NameDictionary", "\tLeaked Name [%3d reference(s)]: hash 0x%08X, '%.*s'\n", useCount, keyValue.first, AZ_STRING_ARG(nameData->GetName()));
             }
         }
 
@@ -156,7 +125,7 @@ namespace AZ
         auto iter = m_dictionary.find(hash);
         if (iter != m_dictionary.end())
         {
-            return Name(iter->second);
+            return Name(iter->second.m_nameData);
         }
         return Name();
     }
@@ -193,7 +162,6 @@ namespace AZ
             {
                 m_deferredHead.m_nextName = &deferredName;
                 deferredName.m_previousName = &m_deferredHead;
-                deferredName.m_linkedToDictionary = true;
             }
         }
     }
@@ -263,19 +231,20 @@ namespace AZ
             {
                 Internal::NameData* nameData = aznew Internal::NameData(nameString, hash);
                 nameData->m_hashCollision = collisionDetected;
-                m_dictionary.emplace(hash, nameData);
+                // Piecewise construct to prevent creating a temporary ScopedNameDataWrapper that destructs
+                m_dictionary.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(hash), AZStd::forward_as_tuple(*this, nameData));
                 return Name(nameData);
             }
             // Found the desired entry, return it
-            else if (iter->second->GetName() == nameString)
+            else if (iter->second.m_nameData->GetName() == nameString)
             {
-                return Name(iter->second);
+                return Name(iter->second.m_nameData);
             }
             // Hash collision, try a new hash
             else
             {
                 collisionDetected = true;
-                iter->second->m_hashCollision = true; // Make sure the existing entry is flagged as colliding too
+                iter->second.m_nameData->m_hashCollision = true; // Make sure the existing entry is flagged as colliding too
                 ++hash;
                 iter = m_dictionary.find(hash);
             }
@@ -310,7 +279,7 @@ namespace AZ
             return;
         }
 
-        Internal::NameData* nameData = dictIt->second;
+        Internal::NameData* nameData = dictIt->second.m_nameData;
 
         // Check m_hashCollision inside the m_sharedMutex because a new collision could have happened
         // on another thread before taking the lock.
@@ -350,26 +319,27 @@ namespace AZ
 
             for (auto& iter : m_dictionary)
             {
-                const size_t nameLength = iter.second->m_name.size();
+                Internal::NameData* nameData = iter.second.m_nameData;
+                const size_t nameLength = nameData->m_name.size();
                 actualStringMemoryUsed += nameLength;
-                potentialStringMemoryUsed += (nameLength * iter.second->m_useCount);
+                potentialStringMemoryUsed += (nameLength * nameData->m_useCount);
 
                 if (!longestName || longestName->m_name.size() < nameLength)
                 {
-                    longestName = iter.second;
+                    longestName = nameData;
                 }
 
                 if (!mostRepeatedName)
                 {
-                    mostRepeatedName = iter.second;
+                    mostRepeatedName = nameData;
                 }
                 else
                 {
                     const size_t mostIndividualSavings = mostRepeatedName->m_name.size() * (mostRepeatedName->m_useCount - 1);
-                    const size_t currentIndividualSavings = nameLength * (iter.second->m_useCount - 1);
+                    const size_t currentIndividualSavings = nameLength * (nameData->m_useCount - 1);
                     if (currentIndividualSavings > mostIndividualSavings)
                     {
-                        mostRepeatedName = iter.second;
+                        mostRepeatedName = nameData;
                     }
                 }
             }
@@ -405,4 +375,32 @@ namespace AZ
         const uint32_t hash = AZStd::hash<AZStd::string_view>()(name) & 0xFFFFFFFF;
         return hash;
     }
+
+
+    // NameDictionary::ScopedNameDataWrapper RAII implementation
+    NameDictionary::ScopedNameDataWrapper::ScopedNameDataWrapper(NameDictionary& nameDictionary, Internal::NameData* nameData)
+        : m_nameData(nameData)
+        , m_nameDictionary(nameDictionary)
+    {
+        if (m_nameData->m_nameDictionary == nullptr)
+        {
+            m_nameData->m_nameDictionary = &m_nameDictionary;
+        }
+    }
+
+    NameDictionary::ScopedNameDataWrapper::ScopedNameDataWrapper(ScopedNameDataWrapper&& other)
+        : m_nameData(other.m_nameData)
+        , m_nameDictionary(other.m_nameDictionary)
+    {
+        // Reset to nullptr
+        other.m_nameData = nullptr;
+    }
+
+    NameDictionary::ScopedNameDataWrapper::~ScopedNameDataWrapper()
+    {
+        if (m_nameData->m_nameDictionary == &m_nameDictionary)
+        {
+            m_nameData->m_nameDictionary = nullptr;
+        }
+    }
 }

+ 27 - 4
Code/Framework/AzCore/AzCore/Name/NameDictionary.h

@@ -19,6 +19,11 @@
 namespace UnitTest
 {
     class NameDictionaryTester;
+
+    // forward declare RunConcurrencyTest function in UnitTest namespace
+    // so it can be friended
+    template<class ConcurrencyTestThreadT>
+    void RunConcurrencyTest(uint32_t nameCount, uint32_t threadsPerName);
 }
 
 namespace AZ
@@ -48,16 +53,19 @@ namespace AZ
         friend Name;
         friend Internal::NameData;
         friend UnitTest::NameDictionaryTester;
+        template<class ConcurrentcyTestThreadT>
+        friend void UnitTest::RunConcurrencyTest(uint32_t nameCount, uint32_t threadsPerName);
+
         template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
             -> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
         template<typename T> constexpr friend void AZStd::destroy_at(T*);
 
     public:
-
+        AZ_TYPE_INFO(NameDictionary, "{6DBF9DEA-1F65-44DB-977C-65BA9047E869}");
         static void Create();
 
         static void Destroy();
-        static bool IsReady(bool tryCreate = true);
+        static bool IsReady();
         static NameDictionary& Instance();
 
         //! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
@@ -73,13 +81,13 @@ namespace AZ
         Name FindName(Name::Hash hash) const;
 
         NameDictionary();
+        ~NameDictionary();
 
         //! Loads a list of names, starting at a given list head, and ensures they're all created and linked
         //! into our list of deferred load names.
         void LoadDeferredNames(Name* deferredHead);
 
     private:
-        ~NameDictionary();
 
         void ReportStats() const;
 
@@ -104,7 +112,22 @@ namespace AZ
         //! Unloads the data with all deferred names registered using LoadDeferredName.
         void UnloadDeferredNames();
 
-        AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
+        //! Wrapper structure around a NameData pointer
+        //! Which sets the Internal::NameData::m_nameDictionary pointer to this name dictionary
+        //! instance on construction and to nullptr on destruction
+        struct ScopedNameDataWrapper
+        {
+            ScopedNameDataWrapper(NameDictionary& nameDictionary, Internal::NameData* nameData);
+            // Move constructor to prevent m_nameData from being propagated to copies
+            ScopedNameDataWrapper(ScopedNameDataWrapper&&);
+            ~ScopedNameDataWrapper();
+
+            Internal::NameData* m_nameData{};
+        private:
+            NameDictionary& m_nameDictionary;
+        };
+
+        AZStd::unordered_map<Name::Hash, ScopedNameDataWrapper> m_dictionary;
         mutable AZStd::shared_mutex m_sharedMutex;
 
         //! A fixed Name used as the head of a linked list of Name literals.

+ 4 - 3
Code/Framework/AzCore/AzCore/RTTI/ReflectContext.cpp

@@ -7,15 +7,16 @@
  */
 #include <AzCore/RTTI/ReflectContext.h>
 #include <AzCore/Component/Component.h>
+#include <AzCore/Name/NameDictionary.h>
 
 #include <AzCore/std/functional.h>
 #include <AzCore/std/string/string.h>
 
 namespace AZ
 {
-    const AZ::Name Attribute::s_typeField = AZ::Name::FromStringLiteral("$type");
-    const AZ::Name Attribute::s_instanceField = AZ::Name::FromStringLiteral("instance");
-    const AZ::Name Attribute::s_attributeField = AZ::Name::FromStringLiteral("attribute");
+    const AZ::Name Attribute::s_typeField = AZ::Name::FromStringLiteral("$type", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name Attribute::s_instanceField = AZ::Name::FromStringLiteral("instance", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name Attribute::s_attributeField = AZ::Name::FromStringLiteral("attribute", AZ::Interface<AZ::NameDictionary>::Get());
 
     //=========================================================================
     // OnDemandReflectionOwner

+ 2 - 1
Code/Framework/AzCore/Tests/Name/NameBenchmarks.cpp

@@ -6,6 +6,7 @@
  *
  */
 
+#include <AzCore/Interface/Interface.h>
 #include <AzCore/Name/Name.h>
 #include <AzCore/Name/NameDictionary.h>
 #include <AzCore/UnitTest/TestTypes.h>
@@ -165,7 +166,7 @@ namespace AZ::NameBenchmarks
         {
             for (int64_t i = 0; i < state.range(0); ++i)
             {
-                names[i] = (Name::FromStringLiteral("created as a literal"));
+                names[i] = (Name::FromStringLiteral("created as a literal", AZ::Interface<AZ::NameDictionary>::Get()));
             }
             for (int64_t i = 0; i < state.range(0); ++i)
             {

+ 94 - 9
Code/Framework/AzCore/Tests/Name/NameTests.cpp

@@ -33,7 +33,7 @@
 
 namespace UnitTest
 {
-    static AZ::Name globalName = AZ::Name::FromStringLiteral("global");
+    static AZ::Name globalName = AZ::Name::FromStringLiteral("global", AZ::Interface<AZ::NameDictionary>::Get());
 
     class NameDictionaryTester
     {
@@ -50,7 +50,7 @@ namespace UnitTest
             AZ::NameDictionary::Destroy();
         }
 
-        static const AZStd::unordered_map<AZ::Name::Hash, AZ::Internal::NameData*>& GetDictionary()
+        static const auto& GetDictionary()
         {
             return AZ::NameDictionary::Instance().m_dictionary;
         }
@@ -84,14 +84,14 @@ namespace UnitTest
         {
             uint32_t hash = AZ::NameDictionary::Instance().CalcHash(name);
 
-            if (maxUniqueHashes < UINT32_MAX)
+            if (maxUniqueHashes < std::numeric_limits<uint32_t>::max())
             {
                 hash %= maxUniqueHashes;
                 // Rather than use this modded value as the hash, we run a string hash again to
                 // get spread-out hash values that are similar to the real ones, and avoid clustering.
-                char buffer[16];
-                azsnprintf(buffer, 16, "%u", hash);
-                hash = AZStd::hash<AZStd::string_view>()(buffer) & 0xFFFFFFFF;
+                AZStd::fixed_string<16> buffer;
+                AZStd::to_string(buffer, hash);
+                hash = AZStd::hash<AZStd::string_view>{}(buffer) & 0xFFFFFFFF;
             }
 
             return hash;
@@ -303,8 +303,9 @@ namespace UnitTest
         {
             auto& globalDictionary = NameDictionaryTester::GetDictionary();
             // Workaround VS2022 17.3 issue with incorrect detection of unused lambda captures assigning the nameString reference to a same type
-            auto it = AZStd::find_if(globalDictionary.begin(), globalDictionary.end(), [&nameString = nameString](AZStd::pair<AZ::Name::Hash, AZ::Internal::NameData*> entry) {
-                return entry.second->GetName() == nameString;
+            auto it = AZStd::find_if(globalDictionary.begin(), globalDictionary.end(), [&nameString = nameString](const AZStd::pair<AZ::Name::Hash, AZ::NameDictionary::ScopedNameDataWrapper>& entry)
+            {
+                return entry.second.m_nameData->GetName() == nameString;
             });
             EXPECT_TRUE(it != globalDictionary.end()) << "Can't find '" << nameString.data() << "' in local dictionary.";
         }
@@ -682,7 +683,7 @@ namespace UnitTest
 
     TEST_F(NameTest, NameLiteral)
     {
-        static AZ::Name staticName = AZ::Name::FromStringLiteral("static");
+        static AZ::Name staticName = AZ::Name::FromStringLiteral("static", AZ::Interface<AZ::NameDictionary>::Get());
         EXPECT_EQ("literal", AZ_NAME_LITERAL("literal").GetStringView());
         EXPECT_EQ("static", staticName.GetStringView());
         EXPECT_EQ("global", globalName.GetStringView());
@@ -897,5 +898,89 @@ namespace UnitTest
 
         EXPECT_LT(nameTime, stringTime);
     }
+
+    // Fixture for validating that multiple NameDictionarys can exist at once.
+    class MultiNameDictionaryFixture
+        : public ScopedAllocatorSetupFixture
+    {
+    public:
+        MultiNameDictionaryFixture()
+        {
+            m_nameDictionary1 = AZStd::make_unique<AZ::NameDictionary>();
+            m_nameDictionary2 = AZStd::make_unique<AZ::NameDictionary>();
+        }
+
+        ~MultiNameDictionaryFixture()
+        {
+            m_nameDictionary2.reset();
+            m_nameDictionary1.reset();
+        }
+
+    protected:
+        AZStd::unique_ptr<AZ::NameDictionary> m_nameDictionary1;
+        AZStd::unique_ptr<AZ::NameDictionary> m_nameDictionary2;
+    };
+
+    TEST_F(MultiNameDictionaryFixture, MultipleDictionaries_Contains_DifferentNameInstances)
+    {
+        // Dictionary 1 will contain the names of "Name1" and "Dictionary1NameOnly"
+        AZ::Name name1FromDict1 = m_nameDictionary1->MakeName("Name1");
+        AZ::Name dict1OnlyName = m_nameDictionary1->MakeName("Dictionary1NameOnly");
+
+        // Dictionary 2 will contain the names of "Name1" and "Dictionary2NameOnly"
+        AZ::Name name1FromDict2 = m_nameDictionary2->MakeName("Name1");
+        AZ::Name dict2OnlyName = m_nameDictionary2->MakeName("Dictionary2NameOnly");
+
+        // First dictionary EXPECTS
+        EXPECT_FALSE(m_nameDictionary1->FindName(name1FromDict1.GetHash()).IsEmpty());
+        EXPECT_FALSE(m_nameDictionary1->FindName(dict1OnlyName.GetHash()).IsEmpty());
+        // The first dictionary should NOT contain Dictionary2NameOnly
+        EXPECT_TRUE(m_nameDictionary1->FindName(dict2OnlyName.GetHash()).IsEmpty());
+
+        // Second dictionary EXPECTS
+        EXPECT_FALSE(m_nameDictionary2->FindName(name1FromDict1.GetHash()).IsEmpty());
+        EXPECT_FALSE(m_nameDictionary2->FindName(dict2OnlyName.GetHash()).IsEmpty());
+        // The second dictionary should NOT contain Dictionary1NameOnly
+        EXPECT_TRUE(m_nameDictionary2->FindName(dict1OnlyName.GetHash()).IsEmpty());
+
+        // reset the name1FromDict1 variable
+        // Only the reference to "Name1" entry in first dictionary should be removed
+        const AZ::Name::Hash name1Hash = name1FromDict1.GetHash();
+        name1FromDict1 = AZ::Name{};
+
+        EXPECT_TRUE(m_nameDictionary1->FindName(name1Hash).IsEmpty());
+        EXPECT_FALSE(m_nameDictionary2->FindName(name1FromDict2.GetHash()).IsEmpty());
+    }
+
+    TEST_F(MultiNameDictionaryFixture, NameLiteral_IsOnlyLinkedToSingleNameDictionary)
+    {
+        // When run with --gtest_repeat=2 the literal is not relinked to the NameDictionary
+        // if a static variable is used due to not running the constructor
+        const AZ::Name staticName = AZ::Name::FromStringLiteral("firstStatic", m_nameDictionary1.get());
+        const AZ::Name literalRef = AZ::Name::FromStringLiteral("secondLiteral", m_nameDictionary2.get());
+
+        // "firstStatic" should be in dictionary1, but not dictionary2
+        EXPECT_FALSE(m_nameDictionary1->FindName(staticName.GetHash()).IsEmpty());
+        EXPECT_TRUE(m_nameDictionary2->FindName(staticName.GetHash()).IsEmpty());
+
+        // "secondLiteral" should be in dictionary2, but not dictionary1
+        EXPECT_FALSE(m_nameDictionary2->FindName(literalRef.GetHash()).IsEmpty());
+        EXPECT_TRUE(m_nameDictionary1->FindName(literalRef.GetHash()).IsEmpty());
+
+        // Add a "firstStatic" to second dictionary
+        const AZ::Name staticNameDict2{ AZ::Name::FromStringLiteral("firstStatic", m_nameDictionary2.get()) };
+        EXPECT_FALSE(m_nameDictionary1->FindName(staticName.GetHash()).IsEmpty());
+        EXPECT_FALSE(m_nameDictionary2->FindName(staticNameDict2.GetHash()).IsEmpty());
+
+        // Now create a second name literal for the string literal of "secondLiteral"
+        // There should be two different "secondLiteral" instance each associated with a different name dictionary
+        const AZ::Name literalRefForDict1 = AZ::Name::FromStringLiteral("secondLiteral", m_nameDictionary1.get());
+
+        EXPECT_EQ(literalRef, literalRefForDict1) << "The values of the names should be equal";
+        EXPECT_NE(&literalRef, &literalRefForDict1) << "The memory address of the names should not be equal";
+
+        EXPECT_FALSE(m_nameDictionary1->FindName(literalRef.GetHash()).IsEmpty());
+        EXPECT_FALSE(m_nameDictionary2->FindName(literalRefForDict1.GetHash()).IsEmpty());
+    }
 }
 

+ 6 - 5
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/DocumentAdapter.cpp

@@ -9,6 +9,7 @@
 #include <AzCore/Console/IConsole.h>
 #include <AzCore/DOM/DomComparison.h>
 #include <AzCore/DOM/DomUtils.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <AzFramework/DocumentPropertyEditor/DocumentAdapter.h>
 #include <AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h>
 
@@ -23,11 +24,11 @@ AZ_CVAR(
 
 namespace AZ::DocumentPropertyEditor
 {
-    const AZ::Name BoundAdapterMessage::s_typeField = AZ::Name::FromStringLiteral("$type");
-    const AZ::Name BoundAdapterMessage::s_adapterField = AZ::Name::FromStringLiteral("adapter");
-    const AZ::Name BoundAdapterMessage::s_messageNameField = AZ::Name::FromStringLiteral("messageName");
-    const AZ::Name BoundAdapterMessage::s_messageOriginField = AZ::Name::FromStringLiteral("messageOrigin");
-    const AZ::Name BoundAdapterMessage::s_contextDataField = AZ::Name::FromStringLiteral("contextData");
+    const AZ::Name BoundAdapterMessage::s_typeField = AZ::Name::FromStringLiteral("$type", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name BoundAdapterMessage::s_adapterField = AZ::Name::FromStringLiteral("adapter", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name BoundAdapterMessage::s_messageNameField = AZ::Name::FromStringLiteral("messageName", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name BoundAdapterMessage::s_messageOriginField = AZ::Name::FromStringLiteral("messageOrigin", AZ::Interface<AZ::NameDictionary>::Get());
+    const AZ::Name BoundAdapterMessage::s_contextDataField = AZ::Name::FromStringLiteral("contextData", AZ::Interface<AZ::NameDictionary>::Get());
 
     Dom::Value DocumentAdapter::GetContents() const
     {

+ 1 - 0
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/DocumentSchema.h

@@ -11,6 +11,7 @@
 #include <AzCore/DOM/DomUtils.h>
 #include <AzCore/DOM/DomValue.h>
 #include <AzCore/Name/Name.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <AzCore/Outcome/Outcome.h>
 #include <AzCore/RTTI/AttributeReader.h>
 #include <AzCore/std/smart_ptr/make_shared.h>

+ 8 - 8
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp

@@ -9,8 +9,8 @@
 #include <AzCore/Component/ComponentApplicationBus.h>
 #include <AzCore/DOM/DomPath.h>
 #include <AzCore/DOM/DomUtils.h>
-#include <AzCore/Interface/Interface.h>
 #include <AzCore/Name/Name.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <AzCore/RTTI/AttributeReader.h>
 #include <AzCore/RTTI/TypeInfo.h>
 #include <AzCore/Serialization/Utils.h>
@@ -23,13 +23,13 @@ namespace AZ::Reflection
 {
     namespace DescriptorAttributes
     {
-        const Name Handler = Name::FromStringLiteral("Handler");
-        const Name Label = Name::FromStringLiteral("Label");
-        const Name Description = Name::FromStringLiteral("Description");
-        const Name SerializedPath = Name::FromStringLiteral("SerializedPath");
-        const Name Container = Name::FromStringLiteral("Container");
-        const Name ParentContainer = Name::FromStringLiteral("ParentContainer");
-        const Name ParentContainerInstance = Name::FromStringLiteral("ParentContainerInstance");
+        const Name Handler = Name::FromStringLiteral("Handler", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name Label = Name::FromStringLiteral("Label", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name Description = Name::FromStringLiteral("Description", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name SerializedPath = Name::FromStringLiteral("SerializedPath", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name Container = Name::FromStringLiteral("Container", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name ParentContainer = Name::FromStringLiteral("ParentContainer", AZ::Interface<AZ::NameDictionary>::Get());
+        const Name ParentContainerInstance = Name::FromStringLiteral("ParentContainerInstance", AZ::Interface<AZ::NameDictionary>::Get());
     } // namespace DescriptorAttributes
 
     namespace LegacyReflectionInternal

+ 1 - 0
Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp

@@ -16,6 +16,7 @@
 #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
 #include <Atom/RPI.Public/Material/Material.h>
 #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
+#include <AzCore/Name/NameDictionary.h>
 
 namespace AZ
 {

+ 1 - 0
Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp

@@ -9,6 +9,7 @@
 #include <Shadows/ProjectedShadowFeatureProcessor.h>
 
 #include <AzCore/Math/MatrixUtils.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <Math/GaussianMathFilter.h>
 #include <Atom/RPI.Public/RenderPipeline.h>
 #include <Atom/RPI.Public/RPISystemInterface.h>

+ 3 - 2
Gems/LyShine/Code/Source/World/UiCanvasOnMeshComponent.cpp

@@ -12,6 +12,7 @@
 #include <AzCore/Asset/AssetSerializer.h>
 #include <AzCore/Math/IntersectPoint.h>
 #include <AzCore/Math/IntersectSegment.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <LyShine/Bus/UiCanvasBus.h>
 #include <LyShine/Bus/World/UiCanvasRefBus.h>
 #include <LyShine/UiSerializeHelpers.h>
@@ -287,8 +288,8 @@ bool UiCanvasOnMeshComponent::CalculateUVFromRayIntersection(const AzFramework::
     for (const AZ::RPI::ModelLodAsset::Mesh& mesh : meshes)
     {
         // Find position and UV semantics
-        static const AZ::Name positionName = AZ::Name::FromStringLiteral("POSITION");
-        static const AZ::Name uvName = AZ::Name::FromStringLiteral("UV");
+        static const AZ::Name positionName = AZ::Name::FromStringLiteral("POSITION", AZ::Interface<AZ::NameDictionary>::Get());
+        static const AZ::Name uvName = AZ::Name::FromStringLiteral("UV", AZ::Interface<AZ::NameDictionary>::Get());
         auto streamBufferList = mesh.GetStreamBufferInfoList();
         const AZ::RPI::ModelLodAsset::Mesh::StreamBufferInfo* positionBuffer = nullptr;
         const AZ::RPI::ModelLodAsset::Mesh::StreamBufferInfo* uvBuffer = nullptr;

+ 8 - 0
Gems/RecastNavigation/Code/Tests/NavigationMeshTest.cpp

@@ -12,6 +12,7 @@
 #include <AzCore/Component/Entity.h>
 #include <AzCore/Console/Console.h>
 #include <AzCore/EBus/EventSchedulerSystemComponent.h>
+#include <AzCore/Name/NameDictionary.h>
 #include <AzCore/std/smart_ptr/unique_ptr.h>
 #include <AzCore/UnitTest/TestTypes.h>
 #include <AzCore/UnitTest/Mocks/MockITime.h>
@@ -54,6 +55,7 @@ namespace RecastNavigationTests
         unique_ptr<UnitTest::MockPhysicsShape> m_mockPhysicsShape;
         unique_ptr<UnitTest::MockSimulatedBody> m_mockSimulatedBody;
         unique_ptr<AZ::Console> m_console;
+        unique_ptr<AZ::NameDictionary> m_nameDictionary;
 
         void SetUp() override
         {
@@ -62,6 +64,9 @@ namespace RecastNavigationTests
             m_console.reset(aznew AZ::Console());
             AZ::Interface<AZ::IConsole>::Register(m_console.get());
 
+            m_nameDictionary = AZStd::make_unique<AZ::NameDictionary>();
+            AZ::Interface<AZ::NameDictionary>::Register(m_nameDictionary.get());
+
             // register components involved in testing
             m_descriptors = AZStd::make_unique<AZStd::vector<AZ::ComponentDescriptor*>>();
             m_sc = AZStd::make_unique<AZ::SerializeContext>();
@@ -98,6 +103,9 @@ namespace RecastNavigationTests
             m_sc = {};
             m_bc = {};
 
+            AZ::Interface<AZ::NameDictionary>::Unregister(m_nameDictionary.get());
+            m_nameDictionary.reset();
+
             AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
             m_console = {};
             ::UnitTest::AllocatorsFixture::TearDown();

+ 3 - 1
Gems/Stars/Code/Source/StarsFeatureProcessor.cpp

@@ -20,6 +20,8 @@
 #include <Atom/RPI.Public/ViewportContext.h>
 #include <Atom/RPI.Public/ViewportContextBus.h>
 
+#include <AzCore/Name/NameDictionary.h>
+
 namespace AZ::Render
 {
     void StarsFeatureProcessor::Reflect(ReflectContext* context)
@@ -252,7 +254,7 @@ namespace AZ::Render
 
         auto setClearValue = [&](RPI::Pass* pass)-> RPI::PassFilterExecutionFlow
         {
-            Name slotName = Name::FromStringLiteral(slot);
+            Name slotName = Name::FromStringLiteral(slot, AZ::Interface<AZ::NameDictionary>::Get());
             if (auto binding = pass->FindAttachmentBinding(slotName))
             {
                 binding->m_unifiedScopeDesc.m_loadStoreAction.m_clearValue = blackClearValue;