From f36255b22ad0115c2cd86b184ff9f8518419857e Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 8 Dec 2021 13:57:24 -0800 Subject: [PATCH 01/49] Initial value implementation Signed-off-by: Nicholas Van Sickle --- .../AzCore/AzCore/DOM/DomDocument.cpp | 0 .../Framework/AzCore/AzCore/DOM/DomDocument.h | 0 Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 747 ++++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomValue.h | 289 +++++++ .../AzCore/AzCore/azcore_files.cmake | 4 + 5 files changed, 1040 insertions(+) create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomDocument.h create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomValue.cpp create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomValue.h diff --git a/Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp b/Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Framework/AzCore/AzCore/DOM/DomDocument.h b/Code/Framework/AzCore/AzCore/DOM/DomDocument.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp new file mode 100644 index 0000000000..ad6e648620 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -0,0 +1,747 @@ +/* + * 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 + +#include + +namespace AZ::Dom +{ + template + AZStd::shared_ptr& CheckCopyOnWrite(AZStd::shared_ptr& refCountedPointer) + { + if (refCountedPointer.use_count() > 1) + { + AZStd::shared_ptr newPointer = AZStd::make_shared(); + *newPointer = *refCountedPointer; + refCountedPointer = AZStd::move(newPointer); + } + return refCountedPointer; + } + + AZ::Name Node::GetName() const + { + return m_name; + } + + void Node::SetName(AZ::Name name) + { + m_name = name; + } + + ObjectPtr Node::GetMutableProperties() + { + CheckCopyOnWrite(m_object); + return m_object; + } + + ConstObjectPtr Node::GetProperties() const + { + return m_object; + } + + ArrayPtr Node::GetMutableChildren() + { + CheckCopyOnWrite(m_array); + return m_array; + } + + ConstArrayPtr Node::GetChildren() const + { + return m_array; + } + + bool Node::operator==(const Node& rhs) const + { + return m_name == rhs.m_name && m_array == rhs.m_array && m_object == rhs.m_object; + } + + Value::Value() + { + } + + Value::Value(const Value& value) + : m_value(value.m_value) + { + } + + Value::Value(Value&& value) noexcept + : m_value(value.m_value) + { + } + + Value::Value(AZStd::string_view string, bool copy) + { + if (copy) + { + CopyFromString(string); + } + else + { + SetString(string); + } + } + + Value::Value(AZStd::any* value) + : m_value(value) + { + } + + Value Value::FromOpaqueValue(AZStd::any& value) + { + return Value(&value); + } + + Value::Value(int64_t value) + : m_value(value) + { + } + + Value::Value(uint64_t value) + : m_value(value) + { + } + + Value::Value(double value) + : m_value(value) + { + } + + Value::Value(bool value) + : m_value(value) + { + } + + Value& Value::operator=(const Value& other) + { + m_value = other.m_value; + return *this; + } + + Value& Value::operator=(Value&& other) noexcept + { + m_value = other.m_value; + return *this; + } + + bool Value::operator==(const Value& rhs) const + { + if (IsString() && rhs.IsString()) + { + return GetString() == rhs.GetString(); + } + else if (IsNumber() && rhs.IsNumber()) + { + if (IsInt()) + { + return GetInt() == rhs.GetInt(); + } + else if (IsUint()) + { + return GetUint() == rhs.GetUint(); + } + else + { + return GetDouble() == rhs.GetDouble(); + } + } + else + { + return m_value == rhs.m_value; + } + } + + bool Value::operator!=(const Value& rhs) const + { + return !operator==(rhs); + } + + void Value::Swap(Value& other) noexcept + { + AZStd::swap(m_value, other.m_value); + } + + Type Dom::Value::GetType() const + { + switch (m_value.index()) + { + case 0: // AZStd::monostate + return Type::NullType; + case 1: // int64_t + case 2: // uint64_t + case 3: // double + return Type::NumberType; + case 4: // bool + return AZStd::get(m_value) ? Type::TrueType : Type::FalseType; + case 5: // AZStd::string_view + case 6: // AZStd::shared_ptr + return Type::StringType; + case 7: // ObjectPtr + return Type::ObjectType; + case 8: // ArrayPtr + return Type::ArrayType; + case 9: // Node + return Type::NodeType; + case 10: // AZStd::any* + return Type::OpaqueType; + } + AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); + return Type::NullType; + } + + bool Value::IsNull() const + { + return GetType() == Type::NullType; + } + + bool Value::IsFalse() const + { + return GetType() == Type::FalseType; + } + + bool Value::IsTrue() const + { + return GetType() == Type::TrueType; + } + + bool Value::IsBool() const + { + return AZStd::holds_alternative(m_value); + } + + bool Value::IsNode() const + { + return GetType() == Type::NodeType; + } + + bool Value::IsObject() const + { + return GetType() == Type::ObjectType; + } + + bool Value::IsArray() const + { + return GetType() == Type::ArrayType; + } + + bool Value::IsOpaqueValue() const + { + return GetType() == Type::OpaqueType; + } + + bool Value::IsNumber() const + { + return GetType() == Type::NumberType; + } + + bool Value::IsInt() const + { + return AZStd::holds_alternative(m_value); + } + + bool Value::IsUint() const + { + return AZStd::holds_alternative(m_value); + } + + bool Value::IsDouble() const + { + return AZStd::holds_alternative(m_value); + } + + bool Value::IsString() const + { + return GetType() == Type::StringType; + } + + Value& Value::SetObject() + { + m_value = AZStd::make_shared(); + return *this; + } + + const Object::ContainerType& Value::GetObjectInternal() const + { + const Type type = GetType(); + AZ_Assert( + type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value"); + if (type == Type::ObjectType) + { + return AZStd::get(m_value)->m_values; + } + else + { + return AZStd::get(m_value).GetProperties()->m_values; + } + } + + Object::ContainerType& Value::GetObjectInternal() + { + const Type type = GetType(); + AZ_Assert( + type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value"); + if (type == Type::ObjectType) + { + return CheckCopyOnWrite(AZStd::get(m_value))->m_values; + } + else + { + return AZStd::get(m_value).GetMutableProperties()->m_values; + } + } + + const Array::ContainerType& Value::GetArrayInternal() const + { + const Type type = GetType(); + AZ_Assert( + type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value"); + if (type == Type::ObjectType) + { + return AZStd::get(m_value)->m_values; + } + else + { + return AZStd::get(m_value).GetChildren()->m_values; + } + } + + Array::ContainerType& Value::GetArrayInternal() + { + const Type type = GetType(); + AZ_Assert( + type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value"); + if (type == Type::ObjectType) + { + return CheckCopyOnWrite(AZStd::get(m_value))->m_values; + } + else + { + return AZStd::get(m_value).GetMutableChildren()->m_values; + } + } + + size_t Value::MemberCount() const + { + return GetObjectInternal().size(); + } + + size_t Value::MemberCapacity() const + { + return GetObjectInternal().size(); + } + + bool Value::ObjectEmpty() const + { + return GetObjectInternal().empty(); + } + + Value& Value::operator[](KeyType name) + { + return FindMember(name)->second; + } + + const Value& Value::operator[](KeyType name) const + { + return FindMember(name)->second; + } + + Value& Value::operator[](AZStd::string_view name) + { + return operator[](AZ::Name(name)); + } + + const Value& Value::operator[](AZStd::string_view name) const + { + return operator[](AZ::Name(name)); + } + + Object::ConstIterator Value::MemberBegin() const + { + return GetObjectInternal().begin(); + } + + Object::ConstIterator Value::MemberEnd() const + { + return GetObjectInternal().end(); + } + + Object::Iterator Value::MemberBegin() + { + return GetObjectInternal().begin(); + } + + Object::Iterator Value::MemberEnd() + { + return GetObjectInternal().end(); + } + + Object::ConstIterator Value::FindMember(KeyType name) const + { + const Object::ContainerType& object = GetObjectInternal(); + return AZStd::find_if( + object.begin(), object.end(), + [&name](const Object::EntryType& entry) + { + return entry.first == name; + }); + } + + Object::ConstIterator Value::FindMember(AZStd::string_view name) const + { + return FindMember(AZ::Name(name)); + } + + Object::Iterator Value::FindMember(KeyType name) + { + Object::ContainerType& object = GetObjectInternal(); + return AZStd::find_if( + object.begin(), object.end(), + [&name](const Object::EntryType& entry) + { + return entry.first == name; + }); + } + + Object::Iterator Value::FindMember(AZStd::string_view name) + { + return FindMember(AZ::Name(name)); + } + + Value& Value::MemberReserve(size_t newCapacity) + { + GetObjectInternal().reserve(newCapacity); + return *this; + } + + bool Value::HasMember(KeyType name) const + { + return FindMember(name) != GetObjectInternal().end(); + } + + bool Value::HasMember(AZStd::string_view name) const + { + return HasMember(AZ::Name(name)); + } + + Value& Value::AddMember(KeyType name, const Value& value) + { + Object::ContainerType& object = GetObjectInternal(); + if (auto memberIt = FindMember(name); memberIt != object.end()) + { + memberIt->second = value; + } + else + { + object.emplace_back(name, value); + } + return *this; + } + + Value& Value::AddMember(AZStd::string_view name, const Value& value) + { + return AddMember(AZ::Name(name), value); + } + + Value& Value::AddMember(AZ::Name name, Value&& value) + { + Object::ContainerType& object = GetObjectInternal(); + if (auto memberIt = FindMember(name); memberIt != object.end()) + { + memberIt->second = value; + } + else + { + object.emplace_back(name, value); + } + return *this; + } + + Value& Value::AddMember(AZStd::string_view name, Value&& value) + { + return AddMember(AZ::Name(name), value); + } + + void Value::RemoveAllMembers() + { + GetObjectInternal().clear(); + } + + void Value::RemoveMember(KeyType name) + { + Object::ContainerType& object = GetObjectInternal(); + object.erase(AZStd::remove_if( + object.begin(), object.end(), + [&name](const Object::EntryType& entry) + { + return entry.first == name; + })); + } + + void Value::RemoveMember(AZStd::string_view name) + { + RemoveMember(AZ::Name(name)); + } + + Object::Iterator Value::RemoveMember(Object::Iterator pos) + { + Object::ContainerType& object = GetObjectInternal(); + Object::Iterator nextIndex = object.end(); + auto lastEntry = object.end() - 1; + if (pos != lastEntry) + { + AZStd::swap(*pos, *lastEntry); + nextIndex = pos; + } + object.resize(object.size() - 1); + return nextIndex; + } + + Object::Iterator Value::EraseMember(Object::ConstIterator pos) + { + return GetObjectInternal().erase(pos); + } + + Object::Iterator Value::EraseMember(Object::ConstIterator first, Object::ConstIterator last) + { + return GetObjectInternal().erase(first, last); + } + + Object::Iterator Value::EraseMember(KeyType name) + { + return GetObjectInternal().erase(FindMember(name)); + } + + Object::Iterator Value::EraseMember(AZStd::string_view name) + { + return EraseMember(AZ::Name(name)); + } + + Object::ContainerType& Value::GetObject() + { + return GetObjectInternal(); + } + + const Object::ContainerType& Value::GetObject() const + { + return GetObjectInternal(); + } + + Value& Value::SetArray() + { + m_value = AZStd::make_shared(); + return *this; + } + + size_t Value::Size() const + { + return GetArrayInternal().size(); + } + + size_t Value::Capacity() const + { + return GetArrayInternal().capacity(); + } + + bool Value::Empty() const + { + return GetArrayInternal().empty(); + } + + void Value::Clear() + { + GetArrayInternal().clear(); + } + + Value& Value::operator[](size_t index) + { + return GetArrayInternal()[index]; + } + + const Value& Value::operator[](size_t index) const + { + return GetArrayInternal()[index]; + } + + Array::ConstIterator Value::Begin() const + { + return GetArrayInternal().begin(); + } + + Array::ConstIterator Value::End() const + { + return GetArrayInternal().end(); + } + + Array::Iterator Value::Begin() + { + return GetArrayInternal().begin(); + } + + Array::Iterator Value::End() + { + return GetArrayInternal().end(); + } + + Value& Value::Reserve(size_t newCapacity) + { + GetArrayInternal().reserve(newCapacity); + return *this; + } + + Value& Value::PushBack(Value value) + { + GetArrayInternal().push_back(AZStd::move(value)); + return *this; + } + + Value& Value::PopBack() + { + GetArrayInternal().pop_back(); + return *this; + } + + Array::Iterator Value::Erase(Array::ConstIterator pos) + { + return GetArrayInternal().erase(pos); + } + + Array::Iterator Value::Erase(Array::ConstIterator first, Array::ConstIterator last) + { + return GetArrayInternal().erase(first, last); + } + + Array::ContainerType& Value::GetArray() + { + return GetArrayInternal(); + } + + const Array::ContainerType& Value::GetArray() const + { + return GetArrayInternal(); + } + + int64_t Value::GetInt() const + { + switch (m_value.index()) + { + case 1: // int64_t + return AZStd::get(m_value); + case 2: // uint64_t + return aznumeric_cast(AZStd::get(m_value)); + case 3: // double + return aznumeric_cast(AZStd::get(m_value)); + } + AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); + return {}; + } + + void Value::SetInt(int64_t value) + { + m_value = value; + } + + uint64_t Value::GetUint() const + { + switch (m_value.index()) + { + case 1: // int64_t + return aznumeric_cast(AZStd::get(m_value)); + case 2: // uint64_t + return AZStd::get(m_value); + case 3: // double + return aznumeric_cast(AZStd::get(m_value)); + } + AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); + return {}; + } + + void Value::SetUint(uint64_t value) + { + m_value = value; + } + + bool Value::GetBool() const + { + if (IsBool()) + { + return AZStd::get(m_value); + } + AZ_Assert(false, "AZ::Dom::Value: Called GetBool on a non-bool type"); + return {}; + } + + void Value::SetBool(bool value) + { + m_value = value; + } + + double Value::GetDouble() const + { + switch (m_value.index()) + { + case 1: // int64_t + return aznumeric_cast(AZStd::get(m_value)); + case 2: // uint64_t + return aznumeric_cast(AZStd::get(m_value)); + case 3: // double + return AZStd::get(m_value); + } + AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); + return {}; + } + + void Value::SetDouble(double value) + { + m_value = value; + } + + AZStd::string_view Value::GetString() const + { + switch (m_value.index()) + { + case 5: // AZStd::string_view + return AZStd::get(m_value); + case 6: // AZStd::shared_ptr + return *AZStd::get>(m_value); + } + AZ_Assert(false, "AZ::Dom::Value: Called GetString on a non-string type"); + return {}; + } + + size_t Value::GetStringLength() const + { + return GetString().size(); + } + + void Value::SetString(AZStd::string_view value) + { + m_value = value; + } + + void Value::CopyFromString(AZStd::string_view value) + { + m_value = AZStd::make_shared(value); + } + + AZStd::any& Value::GetOpaqueValue() const + { + return *AZStd::get(m_value); + } + + void Value::SetOpaqueValue(AZStd::any& value) + { + m_value = &value; + } + + void Value::SetNull() + { + m_value = AZStd::monostate(); + } +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h new file mode 100644 index 0000000000..f73dbb2549 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -0,0 +1,289 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +namespace AZ::Dom +{ + using KeyType = AZ::Name; + + enum class Type + { + NullType = 0, + FalseType = 1, + TrueType = 2, + ObjectType = 3, + ArrayType = 4, + StringType = 5, + NumberType = 6, + NodeType = 7, + OpaqueType = 8, + }; + + class Value; + + class Array + { + public: + using ContainerType = AZStd::vector; + using Iterator = ContainerType::iterator; + using ConstIterator = ContainerType::const_iterator; + + private: + ContainerType m_values; + + friend class Value; + }; + + using ArrayPtr = AZStd::shared_ptr; + using ConstArrayPtr = AZStd::shared_ptr; + + class Object + { + public: + using EntryType = AZStd::pair; + using ContainerType = AZStd::vector; + using Iterator = ContainerType::iterator; + using ConstIterator = ContainerType::const_iterator; + + private: + ContainerType m_values; + + friend class Value; + }; + + using ObjectPtr = AZStd::shared_ptr; + using ConstObjectPtr = AZStd::shared_ptr; + + class Node + { + public: + AZ::Name GetName() const; + void SetName(AZ::Name name); + + ObjectPtr GetMutableProperties(); + ConstObjectPtr GetProperties() const; + + ArrayPtr GetMutableChildren(); + ConstArrayPtr GetChildren() const; + + bool operator==(const Node& rhs) const; + + private: + AZ::Name m_name; + ArrayPtr m_array; + ObjectPtr m_object; + + friend class Value; + }; + + class Value + { + public: + // Constructors... + Value(); + Value(const Value&); + Value(Value&&) noexcept; + Value(AZStd::string_view string, bool copy); + + explicit Value(int64_t value); + explicit Value(uint64_t value); + explicit Value(double value); + explicit Value(bool value); + + static Value FromOpaqueValue(AZStd::any& value); + + // Equality / comparison / swap... + Value& operator=(const Value&); + Value& operator=(Value&&) noexcept; + + bool operator==(const Value& rhs) const; + bool operator!=(const Value& rhs) const; + + void Swap(Value& other) noexcept; + + // Type info... + Type GetType() const; + bool IsNull() const; + bool IsFalse() const; + bool IsTrue() const; + bool IsBool() const; + bool IsNode() const; + bool IsObject() const; + bool IsArray() const; + bool IsOpaqueValue() const; + bool IsNumber() const; + bool IsInt() const; + bool IsUint() const; + bool IsDouble() const; + bool IsString() const; + + // Object API (also used by Node)... + Value& SetObject(); + size_t MemberCount() const; + size_t MemberCapacity() const; + bool ObjectEmpty() const; + + Value& operator[](KeyType name); + const Value& operator[](KeyType name) const; + Value& operator[](AZStd::string_view name); + const Value& operator[](AZStd::string_view name) const; + + Object::ConstIterator MemberBegin() const; + Object::ConstIterator MemberEnd() const; + Object::Iterator MemberBegin(); + Object::Iterator MemberEnd(); + + Object::ConstIterator FindMember(KeyType name) const; + Object::ConstIterator FindMember(AZStd::string_view name) const; + Object::Iterator FindMember(KeyType name); + Object::Iterator FindMember(AZStd::string_view name); + + Value& MemberReserve(size_t newCapacity); + bool HasMember(KeyType name) const; + bool HasMember(AZStd::string_view name) const; + + Value& AddMember(KeyType name, const Value& value); + Value& AddMember(AZStd::string_view name, const Value& value); + Value& AddMember(KeyType name, Value&& value); + Value& AddMember(AZStd::string_view name, Value&& value); + + void RemoveAllMembers(); + void RemoveMember(KeyType name); + void RemoveMember(AZStd::string_view name); + Object::Iterator RemoveMember(Object::Iterator pos); + Object::Iterator EraseMember(Object::ConstIterator pos); + Object::Iterator EraseMember(Object::ConstIterator first, Object::ConstIterator last); + Object::Iterator EraseMember(KeyType name); + Object::Iterator EraseMember(AZStd::string_view name); + + Object::ContainerType& GetObject(); + const Object::ContainerType& GetObject() const; + + // Array API (also used by Node)... + Value& SetArray(); + + size_t Size() const; + size_t Capacity() const; + bool Empty() const; + void Clear(); + + Value& operator[](size_t index); + const Value& operator[](size_t index) const; + + Array::ConstIterator Begin() const; + Array::ConstIterator End() const; + Array::Iterator Begin(); + Array::Iterator End(); + + Value& Reserve(size_t newCapacity); + Value& PushBack(Value value); + Value& PopBack(); + + Array::Iterator Erase(Array::ConstIterator pos); + Array::Iterator Erase(Array::ConstIterator first, Array::ConstIterator last); + + Array::ContainerType& GetArray(); + const Array::ContainerType& GetArray() const; + + // Node API (supports both object + array API, plus a dedicated NodeName)... + // bool CanConvertToNodeFromObject() const; + // Value& ConvertToNodeFromObject(); + // Value& ConvertToObjectFromNode(); + + void SetNode(AZ::Name name); + void SetNode(AZStd::string_view name); + + AZ::Name GetNodeName() const; + void SetNodeName(AZ::Name name); + void SetNodeName(AZStd::string_view name); + //! Convenience method, sets the first non-node element of a Node. + void SetNodeValue(Value value); + //! Convenience method, gets the first non-node element of a Node. + Value GetNodeValue() const; + + // int API... + int64_t GetInt() const; + void SetInt(int64_t); + + // uint API... + uint64_t GetUint() const; + void SetUint(uint64_t); + + // bool API... + bool GetBool() const; + void SetBool(bool); + + // double API... + double GetDouble() const; + void SetDouble(double); + + // string API... + AZStd::string_view GetString() const; + size_t GetStringLength() const; + void SetString(AZStd::string_view); + void CopyFromString(AZStd::string_view); + + // opaque type API... + AZStd::any& GetOpaqueValue() const; + //! This sets this Value to represent a value of an type that the DOM has + //! no formal knowledge of. Where possible, it should be preferred to + //! serialize an opaque type into a DOM value instead, as serializers + //! and other systems will have no means of dealing with fully arbitrary + //! values. + void SetOpaqueValue(AZStd::any&); + + // null API... + void SetNull(); + + private: + const Object::ContainerType& GetObjectInternal() const; + Object::ContainerType& GetObjectInternal(); + const Array::ContainerType& GetArrayInternal() const; + Array::ContainerType& GetArrayInternal(); + + explicit Value(AZStd::any* opaqueValue); + + // If using the the copy on write model, anything stored internally as a shared_ptr will + // detach and copy when doing a mutating operation if use_count() > 1. + + // This internal storage will not have a 1:1 mapping to the public Type, as there may be + // multiple storage options (e.g. strings being stored as non-owning string_view or + // owning shared_ptr) + using ValueType = AZStd::variant< + // NullType + AZStd::monostate, + // NumberType + int64_t, + uint64_t, + double, + // FalseType & TrueType + bool, + // StringType + AZStd::string_view, + AZStd::shared_ptr, + // ObjectType + ObjectPtr, + // ArrayType + ArrayPtr, + // NodeType + Node, + // OpaqueType + AZStd::any*>; + + ValueType m_value; + }; +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index ef6c7434cc..fa9f94505f 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -127,8 +127,12 @@ set(FILES Debug/TraceReflection.h DOM/DomBackend.cpp DOM/DomBackend.h + DOM/DomDocument.cpp + DOM/DomDocument.h DOM/DomUtils.cpp DOM/DomUtils.h + DOM/DomValue.cpp + DOM/DomValue.h DOM/DomVisitor.cpp DOM/DomVisitor.h DOM/Backends/JSON/JsonBackend.h From fada7ace232457b578730be5870df61fb9c7cfa6 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 8 Dec 2021 22:22:18 -0800 Subject: [PATCH 02/49] Add visitor support to Value Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp | 2 +- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 306 ++++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomValue.h | 49 ++- 3 files changed, 355 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp index 9751b9cecc..2e78661518 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp @@ -21,4 +21,4 @@ namespace AZ::Dom::Utils { return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor); } -} +} // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index ad6e648620..032e5795da 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -744,4 +744,310 @@ namespace AZ::Dom { m_value = AZStd::monostate(); } + + Visitor::Result Value::Accept(Visitor& visitor, bool copyStrings) const + { + Visitor::Result result = AZ::Success(); + + AZStd::visit( + [&](auto&& arg) + { + using Alternative = AZStd::decay_t; + + if constexpr (AZStd::is_same_v) + { + result = visitor.Null(); + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.Int64(arg); + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.Uint64(arg); + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.Double(arg); + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); + } + else if constexpr (AZStd::is_same_v>) + { + result = visitor.String(*arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.StartObject(); + if (result.IsSuccess()) + { + const Object::ContainerType& object = GetObjectInternal(); + for (const Object::EntryType& entry : object) + { + result = entry.second.Accept(visitor, copyStrings); + if (!result.IsSuccess()) + { + return; + } + } + result = visitor.EndObject(object.size()); + } + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.StartArray(); + if (result.IsSuccess()) + { + const Array::ContainerType& arrayContainer = GetArrayInternal(); + for (const Value& entry : arrayContainer) + { + result = entry.Accept(visitor, copyStrings); + if (!result.IsSuccess()) + { + return; + } + } + result = visitor.EndArray(arrayContainer.size()); + } + } + else if constexpr (AZStd::is_same_v) + { + const Node& node = AZStd::get(m_value); + result = visitor.StartNode(node.GetName()); + if (result.IsSuccess()) + { + const Object::ContainerType& object = GetObjectInternal(); + for (const Object::EntryType& entry : object) + { + result = entry.second.Accept(visitor, copyStrings); + if (!result.IsSuccess()) + { + return; + } + } + + const Array::ContainerType& arrayContainer = GetArrayInternal(); + for (const Value& entry : arrayContainer) + { + result = entry.Accept(visitor, copyStrings); + if (!result.IsSuccess()) + { + return; + } + } + + result = visitor.EndNode(object.size(), arrayContainer.size()); + } + } + else if constexpr (AZStd::is_same_v) + { + result = visitor.OpaqueValue(*arg); + } + }, + m_value); + return result; + } + + ValueWriter::ValueWriter(Value& outputValue) + : m_result(outputValue) + { + } + + VisitorFlags ValueWriter::GetVisitorFlags() const + { + return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes; + } + + ValueWriter::ValueInfo::ValueInfo(Value& container) + : m_container(container) + { + } + + Visitor::Result ValueWriter::Null() + { + CurrentValue().SetNull(); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Bool(bool value) + { + CurrentValue().SetBool(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Int64(AZ::s64 value) + { + CurrentValue().SetInt(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Uint64(AZ::u64 value) + { + CurrentValue().SetUint(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Double(double value) + { + CurrentValue().SetDouble(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::String(AZStd::string_view value, Lifetime lifetime) + { + if (lifetime == Lifetime::Persistent) + { + CurrentValue().SetString(value); + } + else + { + CurrentValue().CopyFromString(value); + } + return FinishWrite(); + } + + Visitor::Result ValueWriter::StartObject() + { + CurrentValue().SetObject(); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount) + { + const char* endMethodName; + switch (containerType) + { + case Type::ObjectType: + endMethodName = "EndObject"; + break; + case Type::ArrayType: + endMethodName = "EndArray"; + break; + case Type::NodeType: + endMethodName = "EndNode"; + break; + default: + AZ_Assert(false, "Invalid container type specified"); + return VisitorFailure(VisitorErrorCode::InternalError, "AZ::Dom::ValueWriter: EndContainer called with invalid container type"); + } + + if (m_entryStack.empty()) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format("AZ::Dom::ValueWriter: %s called without a matching call", endMethodName)); + } + + const ValueInfo& topEntry = m_entryStack.top(); + if (topEntry.m_container.GetType() != containerType) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); + } + + if (topEntry.m_attributeCount != attributeCount) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format( + "AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount, + topEntry.m_attributeCount)); + } + + if (topEntry.m_elementCount != elementCount) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format( + "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, + topEntry.m_elementCount)); + } + + m_entryStack.pop(); + return FinishWrite(); + } + + Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount) + { + return EndContainer(Type::ObjectType, attributeCount, 0); + } + + Visitor::Result ValueWriter::Key(AZ::Name key) + { + AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); + AZ_Assert(!m_entryStack.top().m_container.IsArray(), "Attempted to push a key to an array"); + m_entryStack.top().m_key = key; + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime) + { + return Key(AZ::Name(key)); + } + + Visitor::Result ValueWriter::StartArray() + { + CurrentValue().SetArray(); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::EndArray(AZ::u64 elementCount) + { + return EndContainer(Type::ArrayType, 0, elementCount); + } + + Visitor::Result ValueWriter::StartNode(AZ::Name name) + { + CurrentValue().SetNode(name); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) + { + return EndContainer(Type::NodeType, attributeCount, elementCount); + } + + Visitor::Result ValueWriter::FinishWrite() + { + if (m_entryStack.empty()) + { + return VisitorSuccess(); + } + + Value value; + m_entryStack.top().m_value.Swap(value); + ValueInfo& newEntry = m_entryStack.top(); + + if (!newEntry.m_key.IsEmpty()) + { + newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value)); + newEntry.m_key = AZ::Name(); + ++newEntry.m_attributeCount; + } + else + { + newEntry.m_container.PushBack(AZStd::move(value)); + ++newEntry.m_elementCount; + } + + return VisitorSuccess(); + } + + Value& ValueWriter::CurrentValue() + { + if (m_entryStack.empty()) + { + return m_result; + } + return m_entryStack.top().m_value; + } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index f73dbb2549..439971a5a0 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace AZ::Dom @@ -249,6 +250,9 @@ namespace AZ::Dom // null API... void SetNull(); + // Visitor API + Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; + private: const Object::ContainerType& GetObjectInternal() const; Object::ContainerType& GetObjectInternal(); @@ -286,4 +290,47 @@ namespace AZ::Dom ValueType m_value; }; -} // namespace AZ::Dom + + class ValueWriter : public Visitor + { + public: + ValueWriter(Value& outputValue); + + VisitorFlags GetVisitorFlags() const override; + Result Null() override; + Result Bool(bool value) override; + Result Int64(AZ::s64 value) override; + Result Uint64(AZ::u64 value) override; + Result Double(double value) override; + + Result String(AZStd::string_view value, Lifetime lifetime) override; + Result StartObject() override; + Result EndObject(AZ::u64 attributeCount) override; + Result Key(AZ::Name key) override; + Result RawKey(AZStd::string_view key, Lifetime lifetime) override; + Result StartArray() override; + Result EndArray(AZ::u64 elementCount) override; + Result StartNode(AZ::Name name) override; + Result RawStartNode(AZStd::string_view name, Lifetime lifetime) override; + Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) override; + + private: + Result FinishWrite(); + Value& CurrentValue(); + Visitor::Result EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount); + + struct ValueInfo + { + ValueInfo(Value& container); + + KeyType m_key; + Value m_value; + Value& m_container; + AZ::u64 m_attributeCount = 0; + AZ::u64 m_elementCount = 0; + }; + + Value& m_result; + AZStd::stack m_entryStack; + }; + } // namespace AZ::Dom From e7c92b5658a0829885a729039356fbad1bf939bb Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 9 Dec 2021 15:25:20 -0800 Subject: [PATCH 03/49] Move ValueWriter out, add DeepCompareIsEqual Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 405 +++++++++--------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 81 ++-- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 218 ++++++++++ .../AzCore/AzCore/DOM/DomValueWriter.h | 58 +++ .../AzCore/AzCore/azcore_files.cmake | 2 + 5 files changed, 504 insertions(+), 260 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 032e5795da..68a88ed35a 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -9,7 +9,7 @@ #pragma once #include - +#include #include namespace AZ::Dom @@ -26,6 +26,11 @@ namespace AZ::Dom return refCountedPointer; } + Node::Node(AZ::Name name) + : m_name(name) + { + } + AZ::Name Node::GetName() const { return m_name; @@ -36,31 +41,24 @@ namespace AZ::Dom m_name = name; } - ObjectPtr Node::GetMutableProperties() + Object::ContainerType& Node::GetProperties() { - CheckCopyOnWrite(m_object); - return m_object; + return m_properties; } - ConstObjectPtr Node::GetProperties() const + const Object::ContainerType& Node::GetProperties() const { - return m_object; + return m_properties; } - ArrayPtr Node::GetMutableChildren() + Array::ContainerType& Node::GetChildren() { - CheckCopyOnWrite(m_array); - return m_array; + return m_children; } - ConstArrayPtr Node::GetChildren() const + const Array::ContainerType& Node::GetChildren() const { - return m_array; - } - - bool Node::operator==(const Node& rhs) const - { - return m_name == rhs.m_name && m_array == rhs.m_array && m_object == rhs.m_object; + return m_children; } Value::Value() @@ -187,7 +185,7 @@ namespace AZ::Dom return Type::ObjectType; case 8: // ArrayPtr return Type::ArrayType; - case 9: // Node + case 9: // NodePtr return Type::NodeType; case 10: // AZStd::any* return Type::OpaqueType; @@ -267,18 +265,31 @@ namespace AZ::Dom return *this; } + const Node& Value::GetNodeInternal() const + { + AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); + return *AZStd::get(m_value); + } + + Node& Value::GetNodeInternal() + { + AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); + return *CheckCopyOnWrite(AZStd::get(m_value)); + } + const Object::ContainerType& Value::GetObjectInternal() const { const Type type = GetType(); AZ_Assert( - type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value"); + type == Type::ObjectType || type == Type::NodeType, + "AZ::Dom::Value: attempted to retrieve an object from a value that isn't an object or a node"); if (type == Type::ObjectType) { return AZStd::get(m_value)->m_values; } else { - return AZStd::get(m_value).GetProperties()->m_values; + return AZStd::get(m_value)->GetProperties(); } } @@ -286,14 +297,15 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ObjectType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an object from a non-object value"); + type == Type::ObjectType || type == Type::NodeType, + "AZ::Dom::Value: attempted to retrieve an object from a value that isn't an object or a node"); if (type == Type::ObjectType) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } else { - return AZStd::get(m_value).GetMutableProperties()->m_values; + return CheckCopyOnWrite(AZStd::get(m_value))->GetProperties(); } } @@ -301,14 +313,15 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value"); + type == Type::ArrayType || type == Type::NodeType, + "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or a node"); if (type == Type::ObjectType) { return AZStd::get(m_value)->m_values; } else { - return AZStd::get(m_value).GetChildren()->m_values; + return AZStd::get(m_value)->GetChildren(); } } @@ -316,14 +329,15 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a non-array value"); + type == Type::ArrayType || type == Type::NodeType, + "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or node"); if (type == Type::ObjectType) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } else { - return AZStd::get(m_value).GetMutableChildren()->m_values; + return CheckCopyOnWrite(AZStd::get(m_value))->GetChildren(); } } @@ -627,6 +641,67 @@ namespace AZ::Dom return GetArrayInternal(); } + void Value::SetNode(AZ::Name name) + { + m_value = AZStd::make_shared(name); + } + + void Value::SetNode(AZStd::string_view name) + { + SetNode(AZ::Name(name)); + } + + AZ::Name Value::GetNodeName() const + { + return GetNodeInternal().GetName(); + } + + void Value::SetNodeName(AZ::Name name) + { + GetNodeInternal().SetName(name); + } + + void Value::SetNodeName(AZStd::string_view name) + { + SetNodeName(AZ::Name(name)); + } + + void Value::SetNodeValue(Value value) + { + AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: Attempted to set value for non-node type"); + Array::ContainerType& nodeChildren = GetArrayInternal(); + + // Set the first non-node child, if one is found + for (Value& entry : nodeChildren) + { + if (entry.GetType() != Type::NodeType) + { + entry = AZStd::move(value); + return; + } + } + + // Otherwise, append the value entry + nodeChildren.push_back(AZStd::move(value)); + } + + Value Value::GetNodeValue() const + { + AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: Attempted to get value for non-node type"); + const Array::ContainerType& nodeChildren = GetArrayInternal(); + + // Get the first non-node child, if one is found + for (const Value& entry : nodeChildren) + { + if (entry.GetType() != Type::NodeType) + { + return entry; + } + } + + return Value(); + } + int64_t Value::GetInt() const { switch (m_value.index()) @@ -812,9 +887,9 @@ namespace AZ::Dom result = visitor.EndArray(arrayContainer.size()); } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { - const Node& node = AZStd::get(m_value); + const Node& node = *AZStd::get(m_value); result = visitor.StartNode(node.GetName()); if (result.IsSuccess()) { @@ -850,204 +925,124 @@ namespace AZ::Dom return result; } - ValueWriter::ValueWriter(Value& outputValue) - : m_result(outputValue) + AZStd::unique_ptr Value::GetWriteHandler() { + return AZStd::make_unique(*this); } - VisitorFlags ValueWriter::GetVisitorFlags() const + bool Value::DeepCompareIsEqual(const Value& other) const { - return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes; - } - - ValueWriter::ValueInfo::ValueInfo(Value& container) - : m_container(container) - { - } - - Visitor::Result ValueWriter::Null() - { - CurrentValue().SetNull(); - return FinishWrite(); - } - - Visitor::Result ValueWriter::Bool(bool value) - { - CurrentValue().SetBool(value); - return FinishWrite(); - } - - Visitor::Result ValueWriter::Int64(AZ::s64 value) - { - CurrentValue().SetInt(value); - return FinishWrite(); - } - - Visitor::Result ValueWriter::Uint64(AZ::u64 value) - { - CurrentValue().SetUint(value); - return FinishWrite(); - } - - Visitor::Result ValueWriter::Double(double value) - { - CurrentValue().SetDouble(value); - return FinishWrite(); - } - - Visitor::Result ValueWriter::String(AZStd::string_view value, Lifetime lifetime) - { - if (lifetime == Lifetime::Persistent) + if (m_value.index() != other.m_value.index()) { - CurrentValue().SetString(value); - } - else - { - CurrentValue().CopyFromString(value); - } - return FinishWrite(); - } - - Visitor::Result ValueWriter::StartObject() - { - CurrentValue().SetObject(); - - m_entryStack.emplace(CurrentValue()); - return VisitorSuccess(); - } - - Visitor::Result ValueWriter::EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount) - { - const char* endMethodName; - switch (containerType) - { - case Type::ObjectType: - endMethodName = "EndObject"; - break; - case Type::ArrayType: - endMethodName = "EndArray"; - break; - case Type::NodeType: - endMethodName = "EndNode"; - break; - default: - AZ_Assert(false, "Invalid container type specified"); - return VisitorFailure(VisitorErrorCode::InternalError, "AZ::Dom::ValueWriter: EndContainer called with invalid container type"); + return false; } - if (m_entryStack.empty()) - { - return VisitorFailure( - VisitorErrorCode::InternalError, - AZStd::string::format("AZ::Dom::ValueWriter: %s called without a matching call", endMethodName)); - } + return AZStd::visit( + [&](auto&& ourValue) -> bool + { + using Alternative = AZStd::decay_t; + auto&& theirValue = AZStd::get>(other.m_value); - const ValueInfo& topEntry = m_entryStack.top(); - if (topEntry.m_container.GetType() != containerType) - { - return VisitorFailure( - VisitorErrorCode::InternalError, - AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); - } + if constexpr (AZStd::is_same_v) + { + return true; + } + else if constexpr (AZStd::is_same_v) + { + if (ourValue == theirValue) + { + return true; + } - if (topEntry.m_attributeCount != attributeCount) - { - return VisitorFailure( - VisitorErrorCode::InternalError, - AZStd::string::format( - "AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount, - topEntry.m_attributeCount)); - } + if (ourValue->m_values.size() != theirValue->m_values.size()) + { + return false; + } - if (topEntry.m_elementCount != elementCount) - { - return VisitorFailure( - VisitorErrorCode::InternalError, - AZStd::string::format( - "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, - topEntry.m_elementCount)); - } + for (size_t i = 0; i < ourValue->m_values.size(); ++i) + { + const Object::EntryType& lhs = ourValue->m_values[i]; + const Object::EntryType& rhs = theirValue->m_values[i]; + if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second)) + { + return false; + } + } - m_entryStack.pop(); - return FinishWrite(); - } + return true; + } + else if constexpr (AZStd::is_same_v) + { + if (ourValue == theirValue) + { + return true; + } - Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount) - { - return EndContainer(Type::ObjectType, attributeCount, 0); - } + if (ourValue->m_values.size() != theirValue->m_values.size()) + { + return false; + } - Visitor::Result ValueWriter::Key(AZ::Name key) - { - AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); - AZ_Assert(!m_entryStack.top().m_container.IsArray(), "Attempted to push a key to an array"); - m_entryStack.top().m_key = key; - return VisitorSuccess(); - } + for (size_t i = 0; i < ourValue->m_values.size(); ++i) + { + const Value& lhs = ourValue->m_values[i]; + const Value& rhs = theirValue->m_values[i]; + if (!lhs.DeepCompareIsEqual(rhs)) + { + return false; + } + } - Visitor::Result ValueWriter::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime) - { - return Key(AZ::Name(key)); - } + return true; + } + else if constexpr (AZStd::is_same_v) + { + if (ourValue == theirValue) + { + return true; + } - Visitor::Result ValueWriter::StartArray() - { - CurrentValue().SetArray(); + const Node& ourNode = *ourValue; + const Node& theirNode = *theirValue; - m_entryStack.emplace(CurrentValue()); - return VisitorSuccess(); - } + const Object::ContainerType& ourProperties = ourNode.GetProperties(); + const Object::ContainerType& theirProperties = theirNode.GetProperties(); - Visitor::Result ValueWriter::EndArray(AZ::u64 elementCount) - { - return EndContainer(Type::ArrayType, 0, elementCount); - } + if (ourProperties.size() != theirProperties.size()) + { + return false; + } - Visitor::Result ValueWriter::StartNode(AZ::Name name) - { - CurrentValue().SetNode(name); + for (size_t i = 0; i < ourProperties.size(); ++i) + { + const Object::EntryType& lhs = ourProperties[i]; + const Object::EntryType& rhs = theirProperties[i]; + if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second)) + { + return false; + } + } - m_entryStack.emplace(CurrentValue()); - return VisitorSuccess(); - } + const Array::ContainerType& ourChildren = ourNode.GetChildren(); + const Array::ContainerType& theirChildren = theirNode.GetChildren(); - Visitor::Result ValueWriter::EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) - { - return EndContainer(Type::NodeType, attributeCount, elementCount); - } + for (size_t i = 0; i < ourChildren.size(); ++i) + { + const Value& lhs = ourChildren[i]; + const Value& rhs = theirChildren[i]; + if (!lhs.DeepCompareIsEqual(rhs)) + { + return false; + } + } - Visitor::Result ValueWriter::FinishWrite() - { - if (m_entryStack.empty()) - { - return VisitorSuccess(); - } - - Value value; - m_entryStack.top().m_value.Swap(value); - ValueInfo& newEntry = m_entryStack.top(); - - if (!newEntry.m_key.IsEmpty()) - { - newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value)); - newEntry.m_key = AZ::Name(); - ++newEntry.m_attributeCount; - } - else - { - newEntry.m_container.PushBack(AZStd::move(value)); - ++newEntry.m_elementCount; - } - - return VisitorSuccess(); - } - - Value& ValueWriter::CurrentValue() - { - if (m_entryStack.empty()) - { - return m_result; - } - return m_entryStack.top().m_value; + return true; + } + else + { + return ourValue == theirValue; + } + }, + m_value); } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 439971a5a0..a460274602 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -8,13 +8,13 @@ #pragma once +#include #include - #include +#include #include #include #include -#include #include namespace AZ::Dom @@ -72,25 +72,34 @@ namespace AZ::Dom class Node { public: + Node() = default; + Node(AZ::Name name); + Node(const Node&) = default; + Node(Node&&) = default; + + Node& operator=(const Node&) = default; + Node& operator=(Node&&) = default; + AZ::Name GetName() const; void SetName(AZ::Name name); - ObjectPtr GetMutableProperties(); - ConstObjectPtr GetProperties() const; + Object::ContainerType& GetProperties(); + const Object::ContainerType& GetProperties() const; - ArrayPtr GetMutableChildren(); - ConstArrayPtr GetChildren() const; - - bool operator==(const Node& rhs) const; + Array::ContainerType& GetChildren(); + const Array::ContainerType& GetChildren() const; private: AZ::Name m_name; - ArrayPtr m_array; - ObjectPtr m_object; + Object::ContainerType m_properties; + Array::ContainerType m_children; friend class Value; }; + using NodePtr = AZStd::shared_ptr; + using ConstNodePtr = AZStd::shared_ptr; + class Value { public: @@ -252,8 +261,13 @@ namespace AZ::Dom // Visitor API Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; + AZStd::unique_ptr GetWriteHandler(); + + bool DeepCompareIsEqual(const Value& other) const; private: + const Node& GetNodeInternal() const; + Node& GetNodeInternal(); const Object::ContainerType& GetObjectInternal() const; Object::ContainerType& GetObjectInternal(); const Array::ContainerType& GetArrayInternal() const; @@ -284,53 +298,10 @@ namespace AZ::Dom // ArrayType ArrayPtr, // NodeType - Node, + NodePtr, // OpaqueType AZStd::any*>; ValueType m_value; }; - - class ValueWriter : public Visitor - { - public: - ValueWriter(Value& outputValue); - - VisitorFlags GetVisitorFlags() const override; - Result Null() override; - Result Bool(bool value) override; - Result Int64(AZ::s64 value) override; - Result Uint64(AZ::u64 value) override; - Result Double(double value) override; - - Result String(AZStd::string_view value, Lifetime lifetime) override; - Result StartObject() override; - Result EndObject(AZ::u64 attributeCount) override; - Result Key(AZ::Name key) override; - Result RawKey(AZStd::string_view key, Lifetime lifetime) override; - Result StartArray() override; - Result EndArray(AZ::u64 elementCount) override; - Result StartNode(AZ::Name name) override; - Result RawStartNode(AZStd::string_view name, Lifetime lifetime) override; - Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) override; - - private: - Result FinishWrite(); - Value& CurrentValue(); - Visitor::Result EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount); - - struct ValueInfo - { - ValueInfo(Value& container); - - KeyType m_key; - Value m_value; - Value& m_container; - AZ::u64 m_attributeCount = 0; - AZ::u64 m_elementCount = 0; - }; - - Value& m_result; - AZStd::stack m_entryStack; - }; - } // namespace AZ::Dom +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp new file mode 100644 index 0000000000..71710f543b --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ::Dom +{ + ValueWriter::ValueWriter(Value& outputValue) + : m_result(outputValue) + { + } + + VisitorFlags ValueWriter::GetVisitorFlags() const + { + return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes; + } + + ValueWriter::ValueInfo::ValueInfo(Value& container) + : m_container(container) + { + } + + Visitor::Result ValueWriter::Null() + { + CurrentValue().SetNull(); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Bool(bool value) + { + CurrentValue().SetBool(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Int64(AZ::s64 value) + { + CurrentValue().SetInt(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Uint64(AZ::u64 value) + { + CurrentValue().SetUint(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::Double(double value) + { + CurrentValue().SetDouble(value); + return FinishWrite(); + } + + Visitor::Result ValueWriter::String(AZStd::string_view value, Lifetime lifetime) + { + if (lifetime == Lifetime::Persistent) + { + CurrentValue().SetString(value); + } + else + { + CurrentValue().CopyFromString(value); + } + return FinishWrite(); + } + + Visitor::Result ValueWriter::StartObject() + { + CurrentValue().SetObject(); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount) + { + const char* endMethodName; + switch (containerType) + { + case Type::ObjectType: + endMethodName = "EndObject"; + break; + case Type::ArrayType: + endMethodName = "EndArray"; + break; + case Type::NodeType: + endMethodName = "EndNode"; + break; + default: + AZ_Assert(false, "Invalid container type specified"); + return VisitorFailure(VisitorErrorCode::InternalError, "AZ::Dom::ValueWriter: EndContainer called with invalid container type"); + } + + if (m_entryStack.empty()) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format("AZ::Dom::ValueWriter: %s called without a matching call", endMethodName)); + } + + const ValueInfo& topEntry = m_entryStack.top(); + if (topEntry.m_container.GetType() != containerType) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); + } + + if (topEntry.m_attributeCount != attributeCount) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format( + "AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount, + topEntry.m_attributeCount)); + } + + if (topEntry.m_elementCount != elementCount) + { + return VisitorFailure( + VisitorErrorCode::InternalError, + AZStd::string::format( + "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, + topEntry.m_elementCount)); + } + + m_entryStack.pop(); + return FinishWrite(); + } + + Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount) + { + return EndContainer(Type::ObjectType, attributeCount, 0); + } + + Visitor::Result ValueWriter::Key(AZ::Name key) + { + AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); + AZ_Assert(!m_entryStack.top().m_container.IsArray(), "Attempted to push a key to an array"); + m_entryStack.top().m_key = key; + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime) + { + return Key(AZ::Name(key)); + } + + Visitor::Result ValueWriter::StartArray() + { + CurrentValue().SetArray(); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::EndArray(AZ::u64 elementCount) + { + return EndContainer(Type::ArrayType, 0, elementCount); + } + + Visitor::Result ValueWriter::StartNode(AZ::Name name) + { + CurrentValue().SetNode(name); + + m_entryStack.emplace(CurrentValue()); + return VisitorSuccess(); + } + + Visitor::Result ValueWriter::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime) + { + return StartNode(AZ::Name(name)); + } + + Visitor::Result ValueWriter::EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) + { + return EndContainer(Type::NodeType, attributeCount, elementCount); + } + + Visitor::Result ValueWriter::FinishWrite() + { + if (m_entryStack.empty()) + { + return VisitorSuccess(); + } + + Value value; + m_entryStack.top().m_value.Swap(value); + ValueInfo& newEntry = m_entryStack.top(); + + if (!newEntry.m_key.IsEmpty()) + { + newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value)); + newEntry.m_key = AZ::Name(); + ++newEntry.m_attributeCount; + } + else + { + newEntry.m_container.PushBack(AZStd::move(value)); + ++newEntry.m_elementCount; + } + + return VisitorSuccess(); + } + + Value& ValueWriter::CurrentValue() + { + if (m_entryStack.empty()) + { + return m_result; + } + return m_entryStack.top().m_value; + } +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h new file mode 100644 index 0000000000..fb5f4324a6 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -0,0 +1,58 @@ +/* + * 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 +#include + +namespace AZ::Dom +{ + class ValueWriter : public Visitor + { + public: + ValueWriter(Value& outputValue); + + VisitorFlags GetVisitorFlags() const override; + Result Null() override; + Result Bool(bool value) override; + Result Int64(AZ::s64 value) override; + Result Uint64(AZ::u64 value) override; + Result Double(double value) override; + + Result String(AZStd::string_view value, Lifetime lifetime) override; + Result StartObject() override; + Result EndObject(AZ::u64 attributeCount) override; + Result Key(AZ::Name key) override; + Result RawKey(AZStd::string_view key, Lifetime lifetime) override; + Result StartArray() override; + Result EndArray(AZ::u64 elementCount) override; + Result StartNode(AZ::Name name) override; + Result RawStartNode(AZStd::string_view name, Lifetime lifetime) override; + Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) override; + + private: + Result FinishWrite(); + Value& CurrentValue(); + Visitor::Result EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount); + + struct ValueInfo + { + ValueInfo(Value& container); + + KeyType m_key; + Value m_value; + Value& m_container; + AZ::u64 m_attributeCount = 0; + AZ::u64 m_elementCount = 0; + }; + + Value& m_result; + AZStd::stack m_entryStack; + }; +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index fa9f94505f..e17fb941b8 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -133,6 +133,8 @@ set(FILES DOM/DomUtils.h DOM/DomValue.cpp DOM/DomValue.h + DOM/DomValueWriter.cpp + DOM/DomValueWriter.h DOM/DomVisitor.cpp DOM/DomVisitor.h DOM/Backends/JSON/JsonBackend.h From a2d474cc4f30c17b7354221a1c0e55dfb20c6de4 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 9 Dec 2021 17:47:01 -0800 Subject: [PATCH 04/49] Add some intial tests Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 123 +++++++++- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 28 ++- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 4 +- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 0 .../AzCore/Tests/DOM/DomValueTests.cpp | 211 ++++++++++++++++++ .../AzCore/Tests/azcoretests_files.cmake | 2 + 6 files changed, 350 insertions(+), 18 deletions(-) create mode 100644 Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp create mode 100644 Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 68a88ed35a..2f41835a37 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -97,6 +97,16 @@ namespace AZ::Dom return Value(&value); } + Value::Value(int32_t value) + : m_value(aznumeric_cast(value)) + { + } + + Value::Value(uint32_t value) + : m_value(aznumeric_cast(value)) + { + } + Value::Value(int64_t value) : m_value(value) { @@ -107,6 +117,11 @@ namespace AZ::Dom { } + Value::Value(float value) + : m_value(aznumeric_cast(value)) + { + } + Value::Value(double value) : m_value(value) { @@ -117,6 +132,40 @@ namespace AZ::Dom { } + Value::Value(Type type) + { + switch (type) + { + case Type::NullType: + // Null is the default initialized value + break; + case Type::FalseType: + m_value = false; + break; + case Type::TrueType: + m_value = true; + break; + case Type::ObjectType: + SetObject(); + break; + case Type::ArrayType: + SetArray(); + break; + case Type::StringType: + SetString(""); + break; + case Type::NumberType: + m_value = 0.0; + break; + case Type::NodeType: + SetNode(""); + break; + case Type::OpaqueType: + AZ_Assert(false, "AZ::Dom::Value may not be constructed with an empty opaque type"); + break; + } + } + Value& Value::operator=(const Value& other) { m_value = other.m_value; @@ -139,11 +188,11 @@ namespace AZ::Dom { if (IsInt()) { - return GetInt() == rhs.GetInt(); + return GetInt64() == rhs.GetInt64(); } else if (IsUint()) { - return GetUint() == rhs.GetUint(); + return GetUint64() == rhs.GetUint64(); } else { @@ -315,7 +364,7 @@ namespace AZ::Dom AZ_Assert( type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or a node"); - if (type == Type::ObjectType) + if (type == Type::ArrayType) { return AZStd::get(m_value)->m_values; } @@ -331,7 +380,7 @@ namespace AZ::Dom AZ_Assert( type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or node"); - if (type == Type::ObjectType) + if (type == Type::ArrayType) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } @@ -702,7 +751,7 @@ namespace AZ::Dom return Value(); } - int64_t Value::GetInt() const + int64_t Value::GetInt64() const { switch (m_value.index()) { @@ -717,12 +766,22 @@ namespace AZ::Dom return {}; } - void Value::SetInt(int64_t value) + void Value::SetInt64(int64_t value) { m_value = value; } - uint64_t Value::GetUint() const + int32_t Value::GetInt32() const + { + return aznumeric_cast(GetInt64()); + } + + void Value::SetInt32(int32_t value) + { + m_value = aznumeric_cast(value); + } + + uint64_t Value::GetUint64() const { switch (m_value.index()) { @@ -737,11 +796,21 @@ namespace AZ::Dom return {}; } - void Value::SetUint(uint64_t value) + void Value::SetUint64(uint64_t value) { m_value = value; } + uint32_t Value::GetUint32() const + { + return aznumeric_cast(GetUint64()); + } + + void Value::SetUint32(uint32_t value) + { + m_value = aznumeric_cast(value); + } + bool Value::GetBool() const { if (IsBool()) @@ -777,6 +846,16 @@ namespace AZ::Dom m_value = value; } + float Value::GetFloat() const + { + return aznumeric_cast(GetDouble()); + } + + void Value::SetFloat(float value) + { + m_value = aznumeric_cast(value); + } + AZStd::string_view Value::GetString() const { switch (m_value.index()) @@ -861,6 +940,11 @@ namespace AZ::Dom const Object::ContainerType& object = GetObjectInternal(); for (const Object::EntryType& entry : object) { + result = visitor.Key(entry.first); + if (!result.IsSuccess()) + { + return; + } result = entry.second.Accept(visitor, copyStrings); if (!result.IsSuccess()) { @@ -896,6 +980,11 @@ namespace AZ::Dom const Object::ContainerType& object = GetObjectInternal(); for (const Object::EntryType& entry : object) { + result = visitor.Key(entry.first); + if (!result.IsSuccess()) + { + return; + } result = entry.second.Accept(visitor, copyStrings); if (!result.IsSuccess()) { @@ -932,6 +1021,16 @@ namespace AZ::Dom bool Value::DeepCompareIsEqual(const Value& other) const { + if (IsString() && other.IsString()) + { + // If we both hold the same ref counted string we don't need to do a full comparison + if (AZStd::holds_alternative>(m_value) && m_value == other.m_value) + { + return true; + } + return GetString() == other.GetString(); + } + if (m_value.index() != other.m_value.index()) { return false; @@ -1045,4 +1144,12 @@ namespace AZ::Dom }, m_value); } + + Value Value::DeepCopy(bool copyStrings) const + { + Value newValue; + AZStd::unique_ptr writer = newValue.GetWriteHandler(); + Accept(*writer, copyStrings); + return newValue; + } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index a460274602..61c3207ea4 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -109,10 +109,15 @@ namespace AZ::Dom Value(Value&&) noexcept; Value(AZStd::string_view string, bool copy); - explicit Value(int64_t value); - explicit Value(uint64_t value); - explicit Value(double value); - explicit Value(bool value); + Value(int32_t value); + Value(uint32_t value); + Value(int64_t value); + Value(uint64_t value); + Value(float value); + Value(double value); + Value(bool value); + + explicit Value(Type type); static Value FromOpaqueValue(AZStd::any& value); @@ -226,12 +231,16 @@ namespace AZ::Dom Value GetNodeValue() const; // int API... - int64_t GetInt() const; - void SetInt(int64_t); + int64_t GetInt64() const; + void SetInt64(int64_t); + int32_t GetInt32() const; + void SetInt32(int32_t); // uint API... - uint64_t GetUint() const; - void SetUint(uint64_t); + uint64_t GetUint64() const; + void SetUint64(uint64_t); + uint32_t GetUint32() const; + void SetUint32(uint32_t); // bool API... bool GetBool() const; @@ -240,6 +249,8 @@ namespace AZ::Dom // double API... double GetDouble() const; void SetDouble(double); + float GetFloat() const; + void SetFloat(float); // string API... AZStd::string_view GetString() const; @@ -264,6 +275,7 @@ namespace AZ::Dom AZStd::unique_ptr GetWriteHandler(); bool DeepCompareIsEqual(const Value& other) const; + Value DeepCopy(bool copyStrings = true) const; private: const Node& GetNodeInternal() const; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 71710f543b..a760cc5b28 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -39,13 +39,13 @@ namespace AZ::Dom Visitor::Result ValueWriter::Int64(AZ::s64 value) { - CurrentValue().SetInt(value); + CurrentValue().SetInt64(value); return FinishWrite(); } Visitor::Result ValueWriter::Uint64(AZ::u64 value) { - CurrentValue().SetUint(value); + CurrentValue().SetUint64(value); return FinishWrite(); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp new file mode 100644 index 0000000000..02777bf6dd --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Dom::Tests +{ + class DomValueTests : public UnitTest::AllocatorsFixture + { + public: + void SetUp() override + { + UnitTest::AllocatorsFixture::SetUp(); + NameDictionary::Create(); + } + + void TearDown() override + { + m_value = Value(); + + NameDictionary::Destroy(); + UnitTest::AllocatorsFixture::TearDown(); + } + + void PerformValueChecks() + { + Value shallowCopy = m_value; + EXPECT_EQ(m_value, shallowCopy); + EXPECT_TRUE(m_value.DeepCompareIsEqual(shallowCopy)); + + Value deepCopy = m_value.DeepCopy(); + EXPECT_TRUE(m_value.DeepCompareIsEqual(deepCopy)); + } + + Value m_value; + }; + + TEST_F(DomValueTests, EmptyArray) + { + m_value.SetArray(); + + EXPECT_TRUE(m_value.IsArray()); + EXPECT_EQ(m_value.Size(), 0); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, SimpleArray) + { + m_value.SetArray(); + + for (int i = 0; i < 5; ++i) + { + m_value.PushBack(Value(i)); + EXPECT_EQ(m_value.Size(), i + 1); + EXPECT_EQ(m_value[i].GetInt32(), i); + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, NestedArrays) + { + m_value.SetArray(); + for (int j = 0; j < 5; ++j) + { + Value nestedArray(Type::ArrayType); + for (int i = 0; i < 5; ++i) + { + nestedArray.PushBack(Value(i)); + } + m_value.PushBack(AZStd::move(nestedArray)); + } + + EXPECT_EQ(m_value.Size(), 5); + for (int i = 0; i < 3; ++i) + { + EXPECT_EQ(m_value[i].Size(), 5); + for (int j = 0; j < 5; ++j) + { + EXPECT_EQ(m_value[i][j].GetInt32(), j); + } + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, EmptyObject) + { + m_value.SetObject(); + EXPECT_EQ(m_value.MemberCount(), 0); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, SimpleObject) + { + m_value.SetObject(); + for (int i = 0; i < 5; ++i) + { + AZStd::string key = AZStd::string::format("Key%i", i); + m_value.AddMember(key, Value(i)); + EXPECT_EQ(m_value.MemberCount(), i + 1); + EXPECT_EQ(m_value[key].GetInt32(), i); + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, NestedObjects) + { + m_value.SetObject(); + for (int j = 0; j < 3; ++j) + { + Value nestedObject(Type::ObjectType); + for (int i = 0; i < 5; ++i) + { + nestedObject.AddMember(AZStd::string::format("Key%i", i), Value(i)); + } + m_value.AddMember(AZStd::string::format("Obj%i", j), AZStd::move(nestedObject)); + } + + EXPECT_EQ(m_value.MemberCount(), 3); + for (int j = 0; j < 3; ++j) + { + const Value& nestedObject = m_value[AZStd::string::format("Obj%i", j)]; + EXPECT_EQ(nestedObject.MemberCount(), 5); + for (int i = 0; i < 5; ++i) + { + EXPECT_EQ(nestedObject[AZStd::string::format("Key%i", i)].GetInt32(), i); + } + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, EmptyNode) + { + m_value.SetNode("Test"); + EXPECT_EQ(m_value.GetNodeName(), AZ::Name("Test")); + EXPECT_EQ(m_value.MemberCount(), 0); + EXPECT_EQ(m_value.Size(), 0); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, SimpleNode) + { + m_value.SetNode("Test"); + + for (int i = 0; i < 10; ++i) + { + m_value.PushBack(Value(i)); + EXPECT_EQ(m_value.Size(), i + 1); + EXPECT_EQ(m_value[i].GetInt32(), i); + + if (i < 5) + { + AZ::Name key = AZ::Name(AZStd::string::format("TwoTimes%i", i)); + m_value.AddMember(key, Value(i * 2)); + EXPECT_EQ(m_value.MemberCount(), i + 1); + EXPECT_EQ(m_value[key].GetInt32(), i * 2); + } + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, NestedNodes) + { + m_value.SetNode("TopLevel"); + + const AZ::Name childNodeName("ChildNode"); + + for (int i = 0; i < 5; ++i) + { + Value childNode(Type::NodeType); + childNode.SetNodeName(childNodeName); + childNode.SetNodeValue(i); + + childNode.AddMember("foo", i); + childNode.AddMember("bar", Value("test", false)); + + m_value.PushBack(childNode); + } + + EXPECT_EQ(m_value.Size(), 5); + for (int i = 0; i < 5; ++i) + { + const Value& childNode = m_value[i]; + EXPECT_EQ(childNode.GetNodeName(), childNodeName); + EXPECT_EQ(childNode.GetNodeValue().GetInt32(), i); + EXPECT_EQ(childNode["foo"].GetInt32(), i); + EXPECT_EQ(childNode["bar"].GetString(), "test"); + } + + PerformValueChecks(); + } +} // namespace AZ::Dom::Tests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 0fca75e2a8..14a8bbbac0 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -215,6 +215,8 @@ set(FILES AZStd/VectorAndArray.cpp DOM/DomJsonTests.cpp DOM/DomJsonBenchmarks.cpp + DOM/DomValueTests.cpp + DOM/DomValueBenchmarks.cpp ) # Prevent the following files from being grouped in UNITY builds From 15e0bb16939b38b578cd075e297794a72fee4eac Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 9 Dec 2021 21:37:22 -0800 Subject: [PATCH 05/49] Add tests, fix missing bool in Accept and operator[] insert Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 21 ++++- .../AzCore/Tests/DOM/DomValueTests.cpp | 87 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 2f41835a37..675a6931f5 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -407,7 +407,22 @@ namespace AZ::Dom Value& Value::operator[](KeyType name) { - return FindMember(name)->second; + Object::ContainerType& object = GetObjectInternal(); + auto existingEntry = AZStd::find_if( + object.begin(), object.end(), + [&name](const Object::EntryType& entry) + { + return entry.first == name; + }); + if (existingEntry != object.end()) + { + return existingEntry->second; + } + else + { + object.emplace_back(name, Value()); + return object[object.size() - 1].second; + } } const Value& Value::operator[](KeyType name) const @@ -924,6 +939,10 @@ namespace AZ::Dom { result = visitor.Double(arg); } + else if constexpr (AZStd::is_same_v) + { + result = visitor.Bool(arg); + } else if constexpr (AZStd::is_same_v) { result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 02777bf6dd..75cb71248f 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace AZ::Dom::Tests { @@ -208,4 +209,90 @@ namespace AZ::Dom::Tests PerformValueChecks(); } + + TEST_F(DomValueTests, Int64) + { + m_value.SetObject(); + m_value["int64_min"] = AZStd::numeric_limits::min(); + m_value["int64_max"] = AZStd::numeric_limits::max(); + + EXPECT_EQ(m_value["int64_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["int64_min"].GetInt64(), AZStd::numeric_limits::min()); + EXPECT_EQ(m_value["int64_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["int64_max"].GetInt64(), AZStd::numeric_limits::max()); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, Uint64) + { + m_value.SetObject(); + m_value["uint64_min"] = AZStd::numeric_limits::min(); + m_value["uint64_max"] = AZStd::numeric_limits::max(); + + EXPECT_EQ(m_value["uint64_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["uint64_min"].GetInt64(), AZStd::numeric_limits::min()); + EXPECT_EQ(m_value["uint64_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["uint64_max"].GetInt64(), AZStd::numeric_limits::max()); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, Double) + { + m_value.SetObject(); + m_value["double_min"] = AZStd::numeric_limits::min(); + m_value["double_max"] = AZStd::numeric_limits::max(); + + EXPECT_EQ(m_value["double_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["double_min"].GetDouble(), AZStd::numeric_limits::min()); + EXPECT_EQ(m_value["double_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["double_max"].GetDouble(), AZStd::numeric_limits::max()); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, Null) + { + m_value.SetObject(); + m_value["null_value"] = Value(Type::NullType); + + EXPECT_EQ(m_value["null_value"].GetType(), Type::NullType); + EXPECT_EQ(m_value["null_type"], Value()); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, Bool) + { + m_value.SetObject(); + m_value["true_value"] = true; + m_value["false_value"] = false; + + EXPECT_EQ(m_value["true_value"].GetType(), Type::TrueType); + EXPECT_EQ(m_value["true_value"].GetBool(), true); + EXPECT_EQ(m_value["false_value"].GetType(), Type::FalseType); + EXPECT_EQ(m_value["false_value"].GetBool(), false); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, String) + { + m_value.SetObject(); + AZStd::string stringToReference = "foo"; + m_value["no_copy"] = Value(stringToReference, false); + AZStd::string stringToCopy = "bar"; + m_value["copy"] = Value(stringToCopy, true); + + EXPECT_EQ(m_value["no_copy"].GetType(), Type::StringType); + EXPECT_EQ(m_value["no_copy"].GetString(), stringToReference); + EXPECT_EQ(m_value["no_copy"].GetString().data(), stringToReference.data()); + + EXPECT_EQ(m_value["copy"].GetType(), Type::StringType); + EXPECT_EQ(m_value["copy"].GetString(), stringToCopy); + EXPECT_NE(m_value["copy"].GetString().data(), stringToCopy.data()); + + PerformValueChecks(); + } } // namespace AZ::Dom::Tests From 947951b6c7473ccd45647e5e5c3938b5122718e7 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 9 Dec 2021 23:43:22 -0800 Subject: [PATCH 06/49] Add copy on write tests Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 34 ++++++-- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 14 ++- .../AzCore/Tests/DOM/DomValueTests.cpp | 86 +++++++++++++++++++ 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 675a6931f5..d9398da116 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -476,7 +476,7 @@ namespace AZ::Dom return FindMember(AZ::Name(name)); } - Object::Iterator Value::FindMember(KeyType name) + Object::Iterator Value::FindMutableMember(KeyType name) { Object::ContainerType& object = GetObjectInternal(); return AZStd::find_if( @@ -487,9 +487,9 @@ namespace AZ::Dom }); } - Object::Iterator Value::FindMember(AZStd::string_view name) + Object::Iterator Value::FindMutableMember(AZStd::string_view name) { - return FindMember(AZ::Name(name)); + return FindMutableMember(AZ::Name(name)); } Value& Value::MemberReserve(size_t newCapacity) @@ -511,7 +511,7 @@ namespace AZ::Dom Value& Value::AddMember(KeyType name, const Value& value) { Object::ContainerType& object = GetObjectInternal(); - if (auto memberIt = FindMember(name); memberIt != object.end()) + if (auto memberIt = FindMutableMember(name); memberIt != object.end()) { memberIt->second = value; } @@ -530,7 +530,7 @@ namespace AZ::Dom Value& Value::AddMember(AZ::Name name, Value&& value) { Object::ContainerType& object = GetObjectInternal(); - if (auto memberIt = FindMember(name); memberIt != object.end()) + if (auto memberIt = FindMutableMember(name); memberIt != object.end()) { memberIt->second = value; } @@ -601,7 +601,7 @@ namespace AZ::Dom return EraseMember(AZ::Name(name)); } - Object::ContainerType& Value::GetObject() + Object::ContainerType& Value::GetMutableObject() { return GetObjectInternal(); } @@ -647,6 +647,16 @@ namespace AZ::Dom return GetArrayInternal()[index]; } + Value& Value::MutableAt(size_t index) + { + return operator[](index); + } + + const Value& Value::At(size_t index) const + { + return operator[](index); + } + Array::ConstIterator Value::Begin() const { return GetArrayInternal().begin(); @@ -695,7 +705,7 @@ namespace AZ::Dom return GetArrayInternal().erase(first, last); } - Array::ContainerType& Value::GetArray() + Array::ContainerType& Value::GetMutableArray() { return GetArrayInternal(); } @@ -766,6 +776,16 @@ namespace AZ::Dom return Value(); } + Node& Value::GetMutableNode() + { + return GetNodeInternal(); + } + + const Node& Value::GetNode() const + { + return GetNodeInternal(); + } + int64_t Value::GetInt64() const { switch (m_value.index()) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 61c3207ea4..39ae5a5c37 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -162,10 +162,10 @@ namespace AZ::Dom Object::Iterator MemberBegin(); Object::Iterator MemberEnd(); + Object::Iterator FindMutableMember(KeyType name); + Object::Iterator FindMutableMember(AZStd::string_view name); Object::ConstIterator FindMember(KeyType name) const; Object::ConstIterator FindMember(AZStd::string_view name) const; - Object::Iterator FindMember(KeyType name); - Object::Iterator FindMember(AZStd::string_view name); Value& MemberReserve(size_t newCapacity); bool HasMember(KeyType name) const; @@ -185,7 +185,7 @@ namespace AZ::Dom Object::Iterator EraseMember(KeyType name); Object::Iterator EraseMember(AZStd::string_view name); - Object::ContainerType& GetObject(); + Object::ContainerType& GetMutableObject(); const Object::ContainerType& GetObject() const; // Array API (also used by Node)... @@ -199,6 +199,9 @@ namespace AZ::Dom Value& operator[](size_t index); const Value& operator[](size_t index) const; + Value& MutableAt(size_t index); + const Value& At(size_t index) const; + Array::ConstIterator Begin() const; Array::ConstIterator End() const; Array::Iterator Begin(); @@ -211,7 +214,7 @@ namespace AZ::Dom Array::Iterator Erase(Array::ConstIterator pos); Array::Iterator Erase(Array::ConstIterator first, Array::ConstIterator last); - Array::ContainerType& GetArray(); + Array::ContainerType& GetMutableArray(); const Array::ContainerType& GetArray() const; // Node API (supports both object + array API, plus a dedicated NodeName)... @@ -230,6 +233,9 @@ namespace AZ::Dom //! Convenience method, gets the first non-node element of a Node. Value GetNodeValue() const; + Node& GetMutableNode(); + const Node& GetNode() const; + // int API... int64_t GetInt64() const; void SetInt64(int64_t); diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 75cb71248f..84e2e16958 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -295,4 +295,90 @@ namespace AZ::Dom::Tests PerformValueChecks(); } + + TEST_F(DomValueTests, CopyOnWrite_Object) + { + Value v1(Type::ObjectType); + v1["foo"] = 5; + + Value nestedObject(Type::ObjectType); + v1["obj"] = nestedObject; + + Value v2 = v1; + EXPECT_EQ(&v1.GetObject(), &v2.GetObject()); + EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject()); + + v2["foo"] = 0; + + EXPECT_NE(&v1.GetObject(), &v2.GetObject()); + EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject()); + + v2["obj"]["key"] = true; + + EXPECT_NE(&v1.GetObject(), &v2.GetObject()); + EXPECT_NE(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject()); + + v2 = v1; + + EXPECT_EQ(&v1.GetObject(), &v2.GetObject()); + EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject()); + } + + TEST_F(DomValueTests, CopyOnWrite_Array) + { + Value v1(Type::ArrayType); + v1.PushBack(1); + v1.PushBack(2); + + Value nestedArray(Type::ArrayType); + v1.PushBack(nestedArray); + Value v2 = v1; + + EXPECT_EQ(&v1.GetArray(), &v2.GetArray()); + EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + + v2[0] = 0; + + EXPECT_NE(&v1.GetArray(), &v2.GetArray()); + EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + + v2[2].PushBack(42); + + EXPECT_NE(&v1.GetArray(), &v2.GetArray()); + EXPECT_NE(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + + v2 = v1; + + EXPECT_EQ(&v1.GetArray(), &v2.GetArray()); + EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + } + + TEST_F(DomValueTests, CopyOnWrite_Node) + { + Value v1; + v1.SetNode("TopLevel"); + + v1.PushBack(1); + v1.PushBack(2); + v1["obj"].SetNode("Nested"); + Value v2 = v1; + + EXPECT_EQ(&v1.GetNode(), &v2.GetNode()); + EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode()); + + v2[0] = 0; + + EXPECT_NE(&v1.GetNode(), &v2.GetNode()); + EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode()); + + v2["obj"].PushBack(42); + + EXPECT_NE(&v1.GetNode(), &v2.GetNode()); + EXPECT_NE(&v1["obj"].GetNode(), &v2["obj"].GetNode()); + + v2 = v1; + + EXPECT_EQ(&v1.GetNode(), &v2.GetNode()); + EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode()); + } } // namespace AZ::Dom::Tests From 1af39a5c5cc26028f04e00c8504b44cfa17a4462 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Fri, 10 Dec 2021 15:57:52 -0800 Subject: [PATCH 07/49] Add benchmarks, some light optimizations like SSO Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp | 12 ++ Code/Framework/AzCore/AzCore/DOM/DomUtils.h | 3 + Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 49 ++++++-- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 37 ++++++ .../AzCore/AzCore/DOM/DomValueWriter.cpp | 51 ++++++-- .../AzCore/AzCore/DOM/DomValueWriter.h | 11 +- .../AzCore/AzCore/Memory/PoolAllocator.h | 18 +-- .../AzCore/Tests/DOM/DomJsonBenchmarks.cpp | 42 ++++++- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 117 ++++++++++++++++++ .../AzCore/Tests/DOM/DomValueTests.cpp | 19 ++- 10 files changed, 319 insertions(+), 40 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp index 2e78661518..46fa5b9bac 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp @@ -21,4 +21,16 @@ namespace AZ::Dom::Utils { return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor); } + + AZ::Outcome AZ::Dom::Utils::WriteToValue(Backend::WriteCallback writeCallback) + { + Value value; + AZStd::unique_ptr writer = value.GetWriteHandler(); + Visitor::Result result = writeCallback(*writer); + if (!result.IsSuccess()) + { + return AZ::Failure(result.GetError().FormatVisitorErrorMessage()); + } + return AZ::Success(AZStd::move(value)); + } } // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h index 84a6eb5687..f03e5b66b8 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h @@ -9,9 +9,12 @@ #pragma once #include +#include namespace AZ::Dom::Utils { Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor); Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor); + + AZ::Outcome WriteToValue(Backend::WriteCallback writeCallback); } // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index d9398da116..d8650601ba 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -19,7 +19,7 @@ namespace AZ::Dom { if (refCountedPointer.use_count() > 1) { - AZStd::shared_ptr newPointer = AZStd::make_shared(); + AZStd::shared_ptr newPointer = AZStd::allocate_shared(AZStdAlloc()); *newPointer = *refCountedPointer; refCountedPointer = AZStd::move(newPointer); } @@ -71,8 +71,8 @@ namespace AZ::Dom } Value::Value(Value&& value) noexcept - : m_value(value.m_value) { + operator=(value); } Value::Value(AZStd::string_view string, bool copy) @@ -174,7 +174,7 @@ namespace AZ::Dom Value& Value::operator=(Value&& other) noexcept { - m_value = other.m_value; + m_value.swap(other.m_value); return *this; } @@ -212,7 +212,7 @@ namespace AZ::Dom void Value::Swap(Value& other) noexcept { - AZStd::swap(m_value, other.m_value); + m_value.swap(other.m_value); } Type Dom::Value::GetType() const @@ -229,14 +229,15 @@ namespace AZ::Dom return AZStd::get(m_value) ? Type::TrueType : Type::FalseType; case 5: // AZStd::string_view case 6: // AZStd::shared_ptr + case 7: // ShortStringType return Type::StringType; - case 7: // ObjectPtr + case 8: // ObjectPtr return Type::ObjectType; - case 8: // ArrayPtr + case 9: // ArrayPtr return Type::ArrayType; - case 9: // NodePtr + case 10: // NodePtr return Type::NodeType; - case 10: // AZStd::any* + case 11: // AZStd::any* return Type::OpaqueType; } AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); @@ -310,7 +311,7 @@ namespace AZ::Dom Value& Value::SetObject() { - m_value = AZStd::make_shared(); + m_value = AZStd::allocate_shared(AZStdAlloc()); return *this; } @@ -511,6 +512,7 @@ namespace AZ::Dom Value& Value::AddMember(KeyType name, const Value& value) { Object::ContainerType& object = GetObjectInternal(); + object.reserve((object.size() / Object::ReserveIncrement + 1) * Object::ReserveIncrement); if (auto memberIt = FindMutableMember(name); memberIt != object.end()) { memberIt->second = value; @@ -613,7 +615,7 @@ namespace AZ::Dom Value& Value::SetArray() { - m_value = AZStd::make_shared(); + m_value = AZStd::allocate_shared(AZStdAlloc()); return *this; } @@ -685,7 +687,9 @@ namespace AZ::Dom Value& Value::PushBack(Value value) { - GetArrayInternal().push_back(AZStd::move(value)); + Array::ContainerType& array = GetArrayInternal(); + array.reserve((array.size() / Array::ReserveIncrement + 1) * Array::ReserveIncrement); + array.push_back(AZStd::move(value)); return *this; } @@ -717,7 +721,7 @@ namespace AZ::Dom void Value::SetNode(AZ::Name name) { - m_value = AZStd::make_shared(name); + m_value = AZStd::allocate_shared(AZStdAlloc(), name); } void Value::SetNode(AZStd::string_view name) @@ -899,6 +903,11 @@ namespace AZ::Dom return AZStd::get(m_value); case 6: // AZStd::shared_ptr return *AZStd::get>(m_value); + case 7: // ShortStringType + { + const ShortStringType& ShortString = AZStd::get(m_value); + return { ShortString.m_data.data(), ShortString.m_size }; + } } AZ_Assert(false, "AZ::Dom::Value: Called GetString on a non-string type"); return {}; @@ -911,12 +920,26 @@ namespace AZ::Dom void Value::SetString(AZStd::string_view value) { + if (value.size() <= ShortStringSize) + { + ShortStringType buffer; + buffer.m_size = value.size(); + memcpy(buffer.m_data.data(), value.data(), buffer.m_size); + m_value = buffer; + } m_value = value; } void Value::CopyFromString(AZStd::string_view value) { - m_value = AZStd::make_shared(value); + if (value.size() <= ShortStringSize) + { + SetString(value); + } + else + { + m_value = AZStd::allocate_shared(AZStdAlloc(), value); + } } AZStd::any& Value::GetOpaqueValue() const diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 39ae5a5c37..4dd3bde197 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -11,11 +11,13 @@ #include #include #include +#include #include #include #include #include #include +#include namespace AZ::Dom { @@ -34,6 +36,22 @@ namespace AZ::Dom OpaqueType = 8, }; + class ValueAllocator final : public ThreadPoolBase + { + public: + AZ_CLASS_ALLOCATOR(ValueAllocator, SystemAllocator, 0); + AZ_TYPE_INFO(ValueAllocator, "{5BC8B389-72C7-459E-B502-12E74D61869F}"); + + ValueAllocator() + : ThreadPoolBase("DomValueAllocator", "Allocator for AZ::Dom::Value") + { + } + }; + + // class ValueAllocator : public Internal::PoolAllocatorHelper; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; + static constexpr const size_t ReserveIncrement = 4; private: ContainerType m_values; @@ -59,6 +78,7 @@ namespace AZ::Dom using ContainerType = AZStd::vector; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; + static constexpr const size_t ReserveIncrement = 8; private: ContainerType m_values; @@ -293,6 +313,18 @@ namespace AZ::Dom explicit Value(AZStd::any* opaqueValue); + static constexpr const size_t ShortStringSize = sizeof(AZStd::string_view) - sizeof(size_t); + struct ShortStringType + { + AZStd::array m_data; + size_t m_size; + + bool operator==(const ShortStringType& other) const + { + return m_size == other.m_size ? memcmp(m_data.data(), other.m_data.data(), m_size) == 0 : false; + } + }; + // If using the the copy on write model, anything stored internally as a shared_ptr will // detach and copy when doing a mutating operation if use_count() > 1. @@ -311,6 +343,7 @@ namespace AZ::Dom // StringType AZStd::string_view, AZStd::shared_ptr, + ShortStringType, // ObjectType ObjectPtr, // ArrayType @@ -320,6 +353,10 @@ namespace AZ::Dom // OpaqueType AZStd::any*>; + static_assert( + sizeof(ValueType) == sizeof(AZStd::variant), + "ValueType should have no members larger than ShortStringType"); + ValueType m_value; }; } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index a760cc5b28..cf46a77d54 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -103,35 +103,68 @@ namespace AZ::Dom } const ValueInfo& topEntry = m_entryStack.top(); - if (topEntry.m_container.GetType() != containerType) + Value& container = topEntry.m_container; + ValueBuffer& buffer = GetValueBuffer(); + + if (container.GetType() != containerType) { return VisitorFailure( VisitorErrorCode::InternalError, AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); } - if (topEntry.m_attributeCount != attributeCount) + if (buffer.m_attributes.size() != attributeCount) { return VisitorFailure( VisitorErrorCode::InternalError, AZStd::string::format( "AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount, - topEntry.m_attributeCount)); + buffer.m_attributes.size())); } - if (topEntry.m_elementCount != elementCount) + if (buffer.m_elements.size() != elementCount) { return VisitorFailure( VisitorErrorCode::InternalError, AZStd::string::format( "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, - topEntry.m_elementCount)); + buffer.m_elements.size())); + } + if (buffer.m_attributes.size() > 0) + { + container.MemberReserve(buffer.m_attributes.size()); + for (AZStd::pair& entry : buffer.m_attributes) + { + container.AddMember(AZStd::move(entry.first), AZStd::move(entry.second)); + } + buffer.m_attributes.clear(); + } + + if(buffer.m_elements.size() > 0) + { + container.Reserve(buffer.m_elements.size()); + for (Value& entry : buffer.m_elements) + { + container.PushBack(AZStd::move(entry)); + } + buffer.m_elements.clear(); } m_entryStack.pop(); return FinishWrite(); } + ValueWriter::ValueBuffer& ValueWriter::GetValueBuffer() + { + if (m_entryStack.size() <= m_valueBuffers.size()) + { + return m_valueBuffers[m_entryStack.size() - 1]; + } + + m_valueBuffers.resize(m_entryStack.size()); + return m_valueBuffers[m_entryStack.size() - 1]; + } + Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount) { return EndContainer(Type::ObjectType, attributeCount, 0); @@ -192,16 +225,16 @@ namespace AZ::Dom m_entryStack.top().m_value.Swap(value); ValueInfo& newEntry = m_entryStack.top(); + constexpr const size_t reserveSize = 8; + if (!newEntry.m_key.IsEmpty()) { - newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value)); + GetValueBuffer().m_attributes.emplace_back(AZStd::move(newEntry.m_key), AZStd::move(value)); newEntry.m_key = AZ::Name(); - ++newEntry.m_attributeCount; } else { - newEntry.m_container.PushBack(AZStd::move(value)); - ++newEntry.m_elementCount; + GetValueBuffer().m_elements.emplace_back(AZStd::move(value)); } return VisitorSuccess(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index fb5f4324a6..617184a926 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -48,11 +48,18 @@ namespace AZ::Dom KeyType m_key; Value m_value; Value& m_container; - AZ::u64 m_attributeCount = 0; - AZ::u64 m_elementCount = 0; }; + struct ValueBuffer + { + AZStd::vector m_elements; + AZStd::vector> m_attributes; + }; + + ValueBuffer& GetValueBuffer(); + Value& m_result; AZStd::stack m_entryStack; + AZStd::vector m_valueBuffers; }; } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h b/Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h index 72418b3d6e..bf24f04156 100644 --- a/Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h +++ b/Code/Framework/AzCore/AzCore/Memory/PoolAllocator.h @@ -25,12 +25,12 @@ namespace AZ * Template you can use to create your own thread pool allocators, as you can't inherit from ThreadPoolAllocator. * This is the case because we use tread local storage and we need separate "static" instance for each allocator. */ - template + template class PoolAllocatorHelper - : public SimpleSchemaAllocator + : public SimpleSchemaAllocator { public: - using Base = SimpleSchemaAllocator; + using Base = SimpleSchemaAllocator; using pointer_type = typename Base::pointer_type; using size_type = typename Base::size_type; using difference_type = typename Base::difference_type; @@ -140,13 +140,13 @@ namespace AZ * use PoolAllocatorThreadSafe or do the sync yourself. */ class PoolAllocator - : public Internal::PoolAllocatorHelper + : public Internal::PoolAllocatorHelper { public: AZ_CLASS_ALLOCATOR(PoolAllocator, SystemAllocator, 0); AZ_TYPE_INFO(PoolAllocator, "{D3DC61AF-0949-4BFA-87E0-62FA03A4C025}"); - using Base = Internal::PoolAllocatorHelper; + using Base = Internal::PoolAllocatorHelper; PoolAllocator(const char* name = "PoolAllocator", const char* desc = "Generic pool allocator for small objects") : Base(name, desc) @@ -154,21 +154,21 @@ namespace AZ } }; - template - using ThreadPoolBase = Internal::PoolAllocatorHelper >; + template + using ThreadPoolBase = Internal::PoolAllocatorHelper, ProfileAllocations >; /*! * Thread safe pool allocator. If you want to create your own thread pool heap, * inherit from ThreadPoolBase, as we need unique static variable for allocator type. */ class ThreadPoolAllocator final - : public ThreadPoolBase + : public ThreadPoolBase { public: AZ_CLASS_ALLOCATOR(ThreadPoolAllocator, SystemAllocator, 0); AZ_TYPE_INFO(ThreadPoolAllocator, "{05B4857F-CD06-4942-99FD-CA6A7BAE855A}"); - using Base = ThreadPoolBase; + using Base = ThreadPoolBase; ThreadPoolAllocator() : Base("PoolAllocatorThreadSafe", "Generic thread safe pool allocator for small objects") diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index 0baa4aeb53..3e6143e805 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -8,9 +8,10 @@ #if defined(HAVE_BENCHMARK) -#include #include #include +#include +#include #include #include #include @@ -25,22 +26,26 @@ namespace Benchmark { UnitTest::AllocatorsBenchmarkFixture::SetUp(st); AZ::NameDictionary::Create(); + AZ::AllocatorInstance::Create(); } void SetUp(::benchmark::State& st) override { UnitTest::AllocatorsBenchmarkFixture::SetUp(st); AZ::NameDictionary::Create(); + AZ::AllocatorInstance::Create(); } void TearDown(::benchmark::State& st) override { + AZ::AllocatorInstance::Destroy(); AZ::NameDictionary::Destroy(); UnitTest::AllocatorsBenchmarkFixture::TearDown(st); } void TearDown(const ::benchmark::State& st) override { + AZ::AllocatorInstance::Destroy(); AZ::NameDictionary::Destroy(); UnitTest::AllocatorsBenchmarkFixture::TearDown(st); } @@ -143,6 +148,30 @@ namespace Benchmark } BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocumentInPlace) + BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDomValueInPlace)(benchmark::State& state) + { + AZ::Dom::JsonBackend backend; + AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); + + for (auto _ : state) + { + state.PauseTiming(); + AZStd::string payloadCopy = serializedPayload; + state.ResumeTiming(); + + auto result = AZ::Dom::Utils::WriteToValue( + [&](AZ::Dom::Visitor& visitor) + { + return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor); + }); + + benchmark::DoNotOptimize(result.GetValue()); + } + + state.SetBytesProcessed(serializedPayload.size() * state.iterations()); + } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDomValueInPlace) + BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocument)(benchmark::State& state) { AZ::Dom::JsonBackend backend; @@ -179,6 +208,17 @@ namespace Benchmark } BENCHMARK_REGISTER_JSON(DomJsonBenchmark, JsonUtilsDeserializeToDocument) + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonPayloadGeneration)(benchmark::State& state) + { + for (auto _ : state) + { + benchmark::DoNotOptimize(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1))); + } + + state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); + } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonPayloadGeneration) + #undef BENCHMARK_REGISTER_JSON } // namespace Benchmark diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index e69de29bb2..d11ef7f69e 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ::Dom::Benchmark +{ + class DomValueBenchmark : public UnitTest::AllocatorsBenchmarkFixture + { + public: + void SetUp(const ::benchmark::State& st) override + { + UnitTest::AllocatorsBenchmarkFixture::SetUp(st); + AZ::NameDictionary::Create(); + AZ::AllocatorInstance::Create(); + } + + void SetUp(::benchmark::State& st) override + { + UnitTest::AllocatorsBenchmarkFixture::SetUp(st); + AZ::NameDictionary::Create(); + AZ::AllocatorInstance::Create(); + } + + void TearDown(::benchmark::State& st) override + { + AZ::AllocatorInstance::Destroy(); + AZ::NameDictionary::Destroy(); + UnitTest::AllocatorsBenchmarkFixture::TearDown(st); + } + + void TearDown(const ::benchmark::State& st) override + { + AZ::AllocatorInstance::Destroy(); + AZ::NameDictionary::Destroy(); + UnitTest::AllocatorsBenchmarkFixture::TearDown(st); + } + + Value GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength) + { + Value root(Type::ObjectType); + + AZStd::string entryTemplate; + while (entryTemplate.size() < static_cast(stringTemplateLength)) + { + entryTemplate += "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor "; + } + entryTemplate.resize(stringTemplateLength); + AZStd::string buffer; + + auto createString = [&](int n) -> Value + { + return Value(AZStd::string::format("#%i %s", n, entryTemplate.c_str()), true); + }; + + auto createEntry = [&](int n) -> Value + { + Value entry(Type::ObjectType); + entry.AddMember("string", createString(n)); + entry.AddMember("int", n); + entry.AddMember("double", static_cast(n) * 0.5); + entry.AddMember("bool", n % 2 == 0); + entry.AddMember("null", Value(Type::NullType)); + return entry; + }; + + auto createArray = [&]() -> Value + { + Value array(Type::ArrayType); + for (int i = 0; i < entryCount; ++i) + { + array.PushBack(createEntry(i)); + } + return array; + }; + + auto createObject = [&]() -> Value + { + Value object; + object.SetObject(); + for (int i = 0; i < entryCount; ++i) + { + buffer = AZStd::string::format("Key%i", i); + object.AddMember(AZ::Name(buffer), createArray()); + } + return object; + }; + + root["entries"] = createObject(); + + return root; + } + }; + + BENCHMARK_DEFINE_F(DomValueBenchmark, ValuePayloadGeneration)(benchmark::State& state) + { + for (auto _ : state) + { + benchmark::DoNotOptimize(GenerateDomBenchmarkPayload(state.range(0), state.range(1))); + } + + state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, ValuePayloadGeneration) + ->Args({ 10, 5 }) + ->Args({ 10, 500 }) + ->Args({ 100, 5 }) + ->Args({ 100, 500 }) + ->Unit(benchmark::kMillisecond); +} // namespace AZ::Dom::Benchmark diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 84e2e16958..125f426e02 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -25,12 +25,14 @@ namespace AZ::Dom::Tests { UnitTest::AllocatorsFixture::SetUp(); NameDictionary::Create(); + AZ::AllocatorInstance::Create(); } void TearDown() override { m_value = Value(); + AZ::AllocatorInstance::Destroy(); NameDictionary::Destroy(); UnitTest::AllocatorsFixture::TearDown(); } @@ -279,19 +281,24 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, String) { + const char* s1 = "reference string long enough to avoid SSO"; + const char* s2 = "copy string long enough to avoid SSO"; + m_value.SetObject(); - AZStd::string stringToReference = "foo"; + AZStd::string stringToReference = s1; m_value["no_copy"] = Value(stringToReference, false); - AZStd::string stringToCopy = "bar"; + AZStd::string stringToCopy = s2; m_value["copy"] = Value(stringToCopy, true); EXPECT_EQ(m_value["no_copy"].GetType(), Type::StringType); - EXPECT_EQ(m_value["no_copy"].GetString(), stringToReference); - EXPECT_EQ(m_value["no_copy"].GetString().data(), stringToReference.data()); + EXPECT_EQ(m_value["no_copy"].GetString(), s1); + stringToReference.at(0) = 'F'; + EXPECT_NE(m_value["no_copy"].GetString(), s1); EXPECT_EQ(m_value["copy"].GetType(), Type::StringType); - EXPECT_EQ(m_value["copy"].GetString(), stringToCopy); - EXPECT_NE(m_value["copy"].GetString().data(), stringToCopy.data()); + EXPECT_EQ(m_value["copy"].GetString(), s2); + stringToCopy.at(0) = 'F'; + EXPECT_EQ(m_value["copy"].GetString(), s2); PerformValueChecks(); } From 809ee2dce96fb3464a7a5d5a020469843dcdedc0 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Fri, 10 Dec 2021 16:18:38 -0800 Subject: [PATCH 08/49] Fix "force string copy" logic Signed-off-by: Nicholas Van Sickle --- .../AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp index 17f9bfea54..bc8ac9167c 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp @@ -361,7 +361,7 @@ namespace AZ::Dom::Json bool RapidJsonReadHandler::String(const char* str, rapidjson::SizeType length, bool copy) { - const Lifetime lifetime = copy ? m_stringLifetime : Lifetime::Temporary; + const Lifetime lifetime = !copy ? m_stringLifetime : Lifetime::Temporary; return CheckResult(m_visitor->String(AZStd::string_view(str, length), lifetime)); } @@ -377,7 +377,7 @@ namespace AZ::Dom::Json { m_visitor->Key(AZ::Name(key)); } - const Lifetime lifetime = copy ? m_stringLifetime : Lifetime::Temporary; + const Lifetime lifetime = !copy ? m_stringLifetime : Lifetime::Temporary; return CheckResult(m_visitor->RawKey(key, lifetime)); } From 1f2635d24bdaa200e256312df2df364b9cd26c2c Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Fri, 10 Dec 2021 16:35:26 -0800 Subject: [PATCH 09/49] Switch to the high pref heap allocator, roll back pool interface changes Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 13 ++++++------- .../AzCore/AzCore/Memory/PoolAllocator.h | 18 +++++++++--------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 4dd3bde197..9399e10d15 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,22 +37,20 @@ namespace AZ::Dom OpaqueType = 8, }; - class ValueAllocator final : public ThreadPoolBase + class ValueAllocator final : public SimpleSchemaAllocator { public: - AZ_CLASS_ALLOCATOR(ValueAllocator, SystemAllocator, 0); AZ_TYPE_INFO(ValueAllocator, "{5BC8B389-72C7-459E-B502-12E74D61869F}"); + using Base = SimpleSchemaAllocator; + ValueAllocator() - : ThreadPoolBase("DomValueAllocator", "Allocator for AZ::Dom::Value") + : Base("DomValueAllocator", "Allocator for AZ::Dom::Value") { + DisableOverriding(); } }; - // class ValueAllocator : public Internal::PoolAllocatorHelper + template class PoolAllocatorHelper - : public SimpleSchemaAllocator + : public SimpleSchemaAllocator { public: - using Base = SimpleSchemaAllocator; + using Base = SimpleSchemaAllocator; using pointer_type = typename Base::pointer_type; using size_type = typename Base::size_type; using difference_type = typename Base::difference_type; @@ -140,13 +140,13 @@ namespace AZ * use PoolAllocatorThreadSafe or do the sync yourself. */ class PoolAllocator - : public Internal::PoolAllocatorHelper + : public Internal::PoolAllocatorHelper { public: AZ_CLASS_ALLOCATOR(PoolAllocator, SystemAllocator, 0); AZ_TYPE_INFO(PoolAllocator, "{D3DC61AF-0949-4BFA-87E0-62FA03A4C025}"); - using Base = Internal::PoolAllocatorHelper; + using Base = Internal::PoolAllocatorHelper; PoolAllocator(const char* name = "PoolAllocator", const char* desc = "Generic pool allocator for small objects") : Base(name, desc) @@ -154,21 +154,21 @@ namespace AZ } }; - template - using ThreadPoolBase = Internal::PoolAllocatorHelper, ProfileAllocations >; + template + using ThreadPoolBase = Internal::PoolAllocatorHelper >; /*! * Thread safe pool allocator. If you want to create your own thread pool heap, * inherit from ThreadPoolBase, as we need unique static variable for allocator type. */ class ThreadPoolAllocator final - : public ThreadPoolBase + : public ThreadPoolBase { public: AZ_CLASS_ALLOCATOR(ThreadPoolAllocator, SystemAllocator, 0); AZ_TYPE_INFO(ThreadPoolAllocator, "{05B4857F-CD06-4942-99FD-CA6A7BAE855A}"); - using Base = ThreadPoolBase; + using Base = ThreadPoolBase; ThreadPoolAllocator() : Base("PoolAllocatorThreadSafe", "Generic thread safe pool allocator for small objects") From c53c97cf5f80bda645654cc4613be86a9eccc373 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Sat, 11 Dec 2021 15:54:57 -0800 Subject: [PATCH 10/49] Add another round of benchmarks Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 12 +- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 2 + .../AzCore/AzCore/DOM/DomValueWriter.cpp | 6 + .../AzCore/AzCore/DOM/DomValueWriter.h | 1 + .../AzCore/AzCore/DOM/DomVisitor.cpp | 5 + Code/Framework/AzCore/AzCore/DOM/DomVisitor.h | 2 + .../AzCore/Tests/DOM/DomJsonBenchmarks.cpp | 86 ++++++++++++-- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 110 +++++++++++++++++- 8 files changed, 210 insertions(+), 14 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index d8650601ba..ed4488267a 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -61,6 +61,11 @@ namespace AZ::Dom return m_children; } + Value::Value(AZStd::shared_ptr string) + : m_value(string) + { + } + Value::Value() { } @@ -895,6 +900,11 @@ namespace AZ::Dom m_value = aznumeric_cast(value); } + void Value::SetString(AZStd::shared_ptr string) + { + m_value = string; + } + AZStd::string_view Value::GetString() const { switch (m_value.index()) @@ -992,7 +1002,7 @@ namespace AZ::Dom } else if constexpr (AZStd::is_same_v>) { - result = visitor.String(*arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); + result = visitor.RefCountedString(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); } else if constexpr (AZStd::is_same_v) { diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 9399e10d15..ac6a9d0c51 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -127,6 +127,7 @@ namespace AZ::Dom Value(const Value&); Value(Value&&) noexcept; Value(AZStd::string_view string, bool copy); + Value(AZStd::shared_ptr string); Value(int32_t value); Value(uint32_t value); @@ -281,6 +282,7 @@ namespace AZ::Dom AZStd::string_view GetString() const; size_t GetStringLength() const; void SetString(AZStd::string_view); + void SetString(AZStd::shared_ptr); void CopyFromString(AZStd::string_view); // opaque type API... diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index cf46a77d54..a8cc1f3947 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -68,6 +68,12 @@ namespace AZ::Dom return FinishWrite(); } + Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr value, [[maybe_unused]] Lifetime lifetime) + { + CurrentValue().SetString(value); + return FinishWrite(); + } + Visitor::Result ValueWriter::StartObject() { CurrentValue().SetObject(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index 617184a926..4c1d5a26ee 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -26,6 +26,7 @@ namespace AZ::Dom Result Double(double value) override; Result String(AZStd::string_view value, Lifetime lifetime) override; + Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) override; Result StartObject() override; Result EndObject(AZ::u64 attributeCount) override; Result Key(AZ::Name key) override; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp index ad314da385..af64c4cea8 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp @@ -105,6 +105,11 @@ namespace AZ::Dom return VisitorSuccess(); } + Visitor::Result Visitor::RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) + { + return String(*value, lifetime); + } + Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime) { if (!SupportsOpaqueValues()) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h index bbe78131c3..3439a566e2 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace AZ::Dom { @@ -170,6 +171,7 @@ namespace AZ::Dom //! Operates on a string value. As strings are a reference type. //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy. virtual Result String(AZStd::string_view value, Lifetime lifetime); + virtual Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime); //! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to //! indicate where the value may be stored persistently or requires a copy. //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index 3e6143e805..98e62f4aa6 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -50,7 +50,7 @@ namespace Benchmark UnitTest::AllocatorsBenchmarkFixture::TearDown(st); } - AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength) + rapidjson::Document GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength) { rapidjson::Document document; document.SetObject(); @@ -108,6 +108,13 @@ namespace Benchmark document.SetObject(); document.AddMember("entries", createObject(), document.GetAllocator()); + return document; + } + + AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength) + { + rapidjson::Document document = GenerateDomJsonBenchmarkDocument(entryCount, stringTemplateLength); + AZStd::string serializedJson; auto result = AZ::JsonSerializationUtils::WriteJsonString(document, serializedJson); AZ_Assert(result.IsSuccess(), "Failed to serialize generated JSON"); @@ -124,7 +131,7 @@ namespace Benchmark ->Args({ 100, 500 }) \ ->Unit(benchmark::kMillisecond); - BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocumentInPlace)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)(benchmark::State& state) { AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); @@ -146,9 +153,9 @@ namespace Benchmark state.SetBytesProcessed(serializedPayload.size() * state.iterations()); } - BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocumentInPlace) + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace) - BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDomValueInPlace)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)(benchmark::State& state) { AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); @@ -170,9 +177,9 @@ namespace Benchmark state.SetBytesProcessed(serializedPayload.size() * state.iterations()); } - BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDomValueInPlace) + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace) - BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocument)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjson)(benchmark::State& state) { AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); @@ -190,9 +197,29 @@ namespace Benchmark state.SetBytesProcessed(serializedPayload.size() * state.iterations()); } - BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocument) + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjson) - BENCHMARK_DEFINE_F(DomJsonBenchmark, JsonUtilsDeserializeToDocument)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValue)(benchmark::State& state) + { + AZ::Dom::JsonBackend backend; + AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); + + for (auto _ : state) + { + auto result = AZ::Dom::Utils::WriteToValue( + [&](AZ::Dom::Visitor& visitor) + { + return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor); + }); + + benchmark::DoNotOptimize(result.GetValue()); + } + + state.SetBytesProcessed(serializedPayload.size() * state.iterations()); + } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValue) + + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)(benchmark::State& state) { AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); @@ -206,9 +233,9 @@ namespace Benchmark state.SetBytesProcessed(serializedPayload.size() * state.iterations()); } - BENCHMARK_REGISTER_JSON(DomJsonBenchmark, JsonUtilsDeserializeToDocument) + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeserializeToRapidjson) - BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonPayloadGeneration)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonMakeComplexObject)(benchmark::State& state) { for (auto _ : state) { @@ -217,7 +244,44 @@ namespace Benchmark state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); } - BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonPayloadGeneration) + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonMakeComplexObject) + + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonLookupMemberByString)(benchmark::State& state) + { + rapidjson::Document document(rapidjson::kObjectType); + AZStd::vector keys; + for (int64_t i = 0; i < state.range(0); ++i) + { + AZStd::string key(AZStd::string::format("key%" PRId64, i)); + keys.push_back(key); + document.AddMember(rapidjson::Value(key.data(), static_cast(key.size()), document.GetAllocator()), rapidjson::Value(i), document.GetAllocator()); + } + + for (auto _ : state) + { + for (const AZStd::string& key : keys) + { + benchmark::DoNotOptimize(document.FindMember(key.data())); + } + } + + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + BENCHMARK_REGISTER_F(DomJsonBenchmark, RapidjsonLookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond); + + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeepCopy)(benchmark::State& state) + { + rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1)); + + for (auto _ : state) + { + rapidjson::Document copy; + original.Accept(copy); + } + + state.SetItemsProcessed(state.iterations()); + } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeepCopy) #undef BENCHMARK_REGISTER_JSON } // namespace Benchmark diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index d11ef7f69e..e3a0db594c 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace AZ::Dom::Benchmark { @@ -99,7 +100,7 @@ namespace AZ::Dom::Benchmark } }; - BENCHMARK_DEFINE_F(DomValueBenchmark, ValuePayloadGeneration)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state) { for (auto _ : state) { @@ -108,10 +109,115 @@ namespace AZ::Dom::Benchmark state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); } - BENCHMARK_REGISTER_F(DomValueBenchmark, ValuePayloadGeneration) + BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueMakeComplexObject) ->Args({ 10, 5 }) ->Args({ 10, 500 }) ->Args({ 100, 5 }) ->Args({ 100, 500 }) ->Unit(benchmark::kMillisecond); + + BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopy)(benchmark::State& state) + { + Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); + + for (auto _ : state) + { + Value copy = original; + benchmark::DoNotOptimize(copy); + } + + state.SetItemsProcessed(state.iterations()); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopy) + ->Args({ 10, 5 }) + ->Args({ 10, 500 }) + ->Args({ 100, 5 }) + ->Args({ 100, 500 }) + ->Unit(benchmark::kNanosecond); + + BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopyAndMutate)(benchmark::State& state) + { + Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); + + for (auto _ : state) + { + Value copy = original; + copy["entries"]["Key0"].PushBack(42); + benchmark::DoNotOptimize(copy); + } + + state.SetItemsProcessed(state.iterations()); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopyAndMutate) + ->Args({ 10, 5 }) + ->Args({ 10, 500 }) + ->Args({ 100, 5 }) + ->Args({ 100, 500 }) + ->Unit(benchmark::kNanosecond); + + BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueDeepCopy)(benchmark::State& state) + { + Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); + + for (auto _ : state) + { + Value copy = original.DeepCopy(); + benchmark::DoNotOptimize(copy); + } + + state.SetItemsProcessed(state.iterations()); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueDeepCopy) + ->Args({ 10, 5 }) + ->Args({ 10, 500 }) + ->Args({ 100, 5 }) + ->Args({ 100, 500 }) + ->Unit(benchmark::kMillisecond); + + BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByName)(benchmark::State& state) + { + Value value(Type::ObjectType); + AZStd::vector keys; + for (int64_t i = 0; i < state.range(0); ++i) + { + AZ::Name key(AZStd::string::format("key%" PRId64, i)); + keys.push_back(key); + value[key] = i; + } + + for (auto _ : state) + { + for (const AZ::Name& key : keys) + { + benchmark::DoNotOptimize(value[key]); + } + } + + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByName)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond); + + BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByString)(benchmark::State& state) + { + Value value(Type::ObjectType); + AZStd::vector keys; + for (int64_t i = 0; i < state.range(0); ++i) + { + AZStd::string key(AZStd::string::format("key%" PRId64, i)); + keys.push_back(key); + value[key] = i; + } + + for (auto _ : state) + { + for (const AZStd::string& key : keys) + { + benchmark::DoNotOptimize(value[key]); + } + } + + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond); + } // namespace AZ::Dom::Benchmark From cc120c772cab1ba8a601ca063a85d7e9838b00c2 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Sun, 12 Dec 2021 12:58:52 -0800 Subject: [PATCH 11/49] Add RapidjsonCopyAndMutate to compare w/ shallow copy Signed-off-by: Nicholas Van Sickle --- .../AzCore/Tests/DOM/DomJsonBenchmarks.cpp | 20 ++++++++++++++++++- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index 98e62f4aa6..477802dc37 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -276,13 +276,31 @@ namespace Benchmark for (auto _ : state) { rapidjson::Document copy; - original.Accept(copy); + copy.CopyFrom(original, copy.GetAllocator(), true); + benchmark::DoNotOptimize(copy); } state.SetItemsProcessed(state.iterations()); } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeepCopy) + BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonCopyAndMutate)(benchmark::State& state) + { + rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1)); + + for (auto _ : state) + { + rapidjson::Document copy; + copy.CopyFrom(original, copy.GetAllocator(), true); + copy["entries"]["Key0"].PushBack(42, copy.GetAllocator()); + benchmark::DoNotOptimize(copy); + } + + state.SetItemsProcessed(state.iterations()); + } + BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonCopyAndMutate) + #undef BENCHMARK_REGISTER_JSON } // namespace Benchmark diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index e3a0db594c..3e02eaff2e 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -135,7 +135,7 @@ namespace AZ::Dom::Benchmark ->Args({ 100, 500 }) ->Unit(benchmark::kNanosecond); - BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopyAndMutate)(benchmark::State& state) + BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueCopyAndMutate)(benchmark::State& state) { Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); @@ -148,7 +148,7 @@ namespace AZ::Dom::Benchmark state.SetItemsProcessed(state.iterations()); } - BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopyAndMutate) + BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueCopyAndMutate) ->Args({ 10, 5 }) ->Args({ 10, 500 }) ->Args({ 100, 5 }) From d9ac3c21208442916f3345c01e73306a722beb82 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Sun, 12 Dec 2021 13:56:10 -0800 Subject: [PATCH 12/49] Tidy up Type enum Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 82 +++++++++---------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 18 ++-- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 12 +-- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 12 +-- .../AzCore/Tests/DOM/DomValueTests.cpp | 38 ++++----- 5 files changed, 81 insertions(+), 81 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index ed4488267a..59e21a7d8b 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -141,31 +141,31 @@ namespace AZ::Dom { switch (type) { - case Type::NullType: + case Type::Null: // Null is the default initialized value break; - case Type::FalseType: + case Type::False: m_value = false; break; - case Type::TrueType: + case Type::True: m_value = true; break; - case Type::ObjectType: + case Type::Object: SetObject(); break; - case Type::ArrayType: + case Type::Array: SetArray(); break; - case Type::StringType: + case Type::String: SetString(""); break; - case Type::NumberType: + case Type::Number: m_value = 0.0; break; - case Type::NodeType: + case Type::Node: SetNode(""); break; - case Type::OpaqueType: + case Type::Opaque: AZ_Assert(false, "AZ::Dom::Value may not be constructed with an empty opaque type"); break; } @@ -225,43 +225,43 @@ namespace AZ::Dom switch (m_value.index()) { case 0: // AZStd::monostate - return Type::NullType; + return Type::Null; case 1: // int64_t case 2: // uint64_t case 3: // double - return Type::NumberType; + return Type::Number; case 4: // bool - return AZStd::get(m_value) ? Type::TrueType : Type::FalseType; + return AZStd::get(m_value) ? Type::True : Type::False; case 5: // AZStd::string_view case 6: // AZStd::shared_ptr case 7: // ShortStringType - return Type::StringType; + return Type::String; case 8: // ObjectPtr - return Type::ObjectType; + return Type::Object; case 9: // ArrayPtr - return Type::ArrayType; + return Type::Array; case 10: // NodePtr - return Type::NodeType; + return Type::Node; case 11: // AZStd::any* - return Type::OpaqueType; + return Type::Opaque; } AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); - return Type::NullType; + return Type::Null; } bool Value::IsNull() const { - return GetType() == Type::NullType; + return GetType() == Type::Null; } bool Value::IsFalse() const { - return GetType() == Type::FalseType; + return GetType() == Type::False; } bool Value::IsTrue() const { - return GetType() == Type::TrueType; + return GetType() == Type::True; } bool Value::IsBool() const @@ -271,27 +271,27 @@ namespace AZ::Dom bool Value::IsNode() const { - return GetType() == Type::NodeType; + return GetType() == Type::Node; } bool Value::IsObject() const { - return GetType() == Type::ObjectType; + return GetType() == Type::Object; } bool Value::IsArray() const { - return GetType() == Type::ArrayType; + return GetType() == Type::Array; } bool Value::IsOpaqueValue() const { - return GetType() == Type::OpaqueType; + return GetType() == Type::Opaque; } bool Value::IsNumber() const { - return GetType() == Type::NumberType; + return GetType() == Type::Number; } bool Value::IsInt() const @@ -311,7 +311,7 @@ namespace AZ::Dom bool Value::IsString() const { - return GetType() == Type::StringType; + return GetType() == Type::String; } Value& Value::SetObject() @@ -322,13 +322,13 @@ namespace AZ::Dom const Node& Value::GetNodeInternal() const { - AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); + AZ_Assert(GetType() == Type::Node, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); return *AZStd::get(m_value); } Node& Value::GetNodeInternal() { - AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); + AZ_Assert(GetType() == Type::Node, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); return *CheckCopyOnWrite(AZStd::get(m_value)); } @@ -336,9 +336,9 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ObjectType || type == Type::NodeType, + type == Type::Object || type == Type::Node, "AZ::Dom::Value: attempted to retrieve an object from a value that isn't an object or a node"); - if (type == Type::ObjectType) + if (type == Type::Object) { return AZStd::get(m_value)->m_values; } @@ -352,9 +352,9 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ObjectType || type == Type::NodeType, + type == Type::Object || type == Type::Node, "AZ::Dom::Value: attempted to retrieve an object from a value that isn't an object or a node"); - if (type == Type::ObjectType) + if (type == Type::Object) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } @@ -368,9 +368,9 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ArrayType || type == Type::NodeType, + type == Type::Array || type == Type::Node, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or a node"); - if (type == Type::ArrayType) + if (type == Type::Array) { return AZStd::get(m_value)->m_values; } @@ -384,9 +384,9 @@ namespace AZ::Dom { const Type type = GetType(); AZ_Assert( - type == Type::ArrayType || type == Type::NodeType, + type == Type::Array || type == Type::Node, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or node"); - if (type == Type::ArrayType) + if (type == Type::Array) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } @@ -751,13 +751,13 @@ namespace AZ::Dom void Value::SetNodeValue(Value value) { - AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: Attempted to set value for non-node type"); + AZ_Assert(GetType() == Type::Node, "AZ::Dom::Value: Attempted to set value for non-node type"); Array::ContainerType& nodeChildren = GetArrayInternal(); // Set the first non-node child, if one is found for (Value& entry : nodeChildren) { - if (entry.GetType() != Type::NodeType) + if (entry.GetType() != Type::Node) { entry = AZStd::move(value); return; @@ -770,13 +770,13 @@ namespace AZ::Dom Value Value::GetNodeValue() const { - AZ_Assert(GetType() == Type::NodeType, "AZ::Dom::Value: Attempted to get value for non-node type"); + AZ_Assert(GetType() == Type::Node, "AZ::Dom::Value: Attempted to get value for non-node type"); const Array::ContainerType& nodeChildren = GetArrayInternal(); // Get the first non-node child, if one is found for (const Value& entry : nodeChildren) { - if (entry.GetType() != Type::NodeType) + if (entry.GetType() != Type::Node) { return entry; } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index ac6a9d0c51..274f6b1934 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -26,15 +26,15 @@ namespace AZ::Dom enum class Type { - NullType = 0, - FalseType = 1, - TrueType = 2, - ObjectType = 3, - ArrayType = 4, - StringType = 5, - NumberType = 6, - NodeType = 7, - OpaqueType = 8, + Null = 0, + False = 1, + True = 2, + Object = 3, + Array = 4, + String = 5, + Number = 6, + Node = 7, + Opaque = 8, }; class ValueAllocator final : public SimpleSchemaAllocator diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index a8cc1f3947..3e48c097c8 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -87,13 +87,13 @@ namespace AZ::Dom const char* endMethodName; switch (containerType) { - case Type::ObjectType: + case Type::Object: endMethodName = "EndObject"; break; - case Type::ArrayType: + case Type::Array: endMethodName = "EndArray"; break; - case Type::NodeType: + case Type::Node: endMethodName = "EndNode"; break; default: @@ -173,7 +173,7 @@ namespace AZ::Dom Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount) { - return EndContainer(Type::ObjectType, attributeCount, 0); + return EndContainer(Type::Object, attributeCount, 0); } Visitor::Result ValueWriter::Key(AZ::Name key) @@ -199,7 +199,7 @@ namespace AZ::Dom Visitor::Result ValueWriter::EndArray(AZ::u64 elementCount) { - return EndContainer(Type::ArrayType, 0, elementCount); + return EndContainer(Type::Array, 0, elementCount); } Visitor::Result ValueWriter::StartNode(AZ::Name name) @@ -217,7 +217,7 @@ namespace AZ::Dom Visitor::Result ValueWriter::EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) { - return EndContainer(Type::NodeType, attributeCount, elementCount); + return EndContainer(Type::Node, attributeCount, elementCount); } Visitor::Result ValueWriter::FinishWrite() diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index 3e02eaff2e..51d2e5c10c 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -46,7 +46,7 @@ namespace AZ::Dom::Benchmark Value GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength) { - Value root(Type::ObjectType); + Value root(Type::Object); AZStd::string entryTemplate; while (entryTemplate.size() < static_cast(stringTemplateLength)) @@ -63,18 +63,18 @@ namespace AZ::Dom::Benchmark auto createEntry = [&](int n) -> Value { - Value entry(Type::ObjectType); + Value entry(Type::Object); entry.AddMember("string", createString(n)); entry.AddMember("int", n); entry.AddMember("double", static_cast(n) * 0.5); entry.AddMember("bool", n % 2 == 0); - entry.AddMember("null", Value(Type::NullType)); + entry.AddMember("null", Value(Type::Null)); return entry; }; auto createArray = [&]() -> Value { - Value array(Type::ArrayType); + Value array(Type::Array); for (int i = 0; i < entryCount; ++i) { array.PushBack(createEntry(i)); @@ -176,7 +176,7 @@ namespace AZ::Dom::Benchmark BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByName)(benchmark::State& state) { - Value value(Type::ObjectType); + Value value(Type::Object); AZStd::vector keys; for (int64_t i = 0; i < state.range(0); ++i) { @@ -199,7 +199,7 @@ namespace AZ::Dom::Benchmark BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByString)(benchmark::State& state) { - Value value(Type::ObjectType); + Value value(Type::Object); AZStd::vector keys; for (int64_t i = 0; i < state.range(0); ++i) { diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 125f426e02..62f3ad7601 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -79,7 +79,7 @@ namespace AZ::Dom::Tests m_value.SetArray(); for (int j = 0; j < 5; ++j) { - Value nestedArray(Type::ArrayType); + Value nestedArray(Type::Array); for (int i = 0; i < 5; ++i) { nestedArray.PushBack(Value(i)); @@ -127,7 +127,7 @@ namespace AZ::Dom::Tests m_value.SetObject(); for (int j = 0; j < 3; ++j) { - Value nestedObject(Type::ObjectType); + Value nestedObject(Type::Object); for (int i = 0; i < 5; ++i) { nestedObject.AddMember(AZStd::string::format("Key%i", i), Value(i)); @@ -189,7 +189,7 @@ namespace AZ::Dom::Tests for (int i = 0; i < 5; ++i) { - Value childNode(Type::NodeType); + Value childNode(Type::Node); childNode.SetNodeName(childNodeName); childNode.SetNodeValue(i); @@ -218,9 +218,9 @@ namespace AZ::Dom::Tests m_value["int64_min"] = AZStd::numeric_limits::min(); m_value["int64_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["int64_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["int64_min"].GetType(), Type::Number); EXPECT_EQ(m_value["int64_min"].GetInt64(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["int64_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["int64_max"].GetType(), Type::Number); EXPECT_EQ(m_value["int64_max"].GetInt64(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -232,9 +232,9 @@ namespace AZ::Dom::Tests m_value["uint64_min"] = AZStd::numeric_limits::min(); m_value["uint64_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["uint64_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["uint64_min"].GetType(), Type::Number); EXPECT_EQ(m_value["uint64_min"].GetInt64(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["uint64_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["uint64_max"].GetType(), Type::Number); EXPECT_EQ(m_value["uint64_max"].GetInt64(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -246,9 +246,9 @@ namespace AZ::Dom::Tests m_value["double_min"] = AZStd::numeric_limits::min(); m_value["double_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["double_min"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["double_min"].GetType(), Type::Number); EXPECT_EQ(m_value["double_min"].GetDouble(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["double_max"].GetType(), Type::NumberType); + EXPECT_EQ(m_value["double_max"].GetType(), Type::Number); EXPECT_EQ(m_value["double_max"].GetDouble(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -257,9 +257,9 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, Null) { m_value.SetObject(); - m_value["null_value"] = Value(Type::NullType); + m_value["null_value"] = Value(Type::Null); - EXPECT_EQ(m_value["null_value"].GetType(), Type::NullType); + EXPECT_EQ(m_value["null_value"].GetType(), Type::Null); EXPECT_EQ(m_value["null_type"], Value()); PerformValueChecks(); @@ -271,9 +271,9 @@ namespace AZ::Dom::Tests m_value["true_value"] = true; m_value["false_value"] = false; - EXPECT_EQ(m_value["true_value"].GetType(), Type::TrueType); + EXPECT_EQ(m_value["true_value"].GetType(), Type::True); EXPECT_EQ(m_value["true_value"].GetBool(), true); - EXPECT_EQ(m_value["false_value"].GetType(), Type::FalseType); + EXPECT_EQ(m_value["false_value"].GetType(), Type::False); EXPECT_EQ(m_value["false_value"].GetBool(), false); PerformValueChecks(); @@ -290,12 +290,12 @@ namespace AZ::Dom::Tests AZStd::string stringToCopy = s2; m_value["copy"] = Value(stringToCopy, true); - EXPECT_EQ(m_value["no_copy"].GetType(), Type::StringType); + EXPECT_EQ(m_value["no_copy"].GetType(), Type::String); EXPECT_EQ(m_value["no_copy"].GetString(), s1); stringToReference.at(0) = 'F'; EXPECT_NE(m_value["no_copy"].GetString(), s1); - EXPECT_EQ(m_value["copy"].GetType(), Type::StringType); + EXPECT_EQ(m_value["copy"].GetType(), Type::String); EXPECT_EQ(m_value["copy"].GetString(), s2); stringToCopy.at(0) = 'F'; EXPECT_EQ(m_value["copy"].GetString(), s2); @@ -305,10 +305,10 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, CopyOnWrite_Object) { - Value v1(Type::ObjectType); + Value v1(Type::Object); v1["foo"] = 5; - Value nestedObject(Type::ObjectType); + Value nestedObject(Type::Object); v1["obj"] = nestedObject; Value v2 = v1; @@ -333,11 +333,11 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, CopyOnWrite_Array) { - Value v1(Type::ArrayType); + Value v1(Type::Array); v1.PushBack(1); v1.PushBack(2); - Value nestedArray(Type::ArrayType); + Value nestedArray(Type::Array); v1.PushBack(nestedArray); Value v2 = v1; From e080ade885d84fa0bd0852b705dd46670b5e78bf Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Sun, 12 Dec 2021 14:20:16 -0800 Subject: [PATCH 13/49] Switch high perf allocator back on for containers, modest perf boost Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 274f6b1934..4b09068ec4 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -24,6 +24,7 @@ namespace AZ::Dom { using KeyType = AZ::Name; + //! The type of underlying value stored in a value. \see Value enum class Type { Null = 0, @@ -37,6 +38,9 @@ namespace AZ::Dom Opaque = 8, }; + //! The allocator used by Value. + //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside the vector + //! contents of its container storage. class ValueAllocator final : public SimpleSchemaAllocator { public: @@ -56,7 +60,7 @@ namespace AZ::Dom class Array { public: - using ContainerType = AZStd::vector; + using ContainerType = AZStd::vector>; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; static constexpr const size_t ReserveIncrement = 4; @@ -74,7 +78,7 @@ namespace AZ::Dom { public: using EntryType = AZStd::pair; - using ContainerType = AZStd::vector; + using ContainerType = AZStd::vector>; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; static constexpr const size_t ReserveIncrement = 8; From 0216d0ae9f75927491feab2effa0e2273374fbc3 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 09:11:06 -0800 Subject: [PATCH 14/49] Clarify shared string semantics Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 16 +++++----- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 30 +++++++------------ .../AzCore/AzCore/DOM/DomValueWriter.cpp | 2 +- .../AzCore/AzCore/DOM/DomValueWriter.h | 2 +- .../AzCore/AzCore/DOM/DomVisitor.cpp | 2 +- Code/Framework/AzCore/AzCore/DOM/DomVisitor.h | 11 +++++-- 6 files changed, 30 insertions(+), 33 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 59e21a7d8b..72eb7ebadd 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -61,7 +61,7 @@ namespace AZ::Dom return m_children; } - Value::Value(AZStd::shared_ptr string) + Value::Value(AZStd::shared_ptr string) : m_value(string) { } @@ -233,7 +233,7 @@ namespace AZ::Dom case 4: // bool return AZStd::get(m_value) ? Type::True : Type::False; case 5: // AZStd::string_view - case 6: // AZStd::shared_ptr + case 6: // AZStd::shared_ptr case 7: // ShortStringType return Type::String; case 8: // ObjectPtr @@ -900,7 +900,7 @@ namespace AZ::Dom m_value = aznumeric_cast(value); } - void Value::SetString(AZStd::shared_ptr string) + void Value::SetString(AZStd::shared_ptr string) { m_value = string; } @@ -911,8 +911,8 @@ namespace AZ::Dom { case 5: // AZStd::string_view return AZStd::get(m_value); - case 6: // AZStd::shared_ptr - return *AZStd::get>(m_value); + case 6: // AZStd::shared_ptr + return *AZStd::get>(m_value); case 7: // ShortStringType { const ShortStringType& ShortString = AZStd::get(m_value); @@ -948,7 +948,7 @@ namespace AZ::Dom } else { - m_value = AZStd::allocate_shared(AZStdAlloc(), value); + m_value = AZStd::allocate_shared(AZStdAlloc(), value); } } @@ -1000,7 +1000,7 @@ namespace AZ::Dom { result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); } - else if constexpr (AZStd::is_same_v>) + else if constexpr (AZStd::is_same_v>) { result = visitor.RefCountedString(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); } @@ -1096,7 +1096,7 @@ namespace AZ::Dom if (IsString() && other.IsString()) { // If we both hold the same ref counted string we don't need to do a full comparison - if (AZStd::holds_alternative>(m_value) && m_value == other.m_value) + if (AZStd::holds_alternative>(m_value) && m_value == other.m_value) { return true; } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 4b09068ec4..1a09b9a3c8 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -39,8 +39,7 @@ namespace AZ::Dom }; //! The allocator used by Value. - //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside the vector - //! contents of its container storage. + //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside class ValueAllocator final : public SimpleSchemaAllocator { public: @@ -131,7 +130,7 @@ namespace AZ::Dom Value(const Value&); Value(Value&&) noexcept; Value(AZStd::string_view string, bool copy); - Value(AZStd::shared_ptr string); + Value(AZStd::shared_ptr string); Value(int32_t value); Value(uint32_t value); @@ -242,10 +241,6 @@ namespace AZ::Dom const Array::ContainerType& GetArray() const; // Node API (supports both object + array API, plus a dedicated NodeName)... - // bool CanConvertToNodeFromObject() const; - // Value& ConvertToNodeFromObject(); - // Value& ConvertToObjectFromNode(); - void SetNode(AZ::Name name); void SetNode(AZStd::string_view name); @@ -282,14 +277,14 @@ namespace AZ::Dom float GetFloat() const; void SetFloat(float); - // string API... + // String API... AZStd::string_view GetString() const; size_t GetStringLength() const; void SetString(AZStd::string_view); - void SetString(AZStd::shared_ptr); + void SetString(AZStd::shared_ptr); void CopyFromString(AZStd::string_view); - // opaque type API... + // Opaque type API... AZStd::any& GetOpaqueValue() const; //! This sets this Value to represent a value of an type that the DOM has //! no formal knowledge of. Where possible, it should be preferred to @@ -298,10 +293,10 @@ namespace AZ::Dom //! values. void SetOpaqueValue(AZStd::any&); - // null API... + // Null API... void SetNull(); - // Visitor API + // Visitor API... Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; AZStd::unique_ptr GetWriteHandler(); @@ -330,12 +325,9 @@ namespace AZ::Dom } }; - // If using the the copy on write model, anything stored internally as a shared_ptr will - // detach and copy when doing a mutating operation if use_count() > 1. - - // This internal storage will not have a 1:1 mapping to the public Type, as there may be - // multiple storage options (e.g. strings being stored as non-owning string_view or - // owning shared_ptr) + //! The internal storage type for Value. + //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes + //! for the same type in some instances, such as string storage using ValueType = AZStd::variant< // NullType AZStd::monostate, @@ -347,7 +339,7 @@ namespace AZ::Dom bool, // StringType AZStd::string_view, - AZStd::shared_ptr, + AZStd::shared_ptr, ShortStringType, // ObjectType ObjectPtr, diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 3e48c097c8..ef99809290 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -68,7 +68,7 @@ namespace AZ::Dom return FinishWrite(); } - Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr value, [[maybe_unused]] Lifetime lifetime) + Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr value, [[maybe_unused]] Lifetime lifetime) { CurrentValue().SetString(value); return FinishWrite(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index 4c1d5a26ee..6086ff4fe5 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -26,7 +26,7 @@ namespace AZ::Dom Result Double(double value) override; Result String(AZStd::string_view value, Lifetime lifetime) override; - Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) override; + Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) override; Result StartObject() override; Result EndObject(AZ::u64 attributeCount) override; Result Key(AZ::Name key) override; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp index af64c4cea8..5e3d6c2882 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp @@ -105,7 +105,7 @@ namespace AZ::Dom return VisitorSuccess(); } - Visitor::Result Visitor::RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) + Visitor::Result Visitor::RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) { return String(*value, lifetime); } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h index 3439a566e2..938df1bb5a 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h @@ -168,10 +168,15 @@ namespace AZ::Dom virtual Result Uint64(AZ::u64 value); //! Operates on a double precision, 64 bit floating point value. virtual Result Double(double value); - //! Operates on a string value. As strings are a reference type. - //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy. + //! Operates on a string value. As strings are a reference type, + //! storage semantics are provided to indicate where the value may be stored persistently or requires a copy. + //! \param lifetime Specifies the lifetime of this string - if the string has a temporary lifetime, it cannot + //! safely be stored as a reference. virtual Result String(AZStd::string_view value, Lifetime lifetime); - virtual Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime); + //! Operates on a ref-counted string value. S + //! \param lifetime Specifies the lifetime of this string. If the string has a temporary lifetime, it may not + //! be safely stored as a reference, but may still be safely stored as a ref-counted shared_ptr. + virtual Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime); //! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to //! indicate where the value may be stored persistently or requires a copy. //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special From 8da04f15d3a7e7c1b02e0d3431fe0bd90dfb481c Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 11:15:02 -0800 Subject: [PATCH 15/49] Add some Value documentation Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 36 +++++++++---- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 50 +++++++++++++------ .../AzCore/Tests/DOM/DomValueTests.cpp | 16 +++--- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 72eb7ebadd..dcecc67460 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -144,12 +144,9 @@ namespace AZ::Dom case Type::Null: // Null is the default initialized value break; - case Type::False: + case Type::Bool: m_value = false; break; - case Type::True: - m_value = true; - break; case Type::Object: SetObject(); break; @@ -159,8 +156,14 @@ namespace AZ::Dom case Type::String: SetString(""); break; - case Type::Number: - m_value = 0.0; + case Type::Int64: + m_value = int64_t{}; + break; + case Type::Uint64: + m_value = uint64_t{}; + break; + case Type::Double: + m_value = double{}; break; case Type::Node: SetNode(""); @@ -227,11 +230,13 @@ namespace AZ::Dom case 0: // AZStd::monostate return Type::Null; case 1: // int64_t + return Type::Int64; case 2: // uint64_t + return Type::Uint64; case 3: // double - return Type::Number; + return Type::Double; case 4: // bool - return AZStd::get(m_value) ? Type::True : Type::False; + return Type::Bool; case 5: // AZStd::string_view case 6: // AZStd::shared_ptr case 7: // ShortStringType @@ -256,12 +261,12 @@ namespace AZ::Dom bool Value::IsFalse() const { - return GetType() == Type::False; + return IsBool() && !AZStd::get(m_value); } bool Value::IsTrue() const { - return GetType() == Type::True; + return IsBool() && AZStd::get(m_value); } bool Value::IsBool() const @@ -291,7 +296,16 @@ namespace AZ::Dom bool Value::IsNumber() const { - return GetType() == Type::Number; + switch (GetType()) + { + case Type::Int64: + [[fallthrough]]; + case Type::Uint64: + [[fallthrough]]; + case Type::Double: + return true; + } + return false; } bool Value::IsInt() const diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 1a09b9a3c8..7ba73a8a4b 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -10,15 +10,15 @@ #include #include +#include #include #include -#include +#include #include #include #include #include #include -#include namespace AZ::Dom { @@ -27,19 +27,20 @@ namespace AZ::Dom //! The type of underlying value stored in a value. \see Value enum class Type { - Null = 0, - False = 1, - True = 2, - Object = 3, - Array = 4, - String = 5, - Number = 6, - Node = 7, - Opaque = 8, + Null, + Bool, + Object, + Array, + String, + Int64, + Uint64, + Double, + Node, + Opaque, }; //! The allocator used by Value. - //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside + //! Value heap allocates shared_ptrs for its container storage (Array / Object / Node) alongside class ValueAllocator final : public SimpleSchemaAllocator { public: @@ -56,6 +57,7 @@ namespace AZ::Dom class Value; + //! Internal storage for a Value array: an ordered list of Values. class Array { public: @@ -73,6 +75,7 @@ namespace AZ::Dom using ArrayPtr = AZStd::shared_ptr; using ConstArrayPtr = AZStd::shared_ptr; + //! Internal storage for a Value object: an ordered list of Name / Value pairs. class Object { public: @@ -91,6 +94,9 @@ namespace AZ::Dom using ObjectPtr = AZStd::shared_ptr; using ConstObjectPtr = AZStd::shared_ptr; + //! Storage for a Value node: a named Value with both properties and children. + //! Properties are stored as an ordered list of Name / Value pairs. + //! Children are stored as an oredered list of Values. class Node { public: @@ -122,6 +128,21 @@ namespace AZ::Dom using NodePtr = AZStd::shared_ptr; using ConstNodePtr = AZStd::shared_ptr; + //! Value is a typed union of Dom types that can represent the types provdied by AZ::Dom::Visitor. + //! Value can be one of the following types: + //! - Null: a type with no value, this is the default type for Value + //! - Bool: a true or false boolean value + //! - Object: a container with an ordered list of Name/Value pairs, analagous to a JSON object + //! - Array: a container with an ordered list of Values, analagous to a JSON array + //! - String: a UTF-8 string + //! - Int64: a signed, 64-bit integer + //! - Uint64: an unsigned, 64-bit integer + //! - Double: a double precision floating point value + //! - Node: a container with a Name, an ordered list of Name/Values pairs (attributes), and an ordered list of Values (children), + //! analagous to an XML node + //! - Opaque: an arbitrary value stored in an AZStd::any. This is a non-serializable representation of an entry used only for in-memory + //! options. This is intended to be used as an intermediate value over the course of DOM transformation and as a proxy to pass through + //! types of which the DOM has no knowledge to other systems. class Value { public: @@ -327,7 +348,7 @@ namespace AZ::Dom //! The internal storage type for Value. //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes - //! for the same type in some instances, such as string storage + //! for the same type in some instances, such as string storage using ValueType = AZStd::variant< // NullType AZStd::monostate, @@ -351,8 +372,7 @@ namespace AZ::Dom AZStd::any*>; static_assert( - sizeof(ValueType) == sizeof(AZStd::variant), - "ValueType should have no members larger than ShortStringType"); + sizeof(ValueType) == sizeof(AZStd::variant), "ValueType should have no members larger than ShortStringType"); ValueType m_value; }; diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 62f3ad7601..b88ab746b9 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -218,9 +218,9 @@ namespace AZ::Dom::Tests m_value["int64_min"] = AZStd::numeric_limits::min(); m_value["int64_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["int64_min"].GetType(), Type::Number); + EXPECT_EQ(m_value["int64_min"].GetType(), Type::Int64); EXPECT_EQ(m_value["int64_min"].GetInt64(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["int64_max"].GetType(), Type::Number); + EXPECT_EQ(m_value["int64_max"].GetType(), Type::Int64); EXPECT_EQ(m_value["int64_max"].GetInt64(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -232,9 +232,9 @@ namespace AZ::Dom::Tests m_value["uint64_min"] = AZStd::numeric_limits::min(); m_value["uint64_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["uint64_min"].GetType(), Type::Number); + EXPECT_EQ(m_value["uint64_min"].GetType(), Type::Uint64); EXPECT_EQ(m_value["uint64_min"].GetInt64(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["uint64_max"].GetType(), Type::Number); + EXPECT_EQ(m_value["uint64_max"].GetType(), Type::Uint64); EXPECT_EQ(m_value["uint64_max"].GetInt64(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -246,9 +246,9 @@ namespace AZ::Dom::Tests m_value["double_min"] = AZStd::numeric_limits::min(); m_value["double_max"] = AZStd::numeric_limits::max(); - EXPECT_EQ(m_value["double_min"].GetType(), Type::Number); + EXPECT_EQ(m_value["double_min"].GetType(), Type::Double); EXPECT_EQ(m_value["double_min"].GetDouble(), AZStd::numeric_limits::min()); - EXPECT_EQ(m_value["double_max"].GetType(), Type::Number); + EXPECT_EQ(m_value["double_max"].GetType(), Type::Double); EXPECT_EQ(m_value["double_max"].GetDouble(), AZStd::numeric_limits::max()); PerformValueChecks(); @@ -271,9 +271,9 @@ namespace AZ::Dom::Tests m_value["true_value"] = true; m_value["false_value"] = false; - EXPECT_EQ(m_value["true_value"].GetType(), Type::True); + EXPECT_EQ(m_value["true_value"].GetType(), Type::Bool); EXPECT_EQ(m_value["true_value"].GetBool(), true); - EXPECT_EQ(m_value["false_value"].GetType(), Type::False); + EXPECT_EQ(m_value["false_value"].GetType(), Type::Bool); EXPECT_EQ(m_value["false_value"].GetBool(), false); PerformValueChecks(); From d044f51a8913f60666e379fe143affeaa3dc79a6 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 11:21:38 -0800 Subject: [PATCH 16/49] Remove DomDocument for now Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp | 0 Code/Framework/AzCore/AzCore/DOM/DomDocument.h | 0 Code/Framework/AzCore/AzCore/azcore_files.cmake | 2 -- 3 files changed, 2 deletions(-) delete mode 100644 Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp delete mode 100644 Code/Framework/AzCore/AzCore/DOM/DomDocument.h diff --git a/Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp b/Code/Framework/AzCore/AzCore/DOM/DomDocument.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Code/Framework/AzCore/AzCore/DOM/DomDocument.h b/Code/Framework/AzCore/AzCore/DOM/DomDocument.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index e17fb941b8..ef2511bddc 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -127,8 +127,6 @@ set(FILES Debug/TraceReflection.h DOM/DomBackend.cpp DOM/DomBackend.h - DOM/DomDocument.cpp - DOM/DomDocument.h DOM/DomUtils.cpp DOM/DomUtils.h DOM/DomValue.cpp From 202226c2cb616dd62c3353ee3adfed805ae2ab60 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 11:31:23 -0800 Subject: [PATCH 17/49] Document short string usage Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 7ba73a8a4b..5cb67123e5 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -334,6 +334,8 @@ namespace AZ::Dom explicit Value(AZStd::any* opaqueValue); + // Determine the short string buffer size based on the size of our largest internal type (string_view) + // minus the size of the short string size field. static constexpr const size_t ShortStringSize = sizeof(AZStd::string_view) - sizeof(size_t); struct ShortStringType { @@ -348,7 +350,7 @@ namespace AZ::Dom //! The internal storage type for Value. //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes - //! for the same type in some instances, such as string storage + //! for the same type in some instances, such as string storage. using ValueType = AZStd::variant< // NullType AZStd::monostate, From af91cea8ef25280af54c4d37dadcdb073bca50c7 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 11:56:53 -0800 Subject: [PATCH 18/49] Add copy-on-write note Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 5cb67123e5..ecaea3db3d 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -143,6 +143,10 @@ namespace AZ::Dom //! - Opaque: an arbitrary value stored in an AZStd::any. This is a non-serializable representation of an entry used only for in-memory //! options. This is intended to be used as an intermediate value over the course of DOM transformation and as a proxy to pass through //! types of which the DOM has no knowledge to other systems. + //! \note Value is a copy-on-write data structure and may be cheaply returned by value. Heap allocated data larger than the size of the + //! value itself (objects, arrays, and nodes) are copied by new Values only when their contents change, so care should be taken in + //! performance critical code to avoid mutation operations such as operator[] to avoid copies. It is recommended that an immutable Value + //! be explicitly be stored as a `const Value` to avoid accidental detach and copy operations. class Value { public: From 946a77e9144c582f99f024c88f7831191a1150f9 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 12:06:24 -0800 Subject: [PATCH 19/49] Document ValueWriter, add basic OpaqueValue handling Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp | 6 ++++++ Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h | 6 ++++++ Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp | 2 +- Code/Framework/AzCore/AzCore/DOM/DomVisitor.h | 3 +-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index ef99809290..172f7f2a92 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -220,6 +220,12 @@ namespace AZ::Dom return EndContainer(Type::Node, attributeCount, elementCount); } + Visitor::Result ValueWriter::OpaqueValue(OpaqueType& value) + { + CurrentValue().SetOpaqueValue(value); + return FinishWrite(); + } + Visitor::Result ValueWriter::FinishWrite() { if (m_entryStack.empty()) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index 6086ff4fe5..7430ff910b 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -13,6 +13,8 @@ namespace AZ::Dom { + //! Visitor that writes to a Value. + //! Supports all Visitor operations. class ValueWriter : public Visitor { public: @@ -36,6 +38,7 @@ namespace AZ::Dom Result StartNode(AZ::Name name) override; Result RawStartNode(AZStd::string_view name, Lifetime lifetime) override; Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount) override; + Result OpaqueValue(OpaqueType& value) override; private: Result FinishWrite(); @@ -60,7 +63,10 @@ namespace AZ::Dom ValueBuffer& GetValueBuffer(); Value& m_result; + // Stores info about the current value being processed AZStd::stack m_entryStack; + // Provides temporary storage for elements and attributes to prevent extra heap allocations + // These buffers persist to be reused even as the entry stack changes AZStd::vector m_valueBuffers; }; } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp index 5e3d6c2882..4e13a2a95c 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp @@ -110,7 +110,7 @@ namespace AZ::Dom return String(*value, lifetime); } - Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime) + Visitor::Result Visitor::OpaqueValue([[maybe_unused]] OpaqueType& value) { if (!SupportsOpaqueValues()) { diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h index 938df1bb5a..3bf1b3419b 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h @@ -181,8 +181,7 @@ namespace AZ::Dom //! indicate where the value may be stored persistently or requires a copy. //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special //! cases with specific implementations, not generic usage. - //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy. - virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime); + virtual Result OpaqueValue(OpaqueType& value); //! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced. //! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and //! forward it to the corresponding value call or calls of their choice. From aa9799a63b0b9c413e5f1431a299d41275648987 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 13 Dec 2021 15:05:17 -0800 Subject: [PATCH 20/49] Use the high perf allocator for DomValueWriter Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index 7430ff910b..488535ace6 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -56,17 +56,17 @@ namespace AZ::Dom struct ValueBuffer { - AZStd::vector m_elements; - AZStd::vector> m_attributes; + Array::ContainerType m_elements; + Object::ContainerType m_attributes; }; ValueBuffer& GetValueBuffer(); Value& m_result; // Stores info about the current value being processed - AZStd::stack m_entryStack; + AZStd::stack>> m_entryStack; // Provides temporary storage for elements and attributes to prevent extra heap allocations // These buffers persist to be reused even as the entry stack changes - AZStd::vector m_valueBuffers; + AZStd::vector> m_valueBuffers; }; } // namespace AZ::Dom From 352b4ab6907eb9bf7890bd4670c34a1781d12409 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 15 Dec 2021 10:23:58 -0800 Subject: [PATCH 21/49] Make DomValue final Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index ecaea3db3d..1b69c73ecc 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -147,7 +147,7 @@ namespace AZ::Dom //! value itself (objects, arrays, and nodes) are copied by new Values only when their contents change, so care should be taken in //! performance critical code to avoid mutation operations such as operator[] to avoid copies. It is recommended that an immutable Value //! be explicitly be stored as a `const Value` to avoid accidental detach and copy operations. - class Value + class Value final { public: // Constructors... From bbd00adadeaf315a615b1e08259f44763a5272fb Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 20 Dec 2021 16:54:06 -0800 Subject: [PATCH 22/49] Address some AZ::Dom::Value feedback - Use a vector for shared string storage (to avoid the double heap allocation for AZStd::string) - Use a shared heap allocated any for opaque types (instead of an unsafe ref) - Add a string comparison key lookup benchmark to measure the impact of Name Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 49 ++++++++------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 59 +++++++++---------- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 2 +- .../AzCore/AzCore/DOM/DomValueWriter.h | 2 +- .../AzCore/AzCore/DOM/DomVisitor.cpp | 4 +- Code/Framework/AzCore/AzCore/DOM/DomVisitor.h | 5 +- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 29 +++++++++ 7 files changed, 91 insertions(+), 59 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index dcecc67460..3dafc02c6e 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -19,7 +19,7 @@ namespace AZ::Dom { if (refCountedPointer.use_count() > 1) { - AZStd::shared_ptr newPointer = AZStd::allocate_shared(AZStdAlloc()); + AZStd::shared_ptr newPointer = AZStd::allocate_shared(StdValueAllocator()); *newPointer = *refCountedPointer; refCountedPointer = AZStd::move(newPointer); } @@ -92,12 +92,12 @@ namespace AZ::Dom } } - Value::Value(AZStd::any* value) - : m_value(value) + Value::Value(const AZStd::any& value) + : m_value(AZStd::allocate_shared(StdValueAllocator(), value)) { } - Value Value::FromOpaqueValue(AZStd::any& value) + Value Value::FromOpaqueValue(const AZStd::any& value) { return Value(&value); } @@ -330,7 +330,7 @@ namespace AZ::Dom Value& Value::SetObject() { - m_value = AZStd::allocate_shared(AZStdAlloc()); + m_value = AZStd::allocate_shared(StdValueAllocator()); return *this; } @@ -634,7 +634,7 @@ namespace AZ::Dom Value& Value::SetArray() { - m_value = AZStd::allocate_shared(AZStdAlloc()); + m_value = AZStd::allocate_shared(StdValueAllocator()); return *this; } @@ -740,7 +740,7 @@ namespace AZ::Dom void Value::SetNode(AZ::Name name) { - m_value = AZStd::allocate_shared(AZStdAlloc(), name); + m_value = AZStd::allocate_shared(StdValueAllocator(), name); } void Value::SetNode(AZStd::string_view name) @@ -914,9 +914,9 @@ namespace AZ::Dom m_value = aznumeric_cast(value); } - void Value::SetString(AZStd::shared_ptr string) + void Value::SetString(SharedStringType sharedString) { - m_value = string; + m_value = sharedString; } AZStd::string_view Value::GetString() const @@ -925,12 +925,15 @@ namespace AZ::Dom { case 5: // AZStd::string_view return AZStd::get(m_value); - case 6: // AZStd::shared_ptr - return *AZStd::get>(m_value); + case 6: // AZStd::shared_ptr> + { + auto& buffer = *AZStd::get(m_value); + return { buffer.data(), buffer.size() }; + } case 7: // ShortStringType { - const ShortStringType& ShortString = AZStd::get(m_value); - return { ShortString.m_data.data(), ShortString.m_size }; + const ShortStringType& shortString = AZStd::get(m_value); + return { shortString.data(), shortString.size() }; } } AZ_Assert(false, "AZ::Dom::Value: Called GetString on a non-string type"); @@ -947,8 +950,8 @@ namespace AZ::Dom if (value.size() <= ShortStringSize) { ShortStringType buffer; - buffer.m_size = value.size(); - memcpy(buffer.m_data.data(), value.data(), buffer.m_size); + buffer.resize_no_construct(value.size()); + memcpy(buffer.data(), value.data(), value.size()); m_value = buffer; } m_value = value; @@ -962,18 +965,20 @@ namespace AZ::Dom } else { - m_value = AZStd::allocate_shared(AZStdAlloc(), value); + SharedStringType sharedString = + AZStd::allocate_shared(StdValueAllocator(), value.begin(), value.end()); + m_value = AZStd::move(sharedString); } } - AZStd::any& Value::GetOpaqueValue() const + const AZStd::any& Value::GetOpaqueValue() const { - return *AZStd::get(m_value); + return *AZStd::get>(m_value); } - void Value::SetOpaqueValue(AZStd::any& value) + void Value::SetOpaqueValue(const AZStd::any& value) { - m_value = &value; + m_value = AZStd::allocate_shared(StdValueAllocator(), value); } void Value::SetNull() @@ -1014,7 +1019,7 @@ namespace AZ::Dom { result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); } - else if constexpr (AZStd::is_same_v>) + else if constexpr (AZStd::is_same_v) { result = visitor.RefCountedString(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent); } @@ -1110,7 +1115,7 @@ namespace AZ::Dom if (IsString() && other.IsString()) { // If we both hold the same ref counted string we don't need to do a full comparison - if (AZStd::holds_alternative>(m_value) && m_value == other.m_value) + if (AZStd::holds_alternative(m_value) && m_value == other.m_value) { return true; } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 1b69c73ecc..bc1b790009 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -55,13 +55,15 @@ namespace AZ::Dom } }; + using StdValueAllocator = AZStdAlloc; + class Value; //! Internal storage for a Value array: an ordered list of Values. class Array { public: - using ContainerType = AZStd::vector>; + using ContainerType = AZStd::vector; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; static constexpr const size_t ReserveIncrement = 4; @@ -80,7 +82,7 @@ namespace AZ::Dom { public: using EntryType = AZStd::pair; - using ContainerType = AZStd::vector>; + using ContainerType = AZStd::vector; using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; static constexpr const size_t ReserveIncrement = 8; @@ -150,6 +152,13 @@ namespace AZ::Dom class Value final { public: + // Determine the short string buffer size based on the size of our largest internal type (string_view) + // minus the size of the short string size field. + static constexpr const size_t ShortStringSize = sizeof(AZStd::string_view) - 2; + using ShortStringType = AZStd::fixed_string; + using SharedStringContainer = AZStd::vector; + using SharedStringType = AZStd::shared_ptr; + // Constructors... Value(); Value(const Value&); @@ -167,7 +176,7 @@ namespace AZ::Dom explicit Value(Type type); - static Value FromOpaqueValue(AZStd::any& value); + static Value FromOpaqueValue(const AZStd::any& value); // Equality / comparison / swap... Value& operator=(const Value&); @@ -306,17 +315,17 @@ namespace AZ::Dom AZStd::string_view GetString() const; size_t GetStringLength() const; void SetString(AZStd::string_view); - void SetString(AZStd::shared_ptr); + void SetString(SharedStringType sharedString); void CopyFromString(AZStd::string_view); // Opaque type API... - AZStd::any& GetOpaqueValue() const; + const AZStd::any& GetOpaqueValue() const; //! This sets this Value to represent a value of an type that the DOM has //! no formal knowledge of. Where possible, it should be preferred to //! serialize an opaque type into a DOM value instead, as serializers //! and other systems will have no means of dealing with fully arbitrary //! values. - void SetOpaqueValue(AZStd::any&); + void SetOpaqueValue(const AZStd::any&); // Null API... void SetNull(); @@ -336,49 +345,37 @@ namespace AZ::Dom const Array::ContainerType& GetArrayInternal() const; Array::ContainerType& GetArrayInternal(); - explicit Value(AZStd::any* opaqueValue); - - // Determine the short string buffer size based on the size of our largest internal type (string_view) - // minus the size of the short string size field. - static constexpr const size_t ShortStringSize = sizeof(AZStd::string_view) - sizeof(size_t); - struct ShortStringType - { - AZStd::array m_data; - size_t m_size; - - bool operator==(const ShortStringType& other) const - { - return m_size == other.m_size ? memcmp(m_data.data(), other.m_data.data(), m_size) == 0 : false; - } - }; + explicit Value(const AZStd::any& opaqueValue); //! The internal storage type for Value. //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes //! for the same type in some instances, such as string storage. using ValueType = AZStd::variant< - // NullType + // Null AZStd::monostate, - // NumberType + // Int64 int64_t, + // Uint64 uint64_t, + // Double double, - // FalseType & TrueType + // Bool bool, // StringType AZStd::string_view, - AZStd::shared_ptr, + SharedStringType, ShortStringType, - // ObjectType + // Object ObjectPtr, - // ArrayType + // Array ArrayPtr, - // NodeType + // Node NodePtr, - // OpaqueType - AZStd::any*>; + // Opaque + AZStd::shared_ptr>; static_assert( - sizeof(ValueType) == sizeof(AZStd::variant), "ValueType should have no members larger than ShortStringType"); + sizeof(ValueType) == sizeof(ShortStringType) + sizeof(size_t), "ValueType should have no members larger than ShortStringType"); ValueType m_value; }; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 172f7f2a92..9b814b1bff 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -68,7 +68,7 @@ namespace AZ::Dom return FinishWrite(); } - Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr value, [[maybe_unused]] Lifetime lifetime) + Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr> value, [[maybe_unused]] Lifetime lifetime) { CurrentValue().SetString(value); return FinishWrite(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h index 488535ace6..4fdea293eb 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.h @@ -28,7 +28,7 @@ namespace AZ::Dom Result Double(double value) override; Result String(AZStd::string_view value, Lifetime lifetime) override; - Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) override; + Result RefCountedString(AZStd::shared_ptr> value, Lifetime lifetime) override; Result StartObject() override; Result EndObject(AZ::u64 attributeCount) override; Result Key(AZ::Name key) override; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp index 4e13a2a95c..0da4bcd5d3 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp @@ -105,9 +105,9 @@ namespace AZ::Dom return VisitorSuccess(); } - Visitor::Result Visitor::RefCountedString(AZStd::shared_ptr value, Lifetime lifetime) + Visitor::Result Visitor::RefCountedString(AZStd::shared_ptr> value, Lifetime lifetime) { - return String(*value, lifetime); + return String({ value->data(), value->size() }, lifetime); } Visitor::Result Visitor::OpaqueValue([[maybe_unused]] OpaqueType& value) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h index 3bf1b3419b..f9930f93c6 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h @@ -11,8 +11,9 @@ #include #include #include -#include +#include #include +#include namespace AZ::Dom { @@ -176,7 +177,7 @@ namespace AZ::Dom //! Operates on a ref-counted string value. S //! \param lifetime Specifies the lifetime of this string. If the string has a temporary lifetime, it may not //! be safely stored as a reference, but may still be safely stored as a ref-counted shared_ptr. - virtual Result RefCountedString(AZStd::shared_ptr value, Lifetime lifetime); + virtual Result RefCountedString(AZStd::shared_ptr> value, Lifetime lifetime); //! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to //! indicate where the value may be stored persistently or requires a copy. //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index 51d2e5c10c..af5b367d63 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -220,4 +220,33 @@ namespace AZ::Dom::Benchmark } BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond); + BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByStringComparison)(benchmark::State& state) + { + Value value(Type::Object); + AZStd::vector keys; + for (int64_t i = 0; i < state.range(0); ++i) + { + AZStd::string key(AZStd::string::format("key%" PRId64, i)); + keys.push_back(key); + value[key] = i; + } + + for (auto _ : state) + { + for (const AZStd::string& key : keys) + { + const Object::ContainerType& object = value.GetObject(); + benchmark::DoNotOptimize(AZStd::find_if( + object.cbegin(), object.cend(), + [&key](const Object::EntryType& entry) + { + return key == entry.first.GetStringView(); + })); + } + } + + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByStringComparison)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond); + } // namespace AZ::Dom::Benchmark From 1da99eaea02874f22c1fde8df07e7e730dbd3c6a Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 20 Dec 2021 18:27:00 -0800 Subject: [PATCH 23/49] Add type-safe GetTypeIndex instead of hardcoded index lookups Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 96 ++++++++++++++----- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 54 +++++------ 2 files changed, 97 insertions(+), 53 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 3dafc02c6e..3836568f37 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -26,6 +26,51 @@ namespace AZ::Dom return refCountedPointer; } + namespace Internal + { + template + constexpr size_t GetTypeIndexInternal(size_t index = 0) + { + static_assert(false, "Type not found in ValueType"); + return index; + } + + template + constexpr size_t GetTypeIndexInternal(size_t index = 0) + { + if constexpr (AZStd::is_same_v) + { + return index; + } + else + { + return GetTypeIndexInternal(index + 1); + } + } + + template + struct ExtractTypeArgs + { + }; + + template class TypeToExtract, typename... Args> + struct ExtractTypeArgs> + { + template + static constexpr size_t GetTypeIndex() + { + return GetTypeIndexInternal(); + } + }; + } // namespace Internal + + // Helper function, looks up the index of a type within Value::m_value's storage + template + constexpr size_t GetTypeIndex() + { + return Internal::ExtractTypeArgs::GetTypeIndex(); + } + Node::Node(AZ::Name name) : m_name(name) { @@ -227,27 +272,27 @@ namespace AZ::Dom { switch (m_value.index()) { - case 0: // AZStd::monostate + case GetTypeIndex(): return Type::Null; - case 1: // int64_t + case GetTypeIndex(): return Type::Int64; - case 2: // uint64_t + case GetTypeIndex(): return Type::Uint64; - case 3: // double + case GetTypeIndex(): return Type::Double; - case 4: // bool + case GetTypeIndex(): return Type::Bool; - case 5: // AZStd::string_view - case 6: // AZStd::shared_ptr - case 7: // ShortStringType + case GetTypeIndex(): + case GetTypeIndex(): + case GetTypeIndex(): return Type::String; - case 8: // ObjectPtr + case GetTypeIndex(): return Type::Object; - case 9: // ArrayPtr + case GetTypeIndex(): return Type::Array; - case 10: // NodePtr + case GetTypeIndex(): return Type::Node; - case 11: // AZStd::any* + case GetTypeIndex>(): return Type::Opaque; } AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); @@ -813,11 +858,11 @@ namespace AZ::Dom { switch (m_value.index()) { - case 1: // int64_t + case GetTypeIndex(): return AZStd::get(m_value); - case 2: // uint64_t + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); - case 3: // double + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); } AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); @@ -843,11 +888,11 @@ namespace AZ::Dom { switch (m_value.index()) { - case 1: // int64_t + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); - case 2: // uint64_t + case GetTypeIndex(): return AZStd::get(m_value); - case 3: // double + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); } AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); @@ -888,11 +933,11 @@ namespace AZ::Dom { switch (m_value.index()) { - case 1: // int64_t + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); - case 2: // uint64_t + case GetTypeIndex(): return aznumeric_cast(AZStd::get(m_value)); - case 3: // double + case GetTypeIndex(): return AZStd::get(m_value); } AZ_Assert(false, "AZ::Dom::Value: Called GetInt on a non-numeric type"); @@ -923,14 +968,14 @@ namespace AZ::Dom { switch (m_value.index()) { - case 5: // AZStd::string_view + case GetTypeIndex(): return AZStd::get(m_value); - case 6: // AZStd::shared_ptr> + case GetTypeIndex(): { auto& buffer = *AZStd::get(m_value); return { buffer.data(), buffer.size() }; } - case 7: // ShortStringType + case GetTypeIndex(): { const ShortStringType& shortString = AZStd::get(m_value); return { shortString.data(), shortString.size() }; @@ -965,8 +1010,7 @@ namespace AZ::Dom } else { - SharedStringType sharedString = - AZStd::allocate_shared(StdValueAllocator(), value.begin(), value.end()); + SharedStringType sharedString = AZStd::allocate_shared(StdValueAllocator(), value.begin(), value.end()); m_value = AZStd::move(sharedString); } } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index bc1b790009..86e98030e2 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -159,6 +159,33 @@ namespace AZ::Dom using SharedStringContainer = AZStd::vector; using SharedStringType = AZStd::shared_ptr; + //! The internal storage type for Value. + //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes + //! for the same type in some instances, such as string storage. + using ValueType = AZStd::variant< + // Null + AZStd::monostate, + // Int64 + int64_t, + // Uint64 + uint64_t, + // Double + double, + // Bool + bool, + // StringType + AZStd::string_view, + SharedStringType, + ShortStringType, + // Object + ObjectPtr, + // Array + ArrayPtr, + // Node + NodePtr, + // Opaque + AZStd::shared_ptr>; + // Constructors... Value(); Value(const Value&); @@ -347,33 +374,6 @@ namespace AZ::Dom explicit Value(const AZStd::any& opaqueValue); - //! The internal storage type for Value. - //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes - //! for the same type in some instances, such as string storage. - using ValueType = AZStd::variant< - // Null - AZStd::monostate, - // Int64 - int64_t, - // Uint64 - uint64_t, - // Double - double, - // Bool - bool, - // StringType - AZStd::string_view, - SharedStringType, - ShortStringType, - // Object - ObjectPtr, - // Array - ArrayPtr, - // Node - NodePtr, - // Opaque - AZStd::shared_ptr>; - static_assert( sizeof(ValueType) == sizeof(ShortStringType) + sizeof(size_t), "ValueType should have no members larger than ShortStringType"); From 37330c43a9408ad9ffcd726c14f510c4b5d264f5 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 21 Dec 2021 15:47:42 -0800 Subject: [PATCH 24/49] Address some more Generic Dom Value perf feedback Signed-off-by: Nicholas Van Sickle --- .../Backends/JSON/JsonSerializationUtils.cpp | 4 -- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 57 ++++++++++--------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 4 +- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 26 ++++----- .../AzCore/Tests/DOM/DomJsonBenchmarks.cpp | 26 ++++++--- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 16 +++++- 6 files changed, 77 insertions(+), 56 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp index bc8ac9167c..3baf78b63d 100644 --- a/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/Backends/JSON/JsonSerializationUtils.cpp @@ -373,10 +373,6 @@ namespace AZ::Dom::Json bool RapidJsonReadHandler::Key(const char* str, rapidjson::SizeType length, [[maybe_unused]] bool copy) { AZStd::string_view key = AZStd::string_view(str, length); - if (!m_visitor->SupportsRawKeys()) - { - m_visitor->Key(AZ::Name(key)); - } const Lifetime lifetime = !copy ? m_stringLifetime : Lifetime::Temporary; return CheckResult(m_visitor->RawKey(key, lifetime)); } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 3836568f37..407d1e66a2 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -17,13 +17,15 @@ namespace AZ::Dom template AZStd::shared_ptr& CheckCopyOnWrite(AZStd::shared_ptr& refCountedPointer) { - if (refCountedPointer.use_count() > 1) + if (refCountedPointer.use_count() == 1) { - AZStd::shared_ptr newPointer = AZStd::allocate_shared(StdValueAllocator()); - *newPointer = *refCountedPointer; - refCountedPointer = AZStd::move(newPointer); + return refCountedPointer; + } + else + { + refCountedPointer = AZStd::allocate_shared(StdValueAllocator(), *refCountedPointer); + return refCountedPointer; } - return refCountedPointer; } namespace Internal @@ -72,7 +74,7 @@ namespace AZ::Dom } Node::Node(AZ::Name name) - : m_name(name) + : m_name(AZStd::move(name)) { } @@ -83,7 +85,7 @@ namespace AZ::Dom void Node::SetName(AZ::Name name) { - m_name = name; + m_name = AZStd::move(name); } Object::ContainerType& Node::GetProperties() @@ -106,8 +108,8 @@ namespace AZ::Dom return m_children; } - Value::Value(AZStd::shared_ptr string) - : m_value(string) + Value::Value(SharedStringType sharedString) + : m_value(AZStd::move(sharedString)) { } @@ -122,18 +124,19 @@ namespace AZ::Dom Value::Value(Value&& value) noexcept { - operator=(value); + memcpy(this, &value, sizeof(Value)); + memset(&value, 0, sizeof(Value)); } - Value::Value(AZStd::string_view string, bool copy) + Value::Value(AZStd::string_view stringView, bool copy) { if (copy) { - CopyFromString(string); + CopyFromString(stringView); } else { - SetString(string); + SetString(stringView); } } @@ -227,7 +230,9 @@ namespace AZ::Dom Value& Value::operator=(Value&& other) noexcept { - m_value.swap(other.m_value); + SetNull(); + memcpy(this, &other, sizeof(Value)); + memset(&other, 0, sizeof(Value)); return *this; } @@ -265,7 +270,10 @@ namespace AZ::Dom void Value::Swap(Value& other) noexcept { - m_value.swap(other.m_value); + AZStd::aligned_storage_for_t temp; + memcpy(&temp, this, sizeof(Value)); + memcpy(this, &other, sizeof(Value)); + memcpy(&other, &temp, sizeof(Value)); } Type Dom::Value::GetType() const @@ -583,7 +591,7 @@ namespace AZ::Dom } else { - object.emplace_back(name, value); + object.emplace_back(AZStd::move(name), value); } return *this; } @@ -602,7 +610,7 @@ namespace AZ::Dom } else { - object.emplace_back(name, value); + object.emplace_back(AZStd::move(name), value); } return *this; } @@ -636,15 +644,12 @@ namespace AZ::Dom Object::Iterator Value::RemoveMember(Object::Iterator pos) { Object::ContainerType& object = GetObjectInternal(); - Object::Iterator nextIndex = object.end(); - auto lastEntry = object.end() - 1; - if (pos != lastEntry) + if (!object.empty()) { - AZStd::swap(*pos, *lastEntry); - nextIndex = pos; + AZStd::swap(*pos, object.back()); + object.pop_back(); } - object.resize(object.size() - 1); - return nextIndex; + return object.end(); } Object::Iterator Value::EraseMember(Object::ConstIterator pos) @@ -785,7 +790,7 @@ namespace AZ::Dom void Value::SetNode(AZ::Name name) { - m_value = AZStd::allocate_shared(StdValueAllocator(), name); + m_value = AZStd::allocate_shared(StdValueAllocator(), AZStd::move(name)); } void Value::SetNode(AZStd::string_view name) @@ -800,7 +805,7 @@ namespace AZ::Dom void Value::SetNodeName(AZ::Name name) { - GetNodeInternal().SetName(name); + GetNodeInternal().SetName(AZStd::move(name)); } void Value::SetNodeName(AZStd::string_view name) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 86e98030e2..a777640aaf 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -190,8 +190,8 @@ namespace AZ::Dom Value(); Value(const Value&); Value(Value&&) noexcept; - Value(AZStd::string_view string, bool copy); - Value(AZStd::shared_ptr string); + Value(AZStd::string_view stringView, bool copy); + Value(SharedStringType sharedString); Value(int32_t value); Value(uint32_t value); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 9b814b1bff..3259579aaa 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -82,6 +82,16 @@ namespace AZ::Dom return VisitorSuccess(); } + template + void MoveVectorMemory(AZStd::vector& dest, AZStd::vector& source) + { + dest.resize_no_construct(source.size()); + const size_t size = sizeof(T) * source.size(); + memcpy(dest.data(), source.data(), size); + memset(source.data(), 0, size); + source.resize(0); + } + Visitor::Result ValueWriter::EndContainer(Type containerType, AZ::u64 attributeCount, AZ::u64 elementCount) { const char* endMethodName; @@ -138,22 +148,12 @@ namespace AZ::Dom } if (buffer.m_attributes.size() > 0) { - container.MemberReserve(buffer.m_attributes.size()); - for (AZStd::pair& entry : buffer.m_attributes) - { - container.AddMember(AZStd::move(entry.first), AZStd::move(entry.second)); - } - buffer.m_attributes.clear(); + MoveVectorMemory(container.GetMutableObject(), buffer.m_attributes); } if(buffer.m_elements.size() > 0) { - container.Reserve(buffer.m_elements.size()); - for (Value& entry : buffer.m_elements) - { - container.PushBack(AZStd::move(entry)); - } - buffer.m_elements.clear(); + MoveVectorMemory(container.GetMutableArray(), buffer.m_elements); } m_entryStack.pop(); @@ -180,7 +180,7 @@ namespace AZ::Dom { AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object"); AZ_Assert(!m_entryStack.top().m_container.IsArray(), "Attempted to push a key to an array"); - m_entryStack.top().m_key = key; + m_entryStack.top().m_key = AZStd::move(key); return VisitorSuccess(); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index 477802dc37..8eda110e7b 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -120,6 +120,16 @@ namespace Benchmark AZ_Assert(result.IsSuccess(), "Failed to serialize generated JSON"); return serializedJson; } + + template + void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state) + { + { + T instance = AZStd::move(value); + state.PauseTiming(); + } + state.ResumeTiming(); + } }; // Helper macro for registering JSON benchmarks @@ -148,7 +158,7 @@ namespace Benchmark return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor); }); - benchmark::DoNotOptimize(result.GetValue()); + TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state); } state.SetBytesProcessed(serializedPayload.size() * state.iterations()); @@ -172,7 +182,7 @@ namespace Benchmark return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor); }); - benchmark::DoNotOptimize(result.GetValue()); + TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state); } state.SetBytesProcessed(serializedPayload.size() * state.iterations()); @@ -192,7 +202,7 @@ namespace Benchmark return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor); }); - benchmark::DoNotOptimize(result.GetValue()); + TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state); } state.SetBytesProcessed(serializedPayload.size() * state.iterations()); @@ -212,7 +222,7 @@ namespace Benchmark return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor); }); - benchmark::DoNotOptimize(result.GetValue()); + TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state); } state.SetBytesProcessed(serializedPayload.size() * state.iterations()); @@ -228,7 +238,7 @@ namespace Benchmark { auto result = AZ::JsonSerializationUtils::ReadJsonString(serializedPayload); - benchmark::DoNotOptimize(result.GetValue()); + TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state); } state.SetBytesProcessed(serializedPayload.size() * state.iterations()); @@ -239,7 +249,7 @@ namespace Benchmark { for (auto _ : state) { - benchmark::DoNotOptimize(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1))); + TakeAndDiscardWithoutTimingDtor(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)), state); } state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); @@ -277,7 +287,7 @@ namespace Benchmark { rapidjson::Document copy; copy.CopyFrom(original, copy.GetAllocator(), true); - benchmark::DoNotOptimize(copy); + TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } state.SetItemsProcessed(state.iterations()); @@ -294,7 +304,7 @@ namespace Benchmark rapidjson::Document copy; copy.CopyFrom(original, copy.GetAllocator(), true); copy["entries"]["Key0"].PushBack(42, copy.GetAllocator()); - benchmark::DoNotOptimize(copy); + TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } state.SetItemsProcessed(state.iterations()); diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index af5b367d63..12684c64bc 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -98,13 +98,23 @@ namespace AZ::Dom::Benchmark return root; } + + template + void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state) + { + { + T instance = AZStd::move(value); + state.PauseTiming(); + } + state.ResumeTiming(); + } }; BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state) { for (auto _ : state) { - benchmark::DoNotOptimize(GenerateDomBenchmarkPayload(state.range(0), state.range(1))); + TakeAndDiscardWithoutTimingDtor(GenerateDomBenchmarkPayload(state.range(0), state.range(1)), state); } state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations()); @@ -143,7 +153,7 @@ namespace AZ::Dom::Benchmark { Value copy = original; copy["entries"]["Key0"].PushBack(42); - benchmark::DoNotOptimize(copy); + TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } state.SetItemsProcessed(state.iterations()); @@ -162,7 +172,7 @@ namespace AZ::Dom::Benchmark for (auto _ : state) { Value copy = original.DeepCopy(); - benchmark::DoNotOptimize(copy); + TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } state.SetItemsProcessed(state.iterations()); From 68c93273d687bda5c57e88d9ab630ca6ee03b6f1 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 21 Dec 2021 16:09:11 -0800 Subject: [PATCH 25/49] Add 8 and 16 bit numeric type API Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 20 +++++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomValue.h | 12 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 407d1e66a2..f9f4f726dd 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -150,6 +150,26 @@ namespace AZ::Dom return Value(&value); } + Value::Value(int8_t value) + : m_value(aznumeric_cast(value)) + { + } + + Value::Value(uint8_t value) + : m_value(aznumeric_cast(value)) + { + } + + Value::Value(int16_t value) + : m_value(aznumeric_cast(value)) + { + } + + Value::Value(uint16_t value) + : m_value(aznumeric_cast(value)) + { + } + Value::Value(int32_t value) : m_value(aznumeric_cast(value)) { diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index a777640aaf..7ef794486e 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -193,6 +193,10 @@ namespace AZ::Dom Value(AZStd::string_view stringView, bool copy); Value(SharedStringType sharedString); + Value(int8_t value); + Value(uint8_t value); + Value(int16_t value); + Value(uint16_t value); Value(int32_t value); Value(uint32_t value); Value(int64_t value); @@ -321,12 +325,20 @@ namespace AZ::Dom void SetInt64(int64_t); int32_t GetInt32() const; void SetInt32(int32_t); + int16_t GetInt16() const; + void SetInt16(int16_t); + int8_t GetInt8() const; + void SetInt8(int8_t); // uint API... uint64_t GetUint64() const; void SetUint64(uint64_t); uint32_t GetUint32() const; void SetUint32(uint32_t); + uint16_t GetUint16() const; + void SetUint16(uint16_t); + uint8_t GetUint8() const; + void SetUint8(uint8_t); // bool API... bool GetBool() const; From deb3568aaa15e962b49fb5a0aa6b19960866fa73 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 21 Dec 2021 16:19:32 -0800 Subject: [PATCH 26/49] Fix up a couple opaque value cases Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 4 ++-- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index f9f4f726dd..01d7bcf6e1 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -1042,7 +1042,7 @@ namespace AZ::Dom const AZStd::any& Value::GetOpaqueValue() const { - return *AZStd::get>(m_value); + return *AZStd::get(m_value); } void Value::SetOpaqueValue(const AZStd::any& value) @@ -1165,7 +1165,7 @@ namespace AZ::Dom result = visitor.EndNode(object.size(), arrayContainer.size()); } } - else if constexpr (AZStd::is_same_v) + else if constexpr (AZStd::is_same_v) { result = visitor.OpaqueValue(*arg); } diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 7ef794486e..b9c1062ed7 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -158,6 +158,7 @@ namespace AZ::Dom using ShortStringType = AZStd::fixed_string; using SharedStringContainer = AZStd::vector; using SharedStringType = AZStd::shared_ptr; + using OpaqueStorageType = AZStd::shared_ptr; //! The internal storage type for Value. //! These types do not correspond one-to-one with the Value's external Type as there may be multiple storage classes @@ -184,7 +185,7 @@ namespace AZ::Dom // Node NodePtr, // Opaque - AZStd::shared_ptr>; + OpaqueStorageType>; // Constructors... Value(); From fd70a2207e68dc456a91e23e5446f64b5a6971a6 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 21 Dec 2021 16:47:13 -0800 Subject: [PATCH 27/49] Address a few other small pieces of feedback Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 14 +++++--------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 8 ++++---- .../Framework/AzCore/AzCore/DOM/DomValueWriter.cpp | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 01d7bcf6e1..7659afa070 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -113,10 +113,6 @@ namespace AZ::Dom { } - Value::Value() - { - } - Value::Value(const Value& value) : m_value(value.m_value) { @@ -344,7 +340,7 @@ namespace AZ::Dom bool Value::IsBool() const { - return AZStd::holds_alternative(m_value); + return GetType() == Type::Bool; } bool Value::IsNode() const @@ -383,17 +379,17 @@ namespace AZ::Dom bool Value::IsInt() const { - return AZStd::holds_alternative(m_value); + return GetType() == Type::Int64; } bool Value::IsUint() const { - return AZStd::holds_alternative(m_value); + return GetType() == Type::Uint64; } bool Value::IsDouble() const { - return AZStd::holds_alternative(m_value); + return GetType() == Type::Double; } bool Value::IsString() const @@ -666,7 +662,7 @@ namespace AZ::Dom Object::ContainerType& object = GetObjectInternal(); if (!object.empty()) { - AZStd::swap(*pos, object.back()); + *pos = AZStd::move(object.back()); object.pop_back(); } return object.end(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index b9c1062ed7..b73da92d23 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -103,9 +103,9 @@ namespace AZ::Dom { public: Node() = default; - Node(AZ::Name name); - Node(const Node&) = default; - Node(Node&&) = default; + explicit Node(AZ::Name name); + explicit Node(const Node&) = default; + explicit Node(Node&&) = default; Node& operator=(const Node&) = default; Node& operator=(Node&&) = default; @@ -188,7 +188,7 @@ namespace AZ::Dom OpaqueStorageType>; // Constructors... - Value(); + Value() = default; Value(const Value&); Value(Value&&) noexcept; Value(AZStd::string_view stringView, bool copy); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 3259579aaa..f8ca56ea32 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -70,7 +70,7 @@ namespace AZ::Dom Visitor::Result ValueWriter::RefCountedString(AZStd::shared_ptr> value, [[maybe_unused]] Lifetime lifetime) { - CurrentValue().SetString(value); + CurrentValue().SetString(AZStd::move(value)); return FinishWrite(); } From f69b9b817c19b73921d5e4095d309a51b656fe8c Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 22 Dec 2021 11:03:51 -0800 Subject: [PATCH 28/49] Round of clang compile fixes Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp | 2 +- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 10 ++-------- Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp | 7 +++---- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp index 46fa5b9bac..d65497e194 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp @@ -22,7 +22,7 @@ namespace AZ::Dom::Utils return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor); } - AZ::Outcome AZ::Dom::Utils::WriteToValue(Backend::WriteCallback writeCallback) + AZ::Outcome WriteToValue(Backend::WriteCallback writeCallback) { Value value; AZStd::unique_ptr writer = value.GetWriteHandler(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 7659afa070..7401a5ac98 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -6,8 +6,6 @@ * */ -#pragma once - #include #include #include @@ -31,11 +29,7 @@ namespace AZ::Dom namespace Internal { template - constexpr size_t GetTypeIndexInternal(size_t index = 0) - { - static_assert(false, "Type not found in ValueType"); - return index; - } + constexpr size_t GetTypeIndexInternal(size_t index = 0); template constexpr size_t GetTypeIndexInternal(size_t index = 0) @@ -143,7 +137,7 @@ namespace AZ::Dom Value Value::FromOpaqueValue(const AZStd::any& value) { - return Value(&value); + return Value(value); } Value::Value(int8_t value) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index f8ca56ea32..b7ed44f8de 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -129,7 +129,7 @@ namespace AZ::Dom AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); } - if (buffer.m_attributes.size() != attributeCount) + if (static_cast(buffer.m_attributes.size()) != attributeCount) { return VisitorFailure( VisitorErrorCode::InternalError, @@ -138,7 +138,7 @@ namespace AZ::Dom buffer.m_attributes.size())); } - if (buffer.m_elements.size() != elementCount) + if (static_cast(buffer.m_elements.size()) != elementCount) { return VisitorFailure( VisitorErrorCode::InternalError, @@ -146,6 +146,7 @@ namespace AZ::Dom "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, buffer.m_elements.size())); } + if (buffer.m_attributes.size() > 0) { MoveVectorMemory(container.GetMutableObject(), buffer.m_attributes); @@ -237,8 +238,6 @@ namespace AZ::Dom m_entryStack.top().m_value.Swap(value); ValueInfo& newEntry = m_entryStack.top(); - constexpr const size_t reserveSize = 8; - if (!newEntry.m_key.IsEmpty()) { GetValueBuffer().m_attributes.emplace_back(AZStd::move(newEntry.m_key), AZStd::move(value)); From 8732fab19284ca5736d30f1406659949bed02710 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 22 Dec 2021 12:35:44 -0800 Subject: [PATCH 29/49] One more compile fix Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index b7ed44f8de..210d1fa123 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -134,7 +134,7 @@ namespace AZ::Dom return VisitorFailure( VisitorErrorCode::InternalError, AZStd::string::format( - "AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount, + "AZ::Dom::ValueWriter: %s expected %llu attributes but received %zu attributes instead", endMethodName, attributeCount, buffer.m_attributes.size())); } @@ -143,7 +143,7 @@ namespace AZ::Dom return VisitorFailure( VisitorErrorCode::InternalError, AZStd::string::format( - "AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount, + "AZ::Dom::ValueWriter: %s expected %llu elements but received %zu elements instead", endMethodName, elementCount, buffer.m_elements.size())); } From a891993c35091f4eb3f18ff1ac91c944b08d22ef Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Tue, 4 Jan 2022 09:58:30 -0800 Subject: [PATCH 30/49] Remove 'INF' from integer widget and replace it with better formatted integers. Also fix warning message with invalid param Signed-off-by: mrieggeramzn --- .../UI/PropertyEditor/PropertyIntCtrlCommon.h | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h index bb7d03367b..7d3e7191e3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h @@ -93,23 +93,13 @@ namespace AzToolsFramework toolTipString += "\n"; } toolTipString += "["; - if (propertyControl->minimum() <= aznumeric_cast(QtWidgetLimits::Min())) - { - toolTipString += "-" + QObject::tr(PropertyQTConstant_InfinityString); - } - else - { - toolTipString += QString::number(propertyControl->minimum()); - } + + const QString minString = QLocale().toString(propertyControl->minimum()); + const QString maxString = QLocale().toString(propertyControl->maximum()); + + toolTipString += minString; toolTipString += ", "; - if (propertyControl->maximum() >= aznumeric_cast(QtWidgetLimits::Max())) - { - toolTipString += QObject::tr(PropertyQTConstant_InfinityString); - } - else - { - toolTipString += QString::number(propertyControl->maximum()); - } + toolTipString += maxString; toolTipString += "]"; return true; } @@ -128,15 +118,12 @@ namespace AzToolsFramework { toolTipString += "\n"; } - toolTipString += "[" + QString::number(propertyControl->minimum()) + ", "; - if (propertyControl->maximum() >= aznumeric_cast(QtWidgetLimits::Max())) - { - toolTipString += QObject::tr(PropertyQTConstant_InfinityString); - } - else - { - toolTipString += QString::number(propertyControl->maximum()); - } + + const QString minString = QLocale().toString(propertyControl->minimum()); + const QString maxString = QLocale().toString(propertyControl->maximum()); + + toolTipString += "[" + minString + ", "; + toolTipString += maxString; toolTipString += "]"; return true; } @@ -196,7 +183,7 @@ namespace AzToolsFramework } else { - AZ_WarningOnce("AzToolsFramework", false, "Property %s: 'Min' attribute from property '%s' into widget", debugName); + AZ_WarningOnce("AzToolsFramework", false, "Failed to read 'Min' attribute from property '%s' into widget", debugName); } } else if (attrib == AZ::Edit::Attributes::Max) From 30e21bc2d115f9309a3629df0ac71837861d8e8a Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Tue, 4 Jan 2022 14:52:00 -0800 Subject: [PATCH 31/49] Updating formatting Signed-off-by: mrieggeramzn --- .../UI/PropertyEditor/PropertyIntCtrlCommon.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h index 7d3e7191e3..504e3fd0dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h @@ -92,15 +92,11 @@ namespace AzToolsFramework { toolTipString += "\n"; } - toolTipString += "["; const QString minString = QLocale().toString(propertyControl->minimum()); const QString maxString = QLocale().toString(propertyControl->maximum()); + toolTipString += QString("[%1, %2]").arg(minString).arg(maxString); - toolTipString += minString; - toolTipString += ", "; - toolTipString += maxString; - toolTipString += "]"; return true; } return false; @@ -121,10 +117,8 @@ namespace AzToolsFramework const QString minString = QLocale().toString(propertyControl->minimum()); const QString maxString = QLocale().toString(propertyControl->maximum()); + toolTipString += QString("[%1, %2]").arg(minString).arg(maxString); - toolTipString += "[" + minString + ", "; - toolTipString += maxString; - toolTipString += "]"; return true; } return false; From 41c0fb2b02a285c2682785d8ae22e54a652a1f0a Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Tue, 4 Jan 2022 17:41:58 -0800 Subject: [PATCH 32/49] Address some review feedback Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp | 2 +- Code/Framework/AzCore/AzCore/DOM/DomUtils.h | 2 +- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 248 +++++++++++------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 38 +-- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 4 +- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 4 +- .../AzCore/Tests/DOM/DomValueTests.cpp | 46 ++-- 7 files changed, 203 insertions(+), 141 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp index d65497e194..73c4bd2d76 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp @@ -22,7 +22,7 @@ namespace AZ::Dom::Utils return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor); } - AZ::Outcome WriteToValue(Backend::WriteCallback writeCallback) + AZ::Outcome WriteToValue(const Backend::WriteCallback& writeCallback) { Value value; AZStd::unique_ptr writer = value.GetWriteHandler(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h index f03e5b66b8..ebe4273b48 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h @@ -16,5 +16,5 @@ namespace AZ::Dom::Utils Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor); Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor); - AZ::Outcome WriteToValue(Backend::WriteCallback writeCallback); + AZ::Outcome WriteToValue(const Backend::WriteCallback& writeCallback); } // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 7401a5ac98..8c002b7091 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -12,22 +12,22 @@ namespace AZ::Dom { - template - AZStd::shared_ptr& CheckCopyOnWrite(AZStd::shared_ptr& refCountedPointer) - { - if (refCountedPointer.use_count() == 1) - { - return refCountedPointer; - } - else - { - refCountedPointer = AZStd::allocate_shared(StdValueAllocator(), *refCountedPointer); - return refCountedPointer; - } - } - namespace Internal { + template + AZStd::shared_ptr& CheckCopyOnWrite(AZStd::shared_ptr& refCountedPointer) + { + if (refCountedPointer.use_count() == 1) + { + return refCountedPointer; + } + else + { + refCountedPointer = AZStd::allocate_shared(StdValueAllocator(), *refCountedPointer); + return refCountedPointer; + } + } + template constexpr size_t GetTypeIndexInternal(size_t index = 0); @@ -288,107 +288,173 @@ namespace AZ::Dom Type Dom::Value::GetType() const { - switch (m_value.index()) - { - case GetTypeIndex(): - return Type::Null; - case GetTypeIndex(): - return Type::Int64; - case GetTypeIndex(): - return Type::Uint64; - case GetTypeIndex(): - return Type::Double; - case GetTypeIndex(): - return Type::Bool; - case GetTypeIndex(): - case GetTypeIndex(): - case GetTypeIndex(): - return Type::String; - case GetTypeIndex(): - return Type::Object; - case GetTypeIndex(): - return Type::Array; - case GetTypeIndex(): - return Type::Node; - case GetTypeIndex>(): - return Type::Opaque; - } - AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); - return Type::Null; + return AZStd::visit( + [](auto&& value) -> Type + { + using CurrentType = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + return Type::Null; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Int64; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Uint64; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Double; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Bool; + } + else if constexpr (AZStd::is_same_v) + { + return Type::String; + } + else if constexpr (AZStd::is_same_v) + { + return Type::String; + } + else if constexpr (AZStd::is_same_v) + { + return Type::String; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Object; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Array; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Node; + } + else if constexpr (AZStd::is_same_v) + { + return Type::Opaque; + } + else + { + static_assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); + } + }, + m_value); } bool Value::IsNull() const { - return GetType() == Type::Null; + return AZStd::holds_alternative(m_value); } bool Value::IsFalse() const { - return IsBool() && !AZStd::get(m_value); + const bool* value = AZStd::get_if(&m_value); + return value != nullptr ? !(*value) : false; } bool Value::IsTrue() const { - return IsBool() && AZStd::get(m_value); + const bool* value = AZStd::get_if(&m_value); + return value != nullptr ? *value : false; } bool Value::IsBool() const { - return GetType() == Type::Bool; + return AZStd::holds_alternative(m_value); } bool Value::IsNode() const { - return GetType() == Type::Node; + return AZStd::holds_alternative(m_value); } bool Value::IsObject() const { - return GetType() == Type::Object; + return AZStd::holds_alternative(m_value); } bool Value::IsArray() const { - return GetType() == Type::Array; + return AZStd::holds_alternative(m_value); } bool Value::IsOpaqueValue() const { - return GetType() == Type::Opaque; + return AZStd::holds_alternative(m_value); } bool Value::IsNumber() const { - switch (GetType()) - { - case Type::Int64: - [[fallthrough]]; - case Type::Uint64: - [[fallthrough]]; - case Type::Double: - return true; - } - return false; + return AZStd::visit( + [](auto&& value) -> bool + { + using CurrentType = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + return true; + } + else if constexpr (AZStd::is_same_v) + { + return true; + } + else if constexpr (AZStd::is_same_v) + { + return true; + } + else + { + return false; + } + }, + m_value); } bool Value::IsInt() const { - return GetType() == Type::Int64; + return AZStd::holds_alternative(m_value); } bool Value::IsUint() const { - return GetType() == Type::Uint64; + return AZStd::holds_alternative(m_value); } bool Value::IsDouble() const { - return GetType() == Type::Double; + return AZStd::holds_alternative(m_value); } bool Value::IsString() const { - return GetType() == Type::String; + return AZStd::visit( + [](auto&& value) -> bool + { + using CurrentType = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + return true; + } + else if constexpr (AZStd::is_same_v) + { + return true; + } + else if constexpr (AZStd::is_same_v) + { + return true; + } + else + { + return false; + } + }, + m_value); } Value& Value::SetObject() @@ -406,7 +472,7 @@ namespace AZ::Dom Node& Value::GetNodeInternal() { AZ_Assert(GetType() == Type::Node, "AZ::Dom::Value: attempted to retrieve a node from a non-node value"); - return *CheckCopyOnWrite(AZStd::get(m_value)); + return *Internal::CheckCopyOnWrite(AZStd::get(m_value)); } const Object::ContainerType& Value::GetObjectInternal() const @@ -433,11 +499,11 @@ namespace AZ::Dom "AZ::Dom::Value: attempted to retrieve an object from a value that isn't an object or a node"); if (type == Type::Object) { - return CheckCopyOnWrite(AZStd::get(m_value))->m_values; + return Internal::CheckCopyOnWrite(AZStd::get(m_value))->m_values; } else { - return CheckCopyOnWrite(AZStd::get(m_value))->GetProperties(); + return Internal::CheckCopyOnWrite(AZStd::get(m_value))->GetProperties(); } } @@ -465,11 +531,11 @@ namespace AZ::Dom "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or node"); if (type == Type::Array) { - return CheckCopyOnWrite(AZStd::get(m_value))->m_values; + return Internal::CheckCopyOnWrite(AZStd::get(m_value))->m_values; } else { - return CheckCopyOnWrite(AZStd::get(m_value))->GetChildren(); + return Internal::CheckCopyOnWrite(AZStd::get(m_value))->GetChildren(); } } @@ -698,22 +764,22 @@ namespace AZ::Dom return *this; } - size_t Value::Size() const + size_t Value::ArraySize() const { return GetArrayInternal().size(); } - size_t Value::Capacity() const + size_t Value::ArrayCapacity() const { return GetArrayInternal().capacity(); } - bool Value::Empty() const + bool Value::IsArrayEmpty() const { return GetArrayInternal().empty(); } - void Value::Clear() + void Value::ClearArray() { GetArrayInternal().clear(); } @@ -728,43 +794,43 @@ namespace AZ::Dom return GetArrayInternal()[index]; } - Value& Value::MutableAt(size_t index) + Value& Value::MutableArrayAt(size_t index) { return operator[](index); } - const Value& Value::At(size_t index) const + const Value& Value::ArrayAt(size_t index) const { return operator[](index); } - Array::ConstIterator Value::Begin() const + Array::ConstIterator Value::ArrayBegin() const { return GetArrayInternal().begin(); } - Array::ConstIterator Value::End() const + Array::ConstIterator Value::ArrayEnd() const { return GetArrayInternal().end(); } - Array::Iterator Value::Begin() + Array::Iterator Value::ArrayBegin() { return GetArrayInternal().begin(); } - Array::Iterator Value::End() + Array::Iterator Value::ArrayEnd() { return GetArrayInternal().end(); } - Value& Value::Reserve(size_t newCapacity) + Value& Value::ArrayReserve(size_t newCapacity) { GetArrayInternal().reserve(newCapacity); return *this; } - Value& Value::PushBack(Value value) + Value& Value::ArrayPushBack(Value value) { Array::ContainerType& array = GetArrayInternal(); array.reserve((array.size() / Array::ReserveIncrement + 1) * Array::ReserveIncrement); @@ -772,18 +838,18 @@ namespace AZ::Dom return *this; } - Value& Value::PopBack() + Value& Value::ArrayPopBack() { GetArrayInternal().pop_back(); return *this; } - Array::Iterator Value::Erase(Array::ConstIterator pos) + Array::Iterator Value::ArrayErase(Array::ConstIterator pos) { return GetArrayInternal().erase(pos); } - Array::Iterator Value::Erase(Array::ConstIterator first, Array::ConstIterator last) + Array::Iterator Value::ArrayErase(Array::ConstIterator first, Array::ConstIterator last) { return GetArrayInternal().erase(first, last); } @@ -1007,13 +1073,6 @@ namespace AZ::Dom void Value::SetString(AZStd::string_view value) { - if (value.size() <= ShortStringSize) - { - ShortStringType buffer; - buffer.resize_no_construct(value.size()); - memcpy(buffer.data(), value.data(), value.size()); - m_value = buffer; - } m_value = value; } @@ -1021,7 +1080,10 @@ namespace AZ::Dom { if (value.size() <= ShortStringSize) { - SetString(value); + ShortStringType buffer; + buffer.resize_no_construct(value.size()); + memcpy(buffer.data(), value.data(), value.size()); + m_value = buffer; } else { @@ -1035,9 +1097,9 @@ namespace AZ::Dom return *AZStd::get(m_value); } - void Value::SetOpaqueValue(const AZStd::any& value) + void Value::SetOpaqueValue(AZStd::any value) { - m_value = AZStd::allocate_shared(StdValueAllocator(), value); + m_value = AZStd::allocate_shared(StdValueAllocator(), AZStd::move(value)); } void Value::SetNull() diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index b73da92d23..d67947dd0a 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -103,9 +103,9 @@ namespace AZ::Dom { public: Node() = default; + Node(const Node&) = default; + Node(Node&&) = default; explicit Node(AZ::Name name); - explicit Node(const Node&) = default; - explicit Node(Node&&) = default; Node& operator=(const Node&) = default; Node& operator=(Node&&) = default; @@ -174,7 +174,7 @@ namespace AZ::Dom double, // Bool bool, - // StringType + // String AZStd::string_view, SharedStringType, ShortStringType, @@ -280,28 +280,28 @@ namespace AZ::Dom // Array API (also used by Node)... Value& SetArray(); - size_t Size() const; - size_t Capacity() const; - bool Empty() const; - void Clear(); + size_t ArraySize() const; + size_t ArrayCapacity() const; + bool IsArrayEmpty() const; + void ClearArray(); Value& operator[](size_t index); const Value& operator[](size_t index) const; - Value& MutableAt(size_t index); - const Value& At(size_t index) const; + Value& MutableArrayAt(size_t index); + const Value& ArrayAt(size_t index) const; - Array::ConstIterator Begin() const; - Array::ConstIterator End() const; - Array::Iterator Begin(); - Array::Iterator End(); + Array::ConstIterator ArrayBegin() const; + Array::ConstIterator ArrayEnd() const; + Array::Iterator ArrayBegin(); + Array::Iterator ArrayEnd(); - Value& Reserve(size_t newCapacity); - Value& PushBack(Value value); - Value& PopBack(); + Value& ArrayReserve(size_t newCapacity); + Value& ArrayPushBack(Value value); + Value& ArrayPopBack(); - Array::Iterator Erase(Array::ConstIterator pos); - Array::Iterator Erase(Array::ConstIterator first, Array::ConstIterator last); + Array::Iterator ArrayErase(Array::ConstIterator pos); + Array::Iterator ArrayErase(Array::ConstIterator first, Array::ConstIterator last); Array::ContainerType& GetMutableArray(); const Array::ContainerType& GetArray() const; @@ -365,7 +365,7 @@ namespace AZ::Dom //! serialize an opaque type into a DOM value instead, as serializers //! and other systems will have no means of dealing with fully arbitrary //! values. - void SetOpaqueValue(const AZStd::any&); + void SetOpaqueValue(AZStd::any); // Null API... void SetNull(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 210d1fa123..433e650d04 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -129,7 +129,7 @@ namespace AZ::Dom AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName)); } - if (static_cast(buffer.m_attributes.size()) != attributeCount) + if (aznumeric_cast(buffer.m_attributes.size()) != attributeCount) { return VisitorFailure( VisitorErrorCode::InternalError, @@ -138,7 +138,7 @@ namespace AZ::Dom buffer.m_attributes.size())); } - if (static_cast(buffer.m_elements.size()) != elementCount) + if (aznumeric_cast(buffer.m_elements.size()) != elementCount) { return VisitorFailure( VisitorErrorCode::InternalError, diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index 12684c64bc..74eb78be00 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -77,7 +77,7 @@ namespace AZ::Dom::Benchmark Value array(Type::Array); for (int i = 0; i < entryCount; ++i) { - array.PushBack(createEntry(i)); + array.ArrayPushBack(createEntry(i)); } return array; }; @@ -152,7 +152,7 @@ namespace AZ::Dom::Benchmark for (auto _ : state) { Value copy = original; - copy["entries"]["Key0"].PushBack(42); + copy["entries"]["Key0"].ArrayPushBack(42); TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index b88ab746b9..3cbd532b13 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -55,7 +55,7 @@ namespace AZ::Dom::Tests m_value.SetArray(); EXPECT_TRUE(m_value.IsArray()); - EXPECT_EQ(m_value.Size(), 0); + EXPECT_EQ(m_value.ArraySize(), 0); PerformValueChecks(); } @@ -66,8 +66,8 @@ namespace AZ::Dom::Tests for (int i = 0; i < 5; ++i) { - m_value.PushBack(Value(i)); - EXPECT_EQ(m_value.Size(), i + 1); + m_value.ArrayPushBack(Value(i)); + EXPECT_EQ(m_value.ArraySize(), i + 1); EXPECT_EQ(m_value[i].GetInt32(), i); } @@ -82,15 +82,15 @@ namespace AZ::Dom::Tests Value nestedArray(Type::Array); for (int i = 0; i < 5; ++i) { - nestedArray.PushBack(Value(i)); + nestedArray.ArrayPushBack(Value(i)); } - m_value.PushBack(AZStd::move(nestedArray)); + m_value.ArrayPushBack(AZStd::move(nestedArray)); } - EXPECT_EQ(m_value.Size(), 5); + EXPECT_EQ(m_value.ArraySize(), 5); for (int i = 0; i < 3; ++i) { - EXPECT_EQ(m_value[i].Size(), 5); + EXPECT_EQ(m_value[i].ArraySize(), 5); for (int j = 0; j < 5; ++j) { EXPECT_EQ(m_value[i][j].GetInt32(), j); @@ -154,7 +154,7 @@ namespace AZ::Dom::Tests m_value.SetNode("Test"); EXPECT_EQ(m_value.GetNodeName(), AZ::Name("Test")); EXPECT_EQ(m_value.MemberCount(), 0); - EXPECT_EQ(m_value.Size(), 0); + EXPECT_EQ(m_value.ArraySize(), 0); PerformValueChecks(); } @@ -165,8 +165,8 @@ namespace AZ::Dom::Tests for (int i = 0; i < 10; ++i) { - m_value.PushBack(Value(i)); - EXPECT_EQ(m_value.Size(), i + 1); + m_value.ArrayPushBack(Value(i)); + EXPECT_EQ(m_value.ArraySize(), i + 1); EXPECT_EQ(m_value[i].GetInt32(), i); if (i < 5) @@ -196,10 +196,10 @@ namespace AZ::Dom::Tests childNode.AddMember("foo", i); childNode.AddMember("bar", Value("test", false)); - m_value.PushBack(childNode); + m_value.ArrayPushBack(childNode); } - EXPECT_EQ(m_value.Size(), 5); + EXPECT_EQ(m_value.ArraySize(), 5); for (int i = 0; i < 5; ++i) { const Value& childNode = m_value[i]; @@ -334,30 +334,30 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, CopyOnWrite_Array) { Value v1(Type::Array); - v1.PushBack(1); - v1.PushBack(2); + v1.ArrayPushBack(1); + v1.ArrayPushBack(2); Value nestedArray(Type::Array); - v1.PushBack(nestedArray); + v1.ArrayPushBack(nestedArray); Value v2 = v1; EXPECT_EQ(&v1.GetArray(), &v2.GetArray()); - EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); v2[0] = 0; EXPECT_NE(&v1.GetArray(), &v2.GetArray()); - EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); - v2[2].PushBack(42); + v2[2].ArrayPushBack(42); EXPECT_NE(&v1.GetArray(), &v2.GetArray()); - EXPECT_NE(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + EXPECT_NE(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); v2 = v1; EXPECT_EQ(&v1.GetArray(), &v2.GetArray()); - EXPECT_EQ(&v1.At(2).GetArray(), &v2.At(2).GetArray()); + EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); } TEST_F(DomValueTests, CopyOnWrite_Node) @@ -365,8 +365,8 @@ namespace AZ::Dom::Tests Value v1; v1.SetNode("TopLevel"); - v1.PushBack(1); - v1.PushBack(2); + v1.ArrayPushBack(1); + v1.ArrayPushBack(2); v1["obj"].SetNode("Nested"); Value v2 = v1; @@ -378,7 +378,7 @@ namespace AZ::Dom::Tests EXPECT_NE(&v1.GetNode(), &v2.GetNode()); EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode()); - v2["obj"].PushBack(42); + v2["obj"].ArrayPushBack(42); EXPECT_NE(&v1.GetNode(), &v2.GetNode()); EXPECT_NE(&v1["obj"].GetNode(), &v2["obj"].GetNode()); From 8ed3da5b7f9916474e3e1de2188d23093bf68549 Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Wed, 5 Jan 2022 10:24:53 -0800 Subject: [PATCH 33/49] Adding header file Signed-off-by: mrieggeramzn --- .../AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h index 504e3fd0dc..ed428eeac5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace AzToolsFramework { From acc6248ec98ef8cd24ea057f679d90ca6be812e5 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 5 Jan 2022 11:31:09 -0800 Subject: [PATCH 34/49] Move deep comparison / copy to utils Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp | 148 +++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomUtils.h | 3 + Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 155 +++--------------- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 13 +- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 3 +- .../AzCore/Tests/DOM/DomValueTests.cpp | 6 +- 6 files changed, 186 insertions(+), 142 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp index 73c4bd2d76..c604373296 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp @@ -33,4 +33,152 @@ namespace AZ::Dom::Utils } return AZ::Success(AZStd::move(value)); } + + bool DeepCompareIsEqual(const Value& lhs, const Value& rhs) + { + const Value::ValueType& lhsValue = lhs.GetInternalValue(); + const Value::ValueType& rhsValue = rhs.GetInternalValue(); + + if (lhs.IsString() && rhs.IsString()) + { + // If we both hold the same ref counted string we don't need to do a full comparison + if (AZStd::holds_alternative(lhsValue) && lhsValue == rhsValue) + { + return true; + } + return lhs.GetString() == rhs.GetString(); + } + + return AZStd::visit( + [&](auto&& ourValue) -> bool + { + using Alternative = AZStd::decay_t; + + if constexpr (AZStd::is_same_v) + { + if (!rhs.IsObject()) + { + return false; + } + auto&& theirValue = AZStd::get>(rhsValue); + if (ourValue == theirValue) + { + return true; + } + + const Object::ContainerType& ourValues = ourValue->GetValues(); + const Object::ContainerType& theirValues = theirValue->GetValues(); + + if (ourValues.size() != theirValues.size()) + { + return false; + } + + for (size_t i = 0; i < ourValues.size(); ++i) + { + const Object::EntryType& lhsChild = ourValues[i]; + const Object::EntryType& rhsChild = theirValues[i]; + if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second)) + { + return false; + } + } + + return true; + } + else if constexpr (AZStd::is_same_v) + { + if (!rhs.IsArray()) + { + return false; + } + auto&& theirValue = AZStd::get>(rhsValue); + if (ourValue == theirValue) + { + return true; + } + + const Array::ContainerType& ourValues = ourValue->GetValues(); + const Array::ContainerType& theirValues = theirValue->GetValues(); + + if (ourValues.size() != theirValues.size()) + { + return false; + } + + for (size_t i = 0; i < ourValues.size(); ++i) + { + const Value& lhsChild = ourValues[i]; + const Value& rhsChild = theirValues[i]; + if (!DeepCompareIsEqual(lhsChild, rhsChild)) + { + return false; + } + } + + return true; + } + else if constexpr (AZStd::is_same_v) + { + if (!rhs.IsNode()) + { + return false; + } + auto&& theirValue = AZStd::get>(rhsValue); + if (ourValue == theirValue) + { + return true; + } + + const Node& ourNode = *ourValue; + const Node& theirNode = *theirValue; + + const Object::ContainerType& ourProperties = ourNode.GetProperties(); + const Object::ContainerType& theirProperties = theirNode.GetProperties(); + + if (ourProperties.size() != theirProperties.size()) + { + return false; + } + + for (size_t i = 0; i < ourProperties.size(); ++i) + { + const Object::EntryType& lhsChild = ourProperties[i]; + const Object::EntryType& rhsChild = theirProperties[i]; + if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second)) + { + return false; + } + } + + const Array::ContainerType& ourChildren = ourNode.GetChildren(); + const Array::ContainerType& theirChildren = theirNode.GetChildren(); + + for (size_t i = 0; i < ourChildren.size(); ++i) + { + const Value& lhsChild = ourChildren[i]; + const Value& rhsChild = theirChildren[i]; + if (!DeepCompareIsEqual(lhsChild, rhsChild)) + { + return false; + } + } + + return true; + } + else + { + return lhs == rhs; + } + }, + lhsValue); + } + + Value DeepCopy(const Value& value, bool copyStrings) + { + Value copiedValue; + AZStd::unique_ptr writer = copiedValue.GetWriteHandler(); + value.Accept(*writer, copyStrings); + return copiedValue; + } } // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h index ebe4273b48..5403b93714 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomUtils.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomUtils.h @@ -17,4 +17,7 @@ namespace AZ::Dom::Utils Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor); AZ::Outcome WriteToValue(const Backend::WriteCallback& writeCallback); + + bool DeepCompareIsEqual(const Value& lhs, const Value& rhs); + Value DeepCopy(const Value& value, bool copyStrings = true); } // namespace AZ::Dom::Utils diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 8c002b7091..af6d86d699 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -67,6 +67,16 @@ namespace AZ::Dom return Internal::ExtractTypeArgs::GetTypeIndex(); } + const Array::ContainerType& Array::GetValues() const + { + return m_values; + } + + const Object::ContainerType& Object::GetValues() const + { + return m_values; + } + Node::Node(AZ::Name name) : m_name(AZStd::move(name)) { @@ -130,8 +140,8 @@ namespace AZ::Dom } } - Value::Value(const AZStd::any& value) - : m_value(AZStd::allocate_shared(StdValueAllocator(), value)) + Value::Value(AZStd::any opaqueValue) + : m_value(AZStd::allocate_shared(StdValueAllocator(), AZStd::move(opaqueValue))) { } @@ -660,7 +670,9 @@ namespace AZ::Dom Value& Value::AddMember(KeyType name, const Value& value) { Object::ContainerType& object = GetObjectInternal(); - object.reserve((object.size() / Object::ReserveIncrement + 1) * Object::ReserveIncrement); + // Reserve in ReserveIncremenet chunks instead of the default vector doubling strategy + // Profiling has found that this is an aggregate performance gain for typical workflows + object.reserve(AZ_SIZE_ALIGN_UP(object.size() + 1, Object::ReserveIncrement)); if (auto memberIt = FindMutableMember(name); memberIt != object.end()) { memberIt->second = value; @@ -833,7 +845,9 @@ namespace AZ::Dom Value& Value::ArrayPushBack(Value value) { Array::ContainerType& array = GetArrayInternal(); - array.reserve((array.size() / Array::ReserveIncrement + 1) * Array::ReserveIncrement); + // Reserve in ReserveIncremenet chunks instead of the default vector doubling strategy + // Profiling has found that this is an aggregate performance gain for typical workflows + array.reserve(AZ_SIZE_ALIGN_UP(array.size() + 1, Array::ReserveIncrement)); array.push_back(AZStd::move(value)); return *this; } @@ -1231,137 +1245,8 @@ namespace AZ::Dom return AZStd::make_unique(*this); } - bool Value::DeepCompareIsEqual(const Value& other) const + const Value::ValueType& Value::GetInternalValue() const { - if (IsString() && other.IsString()) - { - // If we both hold the same ref counted string we don't need to do a full comparison - if (AZStd::holds_alternative(m_value) && m_value == other.m_value) - { - return true; - } - return GetString() == other.GetString(); - } - - if (m_value.index() != other.m_value.index()) - { - return false; - } - - return AZStd::visit( - [&](auto&& ourValue) -> bool - { - using Alternative = AZStd::decay_t; - auto&& theirValue = AZStd::get>(other.m_value); - - if constexpr (AZStd::is_same_v) - { - return true; - } - else if constexpr (AZStd::is_same_v) - { - if (ourValue == theirValue) - { - return true; - } - - if (ourValue->m_values.size() != theirValue->m_values.size()) - { - return false; - } - - for (size_t i = 0; i < ourValue->m_values.size(); ++i) - { - const Object::EntryType& lhs = ourValue->m_values[i]; - const Object::EntryType& rhs = theirValue->m_values[i]; - if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second)) - { - return false; - } - } - - return true; - } - else if constexpr (AZStd::is_same_v) - { - if (ourValue == theirValue) - { - return true; - } - - if (ourValue->m_values.size() != theirValue->m_values.size()) - { - return false; - } - - for (size_t i = 0; i < ourValue->m_values.size(); ++i) - { - const Value& lhs = ourValue->m_values[i]; - const Value& rhs = theirValue->m_values[i]; - if (!lhs.DeepCompareIsEqual(rhs)) - { - return false; - } - } - - return true; - } - else if constexpr (AZStd::is_same_v) - { - if (ourValue == theirValue) - { - return true; - } - - const Node& ourNode = *ourValue; - const Node& theirNode = *theirValue; - - const Object::ContainerType& ourProperties = ourNode.GetProperties(); - const Object::ContainerType& theirProperties = theirNode.GetProperties(); - - if (ourProperties.size() != theirProperties.size()) - { - return false; - } - - for (size_t i = 0; i < ourProperties.size(); ++i) - { - const Object::EntryType& lhs = ourProperties[i]; - const Object::EntryType& rhs = theirProperties[i]; - if (lhs.first != rhs.first || !lhs.second.DeepCompareIsEqual(rhs.second)) - { - return false; - } - } - - const Array::ContainerType& ourChildren = ourNode.GetChildren(); - const Array::ContainerType& theirChildren = theirNode.GetChildren(); - - for (size_t i = 0; i < ourChildren.size(); ++i) - { - const Value& lhs = ourChildren[i]; - const Value& rhs = theirChildren[i]; - if (!lhs.DeepCompareIsEqual(rhs)) - { - return false; - } - } - - return true; - } - else - { - return ourValue == theirValue; - } - }, - m_value); - } - - Value Value::DeepCopy(bool copyStrings) const - { - Value newValue; - AZStd::unique_ptr writer = newValue.GetWriteHandler(); - Accept(*writer, copyStrings); - return newValue; + return m_value; } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index d67947dd0a..a6990fcbce 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -67,6 +67,9 @@ namespace AZ::Dom 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"); + + const ContainerType& GetValues() const; private: ContainerType m_values; @@ -86,6 +89,9 @@ namespace AZ::Dom using Iterator = ContainerType::iterator; using ConstIterator = ContainerType::const_iterator; static constexpr const size_t ReserveIncrement = 8; + static_assert((ReserveIncrement & (ReserveIncrement - 1)) == 0, "ReserveIncremenet must be a power of 2"); + + const ContainerType& GetValues() const; private: ContainerType m_values; @@ -374,8 +380,9 @@ namespace AZ::Dom Visitor::Result Accept(Visitor& visitor, bool copyStrings) const; AZStd::unique_ptr GetWriteHandler(); - bool DeepCompareIsEqual(const Value& other) const; - Value DeepCopy(bool copyStrings = true) const; + //! Gets the internal value of this Value. Note that this value's types may not correspond one-to-one with the Type enumeration, + //! as internally the same type might have different storage mechanisms. Where possible, prefer using the typed API. + const ValueType& GetInternalValue() const; private: const Node& GetNodeInternal() const; @@ -385,7 +392,7 @@ namespace AZ::Dom const Array::ContainerType& GetArrayInternal() const; Array::ContainerType& GetArrayInternal(); - explicit Value(const AZStd::any& opaqueValue); + explicit Value(AZStd::any opaqueValue); static_assert( sizeof(ValueType) == sizeof(ShortStringType) + sizeof(size_t), "ValueType should have no members larger than ShortStringType"); diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index 74eb78be00..b20091b232 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -171,7 +172,7 @@ namespace AZ::Dom::Benchmark for (auto _ : state) { - Value copy = original.DeepCopy(); + Value copy = Utils::DeepCopy(original); TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index 3cbd532b13..a98eb307a2 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -41,10 +41,10 @@ namespace AZ::Dom::Tests { Value shallowCopy = m_value; EXPECT_EQ(m_value, shallowCopy); - EXPECT_TRUE(m_value.DeepCompareIsEqual(shallowCopy)); + EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, shallowCopy)); - Value deepCopy = m_value.DeepCopy(); - EXPECT_TRUE(m_value.DeepCompareIsEqual(deepCopy)); + Value deepCopy = Utils::DeepCopy(m_value); + EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, deepCopy)); } Value m_value; From d347a9d2c034dea3878ce357060b7e040bee6885 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 5 Jan 2022 12:56:22 -0800 Subject: [PATCH 35/49] Fix Linux build (a static_assert unfortunately fires for Clang) Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index af6d86d699..824b38e47b 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -352,7 +352,7 @@ namespace AZ::Dom } else { - static_assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); + AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type"); } }, m_value); From 7d9f9f99e657af79cbe6000ccadfa79272c4410f Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Wed, 5 Jan 2022 14:50:03 -0800 Subject: [PATCH 36/49] fix unit test Signed-off-by: mrieggeramzn --- .../Tests/PropertyIntCtrlCommonTests.h | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h index 712eeb99b7..d4f0c8d705 100644 --- a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h +++ b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h @@ -12,6 +12,7 @@ #include #include "IntegerPrimtitiveTestConfig.h" #include +#include #include namespace UnitTest @@ -83,18 +84,6 @@ namespace UnitTest widget->setMaximum(widget->maximum() - 1); } - static std::string GetToolTipStringAtLimits() - { - if constexpr (std::is_signed::value) - { - return "[-INF, INF]"; - } - else - { - return "[0, INF]"; - } - } - void PropertyCtrlHandlersCreated() { using ::testing::Ne; @@ -125,15 +114,19 @@ namespace UnitTest auto& widget = m_widget; auto& handler = m_handler; QString tooltip; - std::string expected; + std::stringstream expected; // Retrieve the tooltip string for this widget auto success = handler->ModifyTooltip(widget, tooltip); - expected = GetToolTipStringAtLimits(); + + const QString minString = QLocale().toString(widget->minimum()); + const QString maxString = QLocale().toString(widget->maximum()); + + expected << "[" << minString.toStdString() << ", " << maxString.toStdString() << "]"; // Expect the operation to be successful and a valid limit tooltip string generated EXPECT_TRUE(success); - EXPECT_STREQ(tooltip.toStdString().c_str(), expected.c_str()); + EXPECT_STREQ(tooltip.toStdString().c_str(), expected.str().c_str()); } void HandlerMinMaxLessLimit_ModifyHandler_ExpectSuccessAndValidLessLimitToolTipString() @@ -149,7 +142,11 @@ namespace UnitTest // Retrieve the tooltip string for this widget auto success = handler->ModifyTooltip(widget, tooltip); - expected << "[" << widget->minimum() << ", " << widget->maximum() << "]"; + + const QString minString = QLocale().toString(widget->minimum()); + const QString maxString = QLocale().toString(widget->maximum()); + + expected << "[" << minString.toStdString() << ", " << maxString.toStdString() << "]"; // Expect the operation to be successful and a valid less than limit tooltip string generated EXPECT_TRUE(success); From 0bcb514c27299df1f09c8cdfe8e6e94247ff2f89 Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Wed, 5 Jan 2022 15:56:53 -0800 Subject: [PATCH 37/49] removing qlocale.h compile issue Signed-off-by: mrieggeramzn --- .../AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h index d4f0c8d705..1bd6f72a19 100644 --- a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h +++ b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h @@ -12,7 +12,6 @@ #include #include "IntegerPrimtitiveTestConfig.h" #include -#include #include namespace UnitTest From f1c8fbe7c07fadb288466909877f8a445b04e469 Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Thu, 6 Jan 2022 13:45:54 -0800 Subject: [PATCH 38/49] remove std and replace with az Signed-off-by: mrieggeramzn --- .../Tests/PropertyIntCtrlCommonTests.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h index 1bd6f72a19..6710d4b7cd 100644 --- a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h +++ b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h @@ -113,19 +113,17 @@ namespace UnitTest auto& widget = m_widget; auto& handler = m_handler; QString tooltip; - std::stringstream expected; // Retrieve the tooltip string for this widget auto success = handler->ModifyTooltip(widget, tooltip); const QString minString = QLocale().toString(widget->minimum()); const QString maxString = QLocale().toString(widget->maximum()); - - expected << "[" << minString.toStdString() << ", " << maxString.toStdString() << "]"; + const AZStd::string expected = AZStd::string::format("[%d, %d]", minString.toStdString().c_str(), maxString.toStdString().c_str()); // Expect the operation to be successful and a valid limit tooltip string generated EXPECT_TRUE(success); - EXPECT_STREQ(tooltip.toStdString().c_str(), expected.str().c_str()); + EXPECT_STREQ(tooltip.toStdString().c_str(), expected.c_str()); } void HandlerMinMaxLessLimit_ModifyHandler_ExpectSuccessAndValidLessLimitToolTipString() @@ -134,7 +132,6 @@ namespace UnitTest auto& widget = m_widget; auto& handler = m_handler; QString tooltip; - std::stringstream expected; // That is not at the extremeties of the type range limit SetWidgetRangeToNonExtremeties(widget); @@ -145,11 +142,11 @@ namespace UnitTest const QString minString = QLocale().toString(widget->minimum()); const QString maxString = QLocale().toString(widget->maximum()); - expected << "[" << minString.toStdString() << ", " << maxString.toStdString() << "]"; + const AZStd::string expected = AZStd::string::format("[%d, %d]", minString.toStdString().c_str(), maxString.toStdString().c_str()); // Expect the operation to be successful and a valid less than limit tooltip string generated EXPECT_TRUE(success); - EXPECT_STREQ(tooltip.toStdString().c_str(), expected.str().c_str()); + EXPECT_STREQ(tooltip.toStdString().c_str(), expected.c_str()); } void EmitWidgetValueChanged() From 273d6e225aaf76436566cdfb56f53a6f1000e15b Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Thu, 6 Jan 2022 14:00:33 -0800 Subject: [PATCH 39/49] Previous commit wasnt correct. Should be using a string not a int Signed-off-by: mrieggeramzn --- .../AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h index 6710d4b7cd..b73e9e0a26 100644 --- a/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h +++ b/Code/Framework/AzToolsFramework/Tests/PropertyIntCtrlCommonTests.h @@ -119,7 +119,7 @@ namespace UnitTest const QString minString = QLocale().toString(widget->minimum()); const QString maxString = QLocale().toString(widget->maximum()); - const AZStd::string expected = AZStd::string::format("[%d, %d]", minString.toStdString().c_str(), maxString.toStdString().c_str()); + const AZStd::string expected = AZStd::string::format("[%s, %s]", minString.toStdString().c_str(), maxString.toStdString().c_str()); // Expect the operation to be successful and a valid limit tooltip string generated EXPECT_TRUE(success); @@ -142,7 +142,7 @@ namespace UnitTest const QString minString = QLocale().toString(widget->minimum()); const QString maxString = QLocale().toString(widget->maximum()); - const AZStd::string expected = AZStd::string::format("[%d, %d]", minString.toStdString().c_str(), maxString.toStdString().c_str()); + const AZStd::string expected = AZStd::string::format("[%s, %s]", minString.toStdString().c_str(), maxString.toStdString().c_str()); // Expect the operation to be successful and a valid less than limit tooltip string generated EXPECT_TRUE(success); From e7f573d22a37321ecdd6de0578d527f0186115a6 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 6 Jan 2022 16:48:07 -0800 Subject: [PATCH 40/49] Make Value ctor explicit Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 30 ------------ Code/Framework/AzCore/AzCore/DOM/DomValue.h | 49 +++++++++---------- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 8 +-- .../AzCore/Tests/DOM/DomValueTests.cpp | 33 +++++++------ 4 files changed, 44 insertions(+), 76 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 824b38e47b..f48586709d 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -969,16 +969,6 @@ namespace AZ::Dom m_value = value; } - int32_t Value::GetInt32() const - { - return aznumeric_cast(GetInt64()); - } - - void Value::SetInt32(int32_t value) - { - m_value = aznumeric_cast(value); - } - uint64_t Value::GetUint64() const { switch (m_value.index()) @@ -999,16 +989,6 @@ namespace AZ::Dom m_value = value; } - uint32_t Value::GetUint32() const - { - return aznumeric_cast(GetUint64()); - } - - void Value::SetUint32(uint32_t value) - { - m_value = aznumeric_cast(value); - } - bool Value::GetBool() const { if (IsBool()) @@ -1044,16 +1024,6 @@ namespace AZ::Dom m_value = value; } - float Value::GetFloat() const - { - return aznumeric_cast(GetDouble()); - } - - void Value::SetFloat(float value) - { - m_value = aznumeric_cast(value); - } - void Value::SetString(SharedStringType sharedString) { m_value = sharedString; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index a6990fcbce..fa496e8dfc 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -198,28 +198,39 @@ namespace AZ::Dom Value(const Value&); Value(Value&&) noexcept; Value(AZStd::string_view stringView, bool copy); - Value(SharedStringType sharedString); + explicit Value(const ValueType&); + explicit Value(ValueType&&); + explicit Value(SharedStringType sharedString); - Value(int8_t value); - Value(uint8_t value); - Value(int16_t value); - Value(uint16_t value); - Value(int32_t value); - Value(uint32_t value); - Value(int64_t value); - Value(uint64_t value); - Value(float value); - Value(double value); - Value(bool value); + explicit Value(int8_t value); + explicit Value(uint8_t value); + explicit Value(int16_t value); + explicit Value(uint16_t value); + explicit Value(int32_t value); + explicit Value(uint32_t value); + explicit Value(int64_t value); + explicit Value(uint64_t value); + explicit Value(float value); + explicit Value(double value); + explicit Value(bool value); explicit Value(Type type); + template + explicit Value(T, AZStd::enable_if_t>* enabled = 0) = delete; + static Value FromOpaqueValue(const AZStd::any& value); // Equality / comparison / swap... Value& operator=(const Value&); Value& operator=(Value&&) noexcept; + template + Value& operator=(T value) + { + return operator=(Value(value)); + } + bool operator==(const Value& rhs) const; bool operator!=(const Value& rhs) const; @@ -330,22 +341,10 @@ namespace AZ::Dom // int API... int64_t GetInt64() const; void SetInt64(int64_t); - int32_t GetInt32() const; - void SetInt32(int32_t); - int16_t GetInt16() const; - void SetInt16(int16_t); - int8_t GetInt8() const; - void SetInt8(int8_t); // uint API... uint64_t GetUint64() const; void SetUint64(uint64_t); - uint32_t GetUint32() const; - void SetUint32(uint32_t); - uint16_t GetUint16() const; - void SetUint16(uint16_t); - uint8_t GetUint8() const; - void SetUint8(uint8_t); // bool API... bool GetBool() const; @@ -354,8 +353,6 @@ namespace AZ::Dom // double API... double GetDouble() const; void SetDouble(double); - float GetFloat() const; - void SetFloat(float); // String API... AZStd::string_view GetString() const; diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index b20091b232..40b96e148b 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -66,9 +66,9 @@ namespace AZ::Dom::Benchmark { Value entry(Type::Object); entry.AddMember("string", createString(n)); - entry.AddMember("int", n); - entry.AddMember("double", static_cast(n) * 0.5); - entry.AddMember("bool", n % 2 == 0); + entry.AddMember("int", Value(n)); + entry.AddMember("double", Value(static_cast(n) * 0.5)); + entry.AddMember("bool", Value(n % 2 == 0)); entry.AddMember("null", Value(Type::Null)); return entry; }; @@ -153,7 +153,7 @@ namespace AZ::Dom::Benchmark for (auto _ : state) { Value copy = original; - copy["entries"]["Key0"].ArrayPushBack(42); + copy["entries"]["Key0"].ArrayPushBack(Value(42)); TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp index a98eb307a2..10e9f29a44 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -68,7 +68,7 @@ namespace AZ::Dom::Tests { m_value.ArrayPushBack(Value(i)); EXPECT_EQ(m_value.ArraySize(), i + 1); - EXPECT_EQ(m_value[i].GetInt32(), i); + EXPECT_EQ(m_value[i].GetInt64(), i); } PerformValueChecks(); @@ -76,6 +76,7 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, NestedArrays) { + Value x(5); m_value.SetArray(); for (int j = 0; j < 5; ++j) { @@ -93,7 +94,7 @@ namespace AZ::Dom::Tests EXPECT_EQ(m_value[i].ArraySize(), 5); for (int j = 0; j < 5; ++j) { - EXPECT_EQ(m_value[i][j].GetInt32(), j); + EXPECT_EQ(m_value[i][j].GetInt64(), j); } } @@ -116,7 +117,7 @@ namespace AZ::Dom::Tests AZStd::string key = AZStd::string::format("Key%i", i); m_value.AddMember(key, Value(i)); EXPECT_EQ(m_value.MemberCount(), i + 1); - EXPECT_EQ(m_value[key].GetInt32(), i); + EXPECT_EQ(m_value[key].GetInt64(), i); } PerformValueChecks(); @@ -142,7 +143,7 @@ namespace AZ::Dom::Tests EXPECT_EQ(nestedObject.MemberCount(), 5); for (int i = 0; i < 5; ++i) { - EXPECT_EQ(nestedObject[AZStd::string::format("Key%i", i)].GetInt32(), i); + EXPECT_EQ(nestedObject[AZStd::string::format("Key%i", i)].GetInt64(), i); } } @@ -167,14 +168,14 @@ namespace AZ::Dom::Tests { m_value.ArrayPushBack(Value(i)); EXPECT_EQ(m_value.ArraySize(), i + 1); - EXPECT_EQ(m_value[i].GetInt32(), i); + EXPECT_EQ(m_value[i].GetInt64(), i); if (i < 5) { AZ::Name key = AZ::Name(AZStd::string::format("TwoTimes%i", i)); m_value.AddMember(key, Value(i * 2)); EXPECT_EQ(m_value.MemberCount(), i + 1); - EXPECT_EQ(m_value[key].GetInt32(), i * 2); + EXPECT_EQ(m_value[key].GetInt64(), i * 2); } } @@ -191,9 +192,9 @@ namespace AZ::Dom::Tests { Value childNode(Type::Node); childNode.SetNodeName(childNodeName); - childNode.SetNodeValue(i); + childNode.SetNodeValue(Value(i)); - childNode.AddMember("foo", i); + childNode.AddMember("foo", Value(i)); childNode.AddMember("bar", Value("test", false)); m_value.ArrayPushBack(childNode); @@ -204,8 +205,8 @@ namespace AZ::Dom::Tests { const Value& childNode = m_value[i]; EXPECT_EQ(childNode.GetNodeName(), childNodeName); - EXPECT_EQ(childNode.GetNodeValue().GetInt32(), i); - EXPECT_EQ(childNode["foo"].GetInt32(), i); + EXPECT_EQ(childNode.GetNodeValue().GetInt64(), i); + EXPECT_EQ(childNode["foo"].GetInt64(), i); EXPECT_EQ(childNode["bar"].GetString(), "test"); } @@ -334,8 +335,8 @@ namespace AZ::Dom::Tests TEST_F(DomValueTests, CopyOnWrite_Array) { Value v1(Type::Array); - v1.ArrayPushBack(1); - v1.ArrayPushBack(2); + v1.ArrayPushBack(Value(1)); + v1.ArrayPushBack(Value(2)); Value nestedArray(Type::Array); v1.ArrayPushBack(nestedArray); @@ -349,7 +350,7 @@ namespace AZ::Dom::Tests EXPECT_NE(&v1.GetArray(), &v2.GetArray()); EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); - v2[2].ArrayPushBack(42); + v2[2].ArrayPushBack(Value(42)); EXPECT_NE(&v1.GetArray(), &v2.GetArray()); EXPECT_NE(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray()); @@ -365,8 +366,8 @@ namespace AZ::Dom::Tests Value v1; v1.SetNode("TopLevel"); - v1.ArrayPushBack(1); - v1.ArrayPushBack(2); + v1.ArrayPushBack(Value(1)); + v1.ArrayPushBack(Value(2)); v1["obj"].SetNode("Nested"); Value v2 = v1; @@ -378,7 +379,7 @@ namespace AZ::Dom::Tests EXPECT_NE(&v1.GetNode(), &v2.GetNode()); EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode()); - v2["obj"].ArrayPushBack(42); + v2["obj"].ArrayPushBack(Value(42)); EXPECT_NE(&v1.GetNode(), &v2.GetNode()); EXPECT_NE(&v1["obj"].GetNode(), &v2["obj"].GetNode()); From 25924c3d384452e67775b2a4c11a84df09cf99d4 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 6 Jan 2022 16:54:57 -0800 Subject: [PATCH 41/49] Use more explicit operator= override Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index fa496e8dfc..23f4bd570f 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -225,10 +225,12 @@ namespace AZ::Dom Value& operator=(const Value&); Value& operator=(Value&&) noexcept; - template - Value& operator=(T value) + //! Assignment operator to allow forwarding types constructible via Value(T) to be assigned + template + auto operator=(T&& arg) + -> AZStd::enable_if_t, Value> && AZStd::is_constructible_v, Value&> { - return operator=(Value(value)); + return operator=(Value(AZStd::forward(arg))); } bool operator==(const Value& rhs) const; From 839bbd734aea320ebf383c6b390cf52a6d867f54 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 6 Jan 2022 16:55:24 -0800 Subject: [PATCH 42/49] Make operator== avoid implicit comparisons Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index f48586709d..6d944c9f45 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -262,21 +262,6 @@ namespace AZ::Dom { return GetString() == rhs.GetString(); } - else if (IsNumber() && rhs.IsNumber()) - { - if (IsInt()) - { - return GetInt64() == rhs.GetInt64(); - } - else if (IsUint()) - { - return GetUint64() == rhs.GetUint64(); - } - else - { - return GetDouble() == rhs.GetDouble(); - } - } else { return m_value == rhs.m_value; From 8b9e3d2175bb7766e0e9e81f0bba310cafd14a90 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Fri, 7 Jan 2022 09:53:14 -0800 Subject: [PATCH 43/49] Simplify disabling Value ctor for pointer types Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 23f4bd570f..ecf8326525 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -216,8 +216,9 @@ namespace AZ::Dom explicit Value(Type type); + // Disable accidental calls to Value(bool) with pointer types template - explicit Value(T, AZStd::enable_if_t>* enabled = 0) = delete; + explicit Value(T*) = delete; static Value FromOpaqueValue(const AZStd::any& value); From 62e7483b0e7a025369c09fcd92bcf326ea71a131 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:44:59 -0600 Subject: [PATCH 44/49] Switched GradientSignal to use GemTestEnvironment. (#6886) This allows actual Shape components to be used instead of MockShapes, which is important for the benchmarks to get accurate results as Mocks are extremely expensive. It also removes a lot of unnecessary mock handling and test setup code. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../EditorGradientSignalPreviewTests.cpp | 38 +++-- .../Code/Tests/GradientSignalImageTests.cpp | 31 ++-- .../Tests/GradientSignalReferencesTests.cpp | 38 ++--- .../Tests/GradientSignalServicesTests.cpp | 10 +- .../Code/Tests/GradientSignalSurfaceTests.cpp | 10 +- .../Code/Tests/GradientSignalTest.cpp | 47 +++--- .../Code/Tests/GradientSignalTestFixtures.cpp | 134 ++++++++---------- .../Code/Tests/GradientSignalTestFixtures.h | 84 +++++------ .../Code/Tests/ImageAssetTests.cpp | 21 +-- 9 files changed, 193 insertions(+), 220 deletions(-) diff --git a/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp b/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp index e8bd9f2dff..83892f1c69 100644 --- a/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp +++ b/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp @@ -29,21 +29,34 @@ namespace UnitTest { GradientSignalTest::SetUp(); - // Set up job manager with two threads so that we can run and test the preview job logic. - AZ::JobManagerDesc desc; - AZ::JobManagerThreadDesc threadDesc; - desc.m_workerThreads.push_back(threadDesc); - desc.m_workerThreads.push_back(threadDesc); - m_jobManager = aznew AZ::JobManager(desc); - m_jobContext = aznew AZ::JobContext(*m_jobManager); - AZ::JobContext::SetGlobalContext(m_jobContext); + auto globalContext = AZ::JobContext::GetGlobalContext(); + if (globalContext) + { + AZ_Assert( + globalContext->GetJobManager().GetNumWorkerThreads() >= 2, + "Job Manager previously started by test environment with too few threads for this test."); + } + else + { + // Set up job manager with two threads so that we can run and test the preview job logic. + AZ::JobManagerDesc desc; + AZ::JobManagerThreadDesc threadDesc; + desc.m_workerThreads.push_back(threadDesc); + desc.m_workerThreads.push_back(threadDesc); + m_jobManager = aznew AZ::JobManager(desc); + m_jobContext = aznew AZ::JobContext(*m_jobManager); + AZ::JobContext::SetGlobalContext(m_jobContext); + } } void TearDown() override { - AZ::JobContext::SetGlobalContext(nullptr); - delete m_jobContext; - delete m_jobManager; + if (m_jobContext) + { + AZ::JobContext::SetGlobalContext(nullptr); + delete m_jobContext; + delete m_jobManager; + } GradientSignalTest::TearDown(); } @@ -180,4 +193,5 @@ namespace UnitTest } } -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); +// This uses a custom test hook so that we can load LmbrCentral and use Shape components in our unit tests. +AZ_UNIT_TEST_HOOK(new UnitTest::GradientSignalTestEnvironment); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp index 5cd3c7edf6..8ec190000d 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp @@ -14,10 +14,13 @@ #include #include #include +#include #include #include +#include + namespace UnitTest { struct GradientSignalImageTestsFixture @@ -89,23 +92,21 @@ namespace UnitTest test.m_imageSize, test.m_imageSize, static_cast(test.m_pixel.GetX()), static_cast(test.m_pixel.GetY())); config.m_tilingX = test.m_tiling; config.m_tilingY = test.m_tiling; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); // Create the Gradient Transform Component. GradientSignal::GradientTransformConfig gradientTransformConfig; gradientTransformConfig.m_wrappingType = test.m_wrappingType; - CreateComponent(entity.get(), gradientTransformConfig); + entity->CreateComponent(gradientTransformConfig); - // Create a mock Shape component that describes the bounds that we're using to map our ImageGradient into world space. - CreateComponent(entity.get()); - MockShapeComponentHandler mockShapeHandler(entity->GetId()); - mockShapeHandler.m_GetLocalBounds = AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds); + LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f)); + auto boxComponent = entity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId); + boxComponent->SetConfiguration(boxConfig); - // Create a mock Transform component that locates our ImageGradient in the center of our desired mock Shape. - MockTransformHandler mockTransformHandler; - mockTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)); - mockTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)); - mockTransformHandler.BusConnect(entity->GetId()); + // Create a transform that locates our gradient in the center of our desired mock Shape. + auto transform = entity->CreateComponent(); + transform->SetLocalTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds))); + transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds))); // All components are created, so activate the entity ActivateEntity(entity.get()); @@ -365,7 +366,7 @@ namespace UnitTest mockShapeTransformHandler.BusConnect(mockShape->GetId()); // Create the mock shape that maps our 3x3 image to a 3x3 sample space in the world. - CreateComponent(mockShape.get()); + mockShape->CreateComponent(); MockShapeComponentHandler mockShapeComponentHandler(mockShape->GetId()); // Create a 2x2 box shape (shapes are inclusive, so that's 3x3 sampling space), so that each pixel in the image directly maps to 1 meter in the box. mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(2.0f)); @@ -379,7 +380,7 @@ namespace UnitTest // Create an ImageGradient with a 3x3 asset with the center pixel set. GradientSignal::ImageGradientConfig gradientConfig; gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1); - CreateComponent(entity.get(), gradientConfig); + entity->CreateComponent(gradientConfig); // Create the test GradientTransform GradientSignal::GradientTransformConfig config; @@ -400,7 +401,7 @@ namespace UnitTest config.m_overrideRotate = false; config.m_overrideScale = false; config.m_is3d = false; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); // Set up the transform on the gradient entity. MockTransformHandler mockTransformHandler; @@ -409,7 +410,7 @@ namespace UnitTest mockTransformHandler.BusConnect(entity->GetId()); // Put a default shape on our gradient entity. This is only used for previews, so it doesn't matter what it gets set to. - CreateComponent(entity.get()); + entity->CreateComponent(); MockShapeComponentHandler mockShapeHandler(entity->GetId()); ActivateEntity(entity.get()); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp index cc91c58fce..72bdfa202e 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp @@ -55,7 +55,7 @@ namespace UnitTest config.m_layers.push_back(layer); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -88,7 +88,7 @@ namespace UnitTest config.m_smoothStep.m_falloffStrength = falloffStrength; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -391,7 +391,7 @@ namespace UnitTest GradientSignal::ReferenceGradientConfig referenceGradientConfig1; referenceGradientConfig1.m_gradientSampler.m_ownerEntityId = referenceGradientEntity1->GetId(); referenceGradientConfig1.m_gradientSampler.m_gradientId = constantGradientEntity->GetId(); - CreateComponent(referenceGradientEntity1.get(), referenceGradientConfig1); + referenceGradientEntity1->CreateComponent(referenceGradientConfig1); ActivateEntity(referenceGradientEntity1.get()); EXPECT_TRUE(referenceGradientConfig1.m_gradientSampler.ValidateGradientEntityId()); @@ -400,7 +400,7 @@ namespace UnitTest GradientSignal::ReferenceGradientConfig referenceGradientConfig2; referenceGradientConfig2.m_gradientSampler.m_ownerEntityId = referenceGradientEntity2->GetId(); referenceGradientConfig2.m_gradientSampler.m_gradientId = referenceGradientEntity1->GetId(); - CreateComponent(referenceGradientEntity2.get(), referenceGradientConfig2); + referenceGradientEntity2->CreateComponent(referenceGradientConfig2); ActivateEntity(referenceGradientEntity2.get()); EXPECT_TRUE(referenceGradientConfig2.m_gradientSampler.ValidateGradientEntityId()); @@ -409,7 +409,7 @@ namespace UnitTest GradientSignal::ReferenceGradientConfig referenceGradientConfig3; referenceGradientConfig3.m_gradientSampler.m_ownerEntityId = referenceGradientEntity3->GetId(); referenceGradientConfig3.m_gradientSampler.m_gradientId = referenceGradientEntity3->GetId(); - CreateComponent(referenceGradientEntity3.get(), referenceGradientConfig3); + referenceGradientEntity3->CreateComponent(referenceGradientConfig3); ActivateEntity(referenceGradientEntity3.get()); EXPECT_FALSE(referenceGradientConfig3.m_gradientSampler.ValidateGradientEntityId()); EXPECT_EQ(referenceGradientConfig3.m_gradientSampler.m_gradientId, AZ::EntityId()); @@ -422,19 +422,19 @@ namespace UnitTest GradientSignal::ReferenceGradientConfig referenceGradientConfig4; referenceGradientConfig4.m_gradientSampler.m_ownerEntityId = referenceGradientEntity4->GetId(); referenceGradientConfig4.m_gradientSampler.m_gradientId = referenceGradientEntity5->GetId(); - CreateComponent(referenceGradientEntity4.get(), referenceGradientConfig4); + referenceGradientEntity4->CreateComponent(referenceGradientConfig4); ActivateEntity(referenceGradientEntity4.get()); GradientSignal::ReferenceGradientConfig referenceGradientConfig5; referenceGradientConfig5.m_gradientSampler.m_ownerEntityId = referenceGradientEntity5->GetId(); referenceGradientConfig5.m_gradientSampler.m_gradientId = referenceGradientEntity6->GetId(); - CreateComponent(referenceGradientEntity5.get(), referenceGradientConfig5); + referenceGradientEntity5->CreateComponent(referenceGradientConfig5); ActivateEntity(referenceGradientEntity5.get()); GradientSignal::ReferenceGradientConfig referenceGradientConfig6; referenceGradientConfig6.m_gradientSampler.m_ownerEntityId = referenceGradientEntity6->GetId(); referenceGradientConfig6.m_gradientSampler.m_gradientId = referenceGradientEntity4->GetId(); - CreateComponent(referenceGradientEntity6.get(), referenceGradientConfig6); + referenceGradientEntity6->CreateComponent(referenceGradientConfig6); ActivateEntity(referenceGradientEntity6.get()); EXPECT_FALSE(referenceGradientConfig6.m_gradientSampler.ValidateGradientEntityId()); @@ -456,7 +456,7 @@ namespace UnitTest // Create an AABB from -1 to 1, so points at coorindates 0 and 1 fall on it, but any points at coordinate 2 won't. auto entityShape = CreateEntity(); - CreateComponent(entityShape.get()); + entityShape->CreateComponent(); MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId()); mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f)); @@ -466,7 +466,7 @@ namespace UnitTest config.m_falloffType = GradientSignal::FalloffType::Outer; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -481,7 +481,7 @@ namespace UnitTest // Create our test shape from -1 to 0, so we have a corner directly on (0, 0). auto entityShape = CreateEntity(); - CreateComponent(entityShape.get()); + entityShape->CreateComponent(); MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId()); mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(0.0f)); @@ -512,7 +512,7 @@ namespace UnitTest } auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -533,7 +533,7 @@ namespace UnitTest // We're pinning a shape, so the bounding box of (0, 0, 0) - (10, 10, 10) will be the one that applies. auto entityShape = CreateEntity(); - CreateComponent(entityShape.get()); + entityShape->CreateComponent(); MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId()); mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3(10.0f)); @@ -551,7 +551,7 @@ namespace UnitTest config.m_altitudeMax = 24.0f; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -584,7 +584,7 @@ namespace UnitTest config.m_altitudeMax = 10.0f; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -612,7 +612,7 @@ namespace UnitTest config.m_altitudeMax = 15.0f; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -649,7 +649,7 @@ namespace UnitTest config.m_altitudeMax = 15.0f; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -684,7 +684,7 @@ namespace UnitTest config.m_surfaceTagList.push_back(AZ_CRC("test_mask", 0x7a16e9ff)); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -711,7 +711,7 @@ namespace UnitTest config.m_surfaceTagList.push_back(AZ_CRC("test_mask", 0x7a16e9ff)); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp index ec770f038d..449719af67 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp @@ -31,7 +31,7 @@ namespace UnitTest config.m_value = expectedOutput; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); GradientSignal::GradientSampler gradientSampler; @@ -75,7 +75,7 @@ namespace UnitTest config.m_gradientSampler.m_gradientId = entityMock->GetId(); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -109,7 +109,7 @@ namespace UnitTest config.m_gradientSampler.m_gradientId = entityMock->GetId(); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -147,7 +147,7 @@ namespace UnitTest config.m_gradientSampler.m_gradientId = entityMock->GetId(); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -185,7 +185,7 @@ namespace UnitTest config.m_gradientSampler.m_gradientId = entityMock->GetId(); auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp index 702965c0de..a716fc79b8 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp @@ -44,11 +44,9 @@ namespace UnitTest // This lets our component register with surfaceData successfully. MockSurfaceDataSystem mockSurfaceDataSystem; - // Create a mock shape entity in case we want to use it. + // Create a mock shape entity in case our gradient test uses shape constraints. // The mock shape is a cube that goes from -0.5 to 0.5 in space. - auto mockShapeEntity = CreateEntity(); - CreateComponent(mockShapeEntity.get()); - MockShapeComponentHandler mockShapeHandler(mockShapeEntity->GetId()); + auto mockShapeEntity = CreateTestEntity(0.5f); ActivateEntity(mockShapeEntity.get()); // For ease of testing, use a constant gradient as our input gradient. @@ -76,8 +74,8 @@ namespace UnitTest // Create the test entity with the GradientSurfaceData component and the required gradient dependency auto entity = CreateEntity(); - CreateComponent(entity.get(), constantGradientConfig); - CreateComponent(entity.get(), config); + entity->CreateComponent(constantGradientConfig); + entity->CreateComponent(config); ActivateEntity(entity.get()); // Get our registered modifier handle (and verify that it's valid) diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp index adc35e6738..13e535e072 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp @@ -27,14 +27,12 @@ namespace UnitTest void TestLevelsGradientComponent(int dataSize, const AZStd::vector& inputData, const AZStd::vector& expectedOutput, float inputMin, float inputMid, float inputMax, float outputMin, float outputMax) { - auto entityMock = CreateEntity(); + auto entityMock = CreateTestEntity(1.0f); const AZ::EntityId id = entityMock->GetId(); UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entityMock.get(), gradientTransformConfig); - CreateComponent(entityMock.get()); - MockShapeComponentHandler mockShapeHandler(entityMock->GetId()); + entityMock->CreateComponent(gradientTransformConfig); ActivateEntity(entityMock.get()); @@ -47,7 +45,7 @@ namespace UnitTest config.m_outputMax = outputMax; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -56,14 +54,12 @@ namespace UnitTest void TestPosterizeGradientComponent(int dataSize, const AZStd::vector& inputData, const AZStd::vector& expectedOutput, GradientSignal::PosterizeGradientConfig::ModeType posterizeMode, int bands) { - auto entityMock = CreateEntity(); + auto entityMock = CreateTestEntity(0.5f); const AZ::EntityId id = entityMock->GetId(); UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entityMock.get(), gradientTransformConfig); - CreateComponent(entityMock.get()); - MockShapeComponentHandler mockShapeHandler(entityMock->GetId()); + entityMock->CreateComponent(gradientTransformConfig); ActivateEntity(entityMock.get()); @@ -73,7 +69,7 @@ namespace UnitTest config.m_bands = bands; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -82,14 +78,12 @@ namespace UnitTest void TestSmoothStepGradientComponent(int dataSize, const AZStd::vector& inputData, const AZStd::vector& expectedOutput, float midpoint, float range, float softness) { - auto entityMock = CreateEntity(); + auto entityMock = CreateTestEntity(0.5f); const AZ::EntityId id = entityMock->GetId(); UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entityMock.get(), gradientTransformConfig); - CreateComponent(entityMock.get()); - MockShapeComponentHandler mockShapeHandler(entityMock->GetId()); + entityMock->CreateComponent(gradientTransformConfig); ActivateEntity(entityMock.get()); @@ -100,7 +94,7 @@ namespace UnitTest config.m_smoothStep.m_falloffStrength = softness; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -108,14 +102,12 @@ namespace UnitTest void TestThresholdGradientComponent(int dataSize, const AZStd::vector& inputData, const AZStd::vector& expectedOutput, float threshold) { - auto entityMock = CreateEntity(); + auto entityMock = CreateTestEntity(0.5f); const AZ::EntityId id = entityMock->GetId(); UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entityMock.get(), gradientTransformConfig); - CreateComponent(entityMock.get()); - MockShapeComponentHandler mockShapeHandler(entityMock->GetId()); + entityMock->CreateComponent(gradientTransformConfig); ActivateEntity(entityMock.get()); @@ -124,7 +116,7 @@ namespace UnitTest config.m_threshold = threshold; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); TestFixedDataSampler(expectedOutput, dataSize, entity->GetId()); @@ -167,11 +159,11 @@ namespace UnitTest AZStd::vector expectedOutput = { AZ_TRAIT_UNIT_TEST_PERLINE_GRADIANT_GOLDEN_VALUES_7878 }; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entity.get(), gradientTransformConfig); - CreateComponent(entity.get()); + entity->CreateComponent(gradientTransformConfig); + entity->CreateComponent(); MockShapeComponentHandler mockShapeHandler(entity->GetId()); ActivateEntity(entity.get()); @@ -197,11 +189,11 @@ namespace UnitTest config.m_randomSeed = 5656; auto entity = CreateEntity(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); GradientSignal::GradientTransformConfig gradientTransformConfig; - CreateComponent(entity.get(), gradientTransformConfig); - CreateComponent(entity.get()); + entity->CreateComponent(gradientTransformConfig); + entity->CreateComponent(); MockShapeComponentHandler mockShapeHandler(entity->GetId()); ActivateEntity(entity.get()); @@ -546,4 +538,5 @@ namespace UnitTest } } -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); +// This uses custom test / benchmark hooks so that we can load LmbrCentral and use Shape components in our unit tests and benchmarks. +AZ_UNIT_TEST_HOOK(new UnitTest::GradientSignalTestEnvironment, UnitTest::GradientSignalBenchmarkEnvironment); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp index 154e9032b2..d81370f6a8 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp @@ -10,7 +10,9 @@ #include #include +#include #include +#include // Base gradient components #include @@ -36,65 +38,48 @@ namespace UnitTest { + void GradientSignalTestEnvironment::AddGemsAndComponents() + { + AddDynamicModulePaths({ "LmbrCentral" }); + + AddComponentDescriptors({ + AzFramework::TransformComponent::CreateDescriptor(), + + GradientSignal::ConstantGradientComponent::CreateDescriptor(), + GradientSignal::DitherGradientComponent::CreateDescriptor(), + GradientSignal::GradientSurfaceDataComponent::CreateDescriptor(), + GradientSignal::GradientTransformComponent::CreateDescriptor(), + GradientSignal::ImageGradientComponent::CreateDescriptor(), + GradientSignal::InvertGradientComponent::CreateDescriptor(), + GradientSignal::LevelsGradientComponent::CreateDescriptor(), + GradientSignal::MixedGradientComponent::CreateDescriptor(), + GradientSignal::PerlinGradientComponent::CreateDescriptor(), + GradientSignal::PosterizeGradientComponent::CreateDescriptor(), + GradientSignal::RandomGradientComponent::CreateDescriptor(), + GradientSignal::ReferenceGradientComponent::CreateDescriptor(), + GradientSignal::ShapeAreaFalloffGradientComponent::CreateDescriptor(), + GradientSignal::SmoothStepGradientComponent::CreateDescriptor(), + GradientSignal::SurfaceAltitudeGradientComponent::CreateDescriptor(), + GradientSignal::SurfaceMaskGradientComponent::CreateDescriptor(), + GradientSignal::SurfaceSlopeGradientComponent::CreateDescriptor(), + GradientSignal::ThresholdGradientComponent::CreateDescriptor(), + + MockShapeComponent::CreateDescriptor(), + }); + } + void GradientSignalBaseFixture::SetupCoreSystems() { - m_app = AZStd::make_unique(); - ASSERT_TRUE(m_app != nullptr); - - AZ::ComponentApplication::Descriptor componentAppDesc; - - m_systemEntity = m_app->Create(componentAppDesc); - ASSERT_TRUE(m_systemEntity != nullptr); - m_app->AddEntity(m_systemEntity); - - AZ::AllocatorInstance::Create(); - AZ::Data::AssetManager::Descriptor desc; - AZ::Data::AssetManager::Create(desc); - m_mockHandler = new ImageAssetMockAssetHandler(); + m_mockHandler = new UnitTest::ImageAssetMockAssetHandler(); AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid()); - - m_mockShapeHandlers = new AZStd::vector>>(); } void GradientSignalBaseFixture::TearDownCoreSystems() { - // Clear any mock shape handlers that we've created for our test entities. - delete m_mockShapeHandlers; - AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler); delete m_mockHandler; // delete after removing from the asset manager AzFramework::LegacyAssetEventBus::ClearQueuedEvents(); - AZ::Data::AssetManager::Destroy(); - AZ::AllocatorInstance::Destroy(); - - m_app->Destroy(); - m_app.reset(); - m_systemEntity = nullptr; - } - - AZStd::unique_ptr> GradientSignalBaseFixture::CreateMockShape( - const AZ::Aabb& spawnerBox, const AZ::EntityId& shapeEntityId) - { - AZStd::unique_ptr> mockShape = - AZStd::make_unique>(shapeEntityId); - - ON_CALL(*mockShape, GetEncompassingAabb).WillByDefault(testing::Return(spawnerBox)); - ON_CALL(*mockShape, GetTransformAndLocalBounds) - .WillByDefault( - [spawnerBox](AZ::Transform& transform, AZ::Aabb& bounds) - { - transform = AZ::Transform::CreateTranslation(spawnerBox.GetCenter()); - bounds = spawnerBox.GetTranslated(-spawnerBox.GetCenter()); - }); - ON_CALL(*mockShape, IsPointInside) - .WillByDefault( - [spawnerBox](const AZ::Vector3& point) -> bool - { - return spawnerBox.Contains(point); - }); - - return mockShape; } AZStd::unique_ptr GradientSignalBaseFixture::CreateMockSurfaceDataSystem(const AZ::Aabb& spawnerBox) @@ -130,16 +115,12 @@ namespace UnitTest // Create the base entity AZStd::unique_ptr testEntity = CreateEntity(); - // Create a mock Shape component that describes the bounds that we're using to map our gradient into world space. - CreateComponent(testEntity.get()); - - // Create and keep a reference to a mock shape handler that will respond to shape requests for the mock shape. - auto mockShapeHandler = - CreateMockShape(AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds), testEntity->GetId()); - m_mockShapeHandlers->push_back(AZStd::move(mockShapeHandler)); + LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f)); + auto boxComponent = testEntity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId); + boxComponent->SetConfiguration(boxConfig); // Create a transform that locates our gradient in the center of our desired mock Shape. - auto transform = CreateComponent(testEntity.get()); + auto transform = testEntity->CreateComponent(); transform->SetLocalTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds))); transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds))); @@ -152,7 +133,7 @@ namespace UnitTest auto entity = CreateTestEntity(shapeHalfBounds); GradientSignal::ConstantGradientConfig config; config.m_value = 0.75f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -168,12 +149,12 @@ namespace UnitTest config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed); config.m_tilingX = 1.0f; config.m_tilingY = 1.0f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); // Create a Gradient Transform Component with arbitrary parameters. GradientSignal::GradientTransformConfig gradientTransformConfig; gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None; - CreateComponent(entity.get(), gradientTransformConfig); + entity->CreateComponent(gradientTransformConfig); ActivateEntity(entity.get()); return entity; @@ -188,12 +169,12 @@ namespace UnitTest config.m_frequency = 1.1f; config.m_octave = 4; config.m_randomSeed = 12345; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); // Create a Gradient Transform Component with arbitrary parameters. GradientSignal::GradientTransformConfig gradientTransformConfig; gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None; - CreateComponent(entity.get(), gradientTransformConfig); + entity->CreateComponent(gradientTransformConfig); ActivateEntity(entity.get()); return entity; @@ -205,12 +186,12 @@ namespace UnitTest auto entity = CreateTestEntity(shapeHalfBounds); GradientSignal::RandomGradientConfig config; config.m_randomSeed = 12345; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); // Create a Gradient Transform Component with arbitrary parameters. GradientSignal::GradientTransformConfig gradientTransformConfig; gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None; - CreateComponent(entity.get(), gradientTransformConfig); + entity->CreateComponent(gradientTransformConfig); ActivateEntity(entity.get()); return entity; @@ -224,7 +205,7 @@ namespace UnitTest config.m_shapeEntityId = entity->GetId(); config.m_falloffWidth = 16.0f; config.m_falloffType = GradientSignal::FalloffType::InnerOuter; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -238,10 +219,11 @@ namespace UnitTest GradientSignal::DitherGradientConfig config; config.m_gradientSampler.m_gradientId = inputGradientId; config.m_useSystemPointsPerUnit = false; - config.m_pointsPerUnit = 1.0f; + // Use a number other than 1.0f for pointsPerUnit to ensure the dither math is getting exercised properly. + config.m_pointsPerUnit = 0.25f; config.m_patternOffset = AZ::Vector3::CreateZero(); config.m_patternType = GradientSignal::DitherGradientConfig::BayerPatternType::PATTERN_SIZE_4x4; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -254,7 +236,7 @@ namespace UnitTest auto entity = CreateTestEntity(shapeHalfBounds); GradientSignal::InvertGradientConfig config; config.m_gradientSampler.m_gradientId = inputGradientId; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -272,7 +254,7 @@ namespace UnitTest config.m_inputMax = 0.9f; config.m_outputMin = 0.0f; config.m_outputMax = 1.0f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -298,7 +280,7 @@ namespace UnitTest layer.m_gradientSampler.m_opacity = 0.75f; config.m_layers.push_back(layer); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -313,7 +295,7 @@ namespace UnitTest config.m_gradientSampler.m_gradientId = inputGradientId; config.m_mode = GradientSignal::PosterizeGradientConfig::ModeType::Ps; config.m_bands = 5; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -327,7 +309,7 @@ namespace UnitTest GradientSignal::ReferenceGradientConfig config; config.m_gradientSampler.m_gradientId = inputGradientId; config.m_gradientSampler.m_ownerEntityId = entity->GetId(); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -343,7 +325,7 @@ namespace UnitTest config.m_smoothStep.m_falloffMidpoint = 0.75f; config.m_smoothStep.m_falloffRange = 0.125f; config.m_smoothStep.m_falloffStrength = 0.25f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -357,7 +339,7 @@ namespace UnitTest GradientSignal::ThresholdGradientConfig config; config.m_gradientSampler.m_gradientId = inputGradientId; config.m_threshold = 0.75f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -370,7 +352,7 @@ namespace UnitTest GradientSignal::SurfaceAltitudeGradientConfig config; config.m_altitudeMin = -5.0f; config.m_altitudeMax = 15.0f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -382,7 +364,7 @@ namespace UnitTest auto entity = CreateTestEntity(shapeHalfBounds); GradientSignal::SurfaceMaskGradientConfig config; config.m_surfaceTagList.push_back(AZ_CRC_CE("test_mask")); - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; @@ -399,7 +381,7 @@ namespace UnitTest config.m_smoothStep.m_falloffMidpoint = 0.75f; config.m_smoothStep.m_falloffRange = 0.125f; config.m_smoothStep.m_falloffStrength = 0.25f; - CreateComponent(entity.get(), config); + entity->CreateComponent(config); ActivateEntity(entity.get()); return entity; diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h index 478f1c1d92..5fda88ea27 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h @@ -9,9 +9,39 @@ #include #include +#include namespace UnitTest { + // The GradientSignal unit tests need to use the GemTestEnvironment to load the LmbrCentral Gem so that Shape components can be used + // in the unit tests and benchmarks. + class GradientSignalTestEnvironment + : public AZ::Test::GemTestEnvironment + { + public: + void AddGemsAndComponents() override; + }; + +#ifdef HAVE_BENCHMARK + //! The Benchmark environment is used for one time setup and tear down of shared resources + class GradientSignalBenchmarkEnvironment + : public AZ::Test::BenchmarkEnvironmentBase + , public GradientSignalTestEnvironment + + { + protected: + void SetUpBenchmark() override + { + SetupEnvironment(); + } + + void TearDownBenchmark() override + { + TeardownEnvironment(); + } + }; +#endif + // Base test fixture used for GradientSignal unit tests and benchmark tests class GradientSignalBaseFixture { @@ -30,24 +60,6 @@ namespace UnitTest entity->Activate(); } - template - Component* CreateComponent(AZ::Entity* entity, const Configuration& config) - { - m_app->RegisterComponentDescriptor(Component::CreateDescriptor()); - return entity->CreateComponent(config); - } - - template - Component* CreateComponent(AZ::Entity* entity) - { - m_app->RegisterComponentDescriptor(Component::CreateDescriptor()); - return entity->CreateComponent(); - } - - // Create a mock shape that will respond to the shape bus with proper responses for the given input box. - AZStd::unique_ptr> CreateMockShape( - const AZ::Aabb& spawnerBox, const AZ::EntityId& shapeEntityId); - // Create a mock SurfaceDataSystem that will respond to requests for surface points with mock responses for points inside // the given input box. AZStd::unique_ptr CreateMockSurfaceDataSystem(const AZ::Aabb& spawnerBox); @@ -77,27 +89,22 @@ namespace UnitTest AZStd::unique_ptr BuildTestSurfaceMaskGradient(float shapeHalfBounds); AZStd::unique_ptr BuildTestSurfaceSlopeGradient(float shapeHalfBounds); - AZStd::unique_ptr m_app; - AZ::Entity* m_systemEntity = nullptr; - ImageAssetMockAssetHandler* m_mockHandler = nullptr; - AZStd::vector>>* m_mockShapeHandlers = nullptr; + UnitTest::ImageAssetMockAssetHandler* m_mockHandler = nullptr; }; struct GradientSignalTest : public GradientSignalBaseFixture - , public UnitTest::AllocatorsTestFixture + , public ::testing::Test { protected: void SetUp() override { - UnitTest::AllocatorsTestFixture::SetUp(); SetupCoreSystems(); } void TearDown() override { TearDownCoreSystems(); - UnitTest::AllocatorsTestFixture::TearDown(); } void TestFixedDataSampler(const AZStd::vector& expectedOutput, int size, AZ::EntityId gradientEntityId); @@ -106,41 +113,36 @@ namespace UnitTest #ifdef HAVE_BENCHMARK class GradientSignalBenchmarkFixture : public GradientSignalBaseFixture - , public UnitTest::AllocatorsBenchmarkFixture - , public UnitTest::TraceBusRedirector + , public ::benchmark::Fixture { public: - void internalSetUp(const benchmark::State& state) + void internalSetUp() { - AZ::Debug::TraceMessageBus::Handler::BusConnect(); - UnitTest::AllocatorsBenchmarkFixture::SetUp(state); SetupCoreSystems(); } - void internalTearDown(const benchmark::State& state) + void internalTearDown() { TearDownCoreSystems(); - UnitTest::AllocatorsBenchmarkFixture::TearDown(state); - AZ::Debug::TraceMessageBus::Handler::BusDisconnect(); } protected: - void SetUp(const benchmark::State& state) override + void SetUp([[maybe_unused]] const benchmark::State& state) override { - internalSetUp(state); + internalSetUp(); } - void SetUp(benchmark::State& state) override + void SetUp([[maybe_unused]] benchmark::State& state) override { - internalSetUp(state); + internalSetUp(); } - void TearDown(const benchmark::State& state) override + void TearDown([[maybe_unused]] const benchmark::State& state) override { - internalTearDown(state); + internalTearDown(); } - void TearDown(benchmark::State& state) override + void TearDown([[maybe_unused]] benchmark::State& state) override { - internalTearDown(state); + internalTearDown(); } }; #endif diff --git a/Gems/GradientSignal/Code/Tests/ImageAssetTests.cpp b/Gems/GradientSignal/Code/Tests/ImageAssetTests.cpp index 0111f10c3a..7b1c260d45 100644 --- a/Gems/GradientSignal/Code/Tests/ImageAssetTests.cpp +++ b/Gems/GradientSignal/Code/Tests/ImageAssetTests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -64,26 +65,8 @@ namespace } } - class ImageAssetTest - : public ::testing::Test + class ImageAssetTest : public ::testing::Test { - protected: - AZ::ComponentApplication m_app; - AZ::Entity* m_systemEntity = nullptr; - - void SetUp() override - { - AZ::ComponentApplication::Descriptor appDesc; - appDesc.m_memoryBlocksByteSize = 128 * 1024 * 1024; - m_systemEntity = m_app.Create(appDesc); - m_app.AddEntity(m_systemEntity); - } - - void TearDown() override - { - m_app.Destroy(); - m_systemEntity = nullptr; - } }; #if AZ_TRAIT_DISABLE_FAILED_GRADIENT_SIGNAL_TESTS From 79431f5e5fbec10608e0de865c6474b0bead2c32 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:50:54 -0600 Subject: [PATCH 45/49] First batch of GetValues() overrides (#6852) * Benchmarks and tests for Image and Constant GetValues Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Verify GetValues for Perlin and Random Gradients Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Standardized the assert format for GetValues(). Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * More GetValues unit tests and test cleanup Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed typos Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * GetValues() unit tests for surface gradients. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Benchmarks for ShapeAreaFalloff Gradient Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added benchmarks for all remaining gradients and cleaned up the helper methods. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Renamed class for better report formatting. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added missing Mocks dependencies for the Editor tests. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * First batch of specific GetValues() overrides. Each one is measurably faster than the generic version. Also, in ShapeAreaFalloffGradient, I optimized and simplified the logic a bit, so GetValue() is marginally faster too. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Components/ConstantGradientComponent.h | 1 + .../Components/ImageGradientComponent.h | 1 + .../Components/PerlinGradientComponent.h | 1 + .../Components/RandomGradientComponent.h | 3 + .../ShapeAreaFalloffGradientComponent.h | 1 + .../Components/ConstantGradientComponent.cpp | 16 +++++ .../Components/ImageGradientComponent.cpp | 33 ++++++++++ .../Components/PerlinGradientComponent.cpp | 34 ++++++++++ .../Components/RandomGradientComponent.cpp | 65 +++++++++++++++---- .../ShapeAreaFalloffGradientComponent.cpp | 59 ++++++++++++++--- 10 files changed, 194 insertions(+), 20 deletions(-) diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h index cb27914b8a..af896f0933 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h @@ -62,6 +62,7 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientRequestBus float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; protected: ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h index 8214436f83..9fc98124cb 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h @@ -69,6 +69,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; // AZ::Data::AssetBus overrides... void OnAssetReady(AZ::Data::Asset asset) override; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h index ef171f5319..1b39f7f17b 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h @@ -70,6 +70,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; private: PerlinGradientConfig m_configuration; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h index b0dbd964a0..274442a2c0 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h @@ -61,6 +61,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; private: RandomGradientConfig m_configuration; @@ -73,5 +74,7 @@ namespace GradientSignal // RandomGradientRequestBus overrides... int GetRandomSeed() const override; void SetRandomSeed(int seed) override; + + float GetRandomValue(const AZ::Vector3& position, AZStd::size_t seed) const; }; } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h index d7c2344fd2..df86c38069 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h @@ -69,6 +69,7 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientRequestBus float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; protected: ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp index 86e8a5abc6..2b1487f75e 100644 --- a/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp @@ -134,6 +134,22 @@ namespace GradientSignal return m_configuration.m_value; } + void ConstantGradientComponent::GetValues( + [[maybe_unused]] AZStd::array_view positions, AZStd::array_view outValues) const + { + if (positions.size() != outValues.size()) + { + AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size()); + return; + } + + for (auto& outValue : outValues) + { + float& writableOutValue = const_cast(outValue); + writableOutValue = m_configuration.m_value; + } + } + float ConstantGradientComponent::GetConstantValue() const { return m_configuration.m_value; diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index 269bfbc2df..06a3674188 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -219,6 +219,39 @@ namespace GradientSignal return 0.0f; } + void ImageGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view outValues) const + { + if (positions.size() != outValues.size()) + { + AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size()); + return; + } + + AZ::Vector3 uvw; + bool wasPointRejected = false; + + AZStd::shared_lock imageLock(m_imageMutex); + + for (size_t index = 0; index < positions.size(); index++) + { + // The const_cast is necessary for now since array_view currently only supports const entries. + // If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed. + auto& outValue = const_cast(outValues[index]); + + m_gradientTransform.TransformPositionToUVWNormalized(positions[index], uvw, wasPointRejected); + + if (!wasPointRejected) + { + outValue = GetValueFromImageAsset( + m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); + } + else + { + outValue = 0.0f; + } + } + } + AZStd::string ImageGradientComponent::GetImageAssetPath() const { AZStd::string assetPathString; diff --git a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp index 3096afa3dc..667d8c30a7 100644 --- a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp @@ -203,6 +203,40 @@ namespace GradientSignal return 0.0f; } + void PerlinGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view outValues) const + { + if (positions.size() != outValues.size()) + { + AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size()); + return; + } + + AZ::Vector3 uvw; + bool wasPointRejected = false; + + AZStd::shared_lock lock(m_transformMutex); + + for (size_t index = 0; index < positions.size(); index++) + { + // The const_cast is necessary for now since array_view currently only supports const entries. + // If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed. + auto& outValue = const_cast(outValues[index]); + + m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected); + + if (!wasPointRejected) + { + outValue = m_perlinImprovedNoise->GenerateOctaveNoise( + uvw.GetX(), uvw.GetY(), uvw.GetZ(), m_configuration.m_octave, m_configuration.m_amplitude, + m_configuration.m_frequency); + } + else + { + outValue = 0.0f; + } + } + } + int PerlinGradientComponent::GetRandomSeed() const { return m_configuration.m_randomSeed; diff --git a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp index 1b6753560c..ae51bdd4ac 100644 --- a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp @@ -145,6 +145,22 @@ namespace GradientSignal m_gradientTransform = newTransform; } + float RandomGradientComponent::GetRandomValue(const AZ::Vector3& position, AZStd::size_t seed) const + { + // generating stable pseudo-random noise from a position based hash + float x = position.GetX(); + float y = position.GetY(); + AZStd::size_t result = 0; + + AZStd::hash_combine(result, x * seed + y); + AZStd::hash_combine(result, y * seed + x); + AZStd::hash_combine(result, x * y * seed); + + // always returns [0.0,1.0] + return static_cast(result % std::numeric_limits::max()) / static_cast(std::numeric_limits::max()); + } + + float RandomGradientComponent::GetValue(const GradientSampleParams& sampleParams) const { @@ -158,23 +174,50 @@ namespace GradientSignal if (!wasPointRejected) { - //generating stable pseudo-random noise from a position based hash - float x = uvw.GetX(); - float y = uvw.GetY(); - AZStd::size_t result = 0; - const AZStd::size_t seed = m_configuration.m_randomSeed + AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm + const AZStd::size_t seed = m_configuration.m_randomSeed + + AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm - AZStd::hash_combine(result, x * seed + y); - AZStd::hash_combine(result, y * seed + x); - AZStd::hash_combine(result, x * y * seed); - - //always returns [0.0,1.0] - return static_cast(result % std::numeric_limits::max()) / static_cast(std::numeric_limits::max()); + return GetRandomValue(uvw, seed); } return 0.0f; } + void RandomGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view outValues) const + { + if (positions.size() != outValues.size()) + { + AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size()); + return; + } + + AZ::Vector3 uvw; + bool wasPointRejected = false; + const AZStd::size_t seed = m_configuration.m_randomSeed + + AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm + + AZStd::shared_lock lock(m_transformMutex); + + for (size_t index = 0; index < positions.size(); index++) + { + // The const_cast is necessary for now since array_view currently only supports const entries. + // If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed. + auto& outValue = const_cast(outValues[index]); + + m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected); + + if (!wasPointRejected) + { + outValue = GetRandomValue(uvw, seed); + } + else + { + outValue = 0.0f; + } + } + } + + int RandomGradientComponent::GetRandomSeed() const { return m_configuration.m_randomSeed; diff --git a/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp index 9e1a250900..f2416f4fb6 100644 --- a/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp @@ -157,21 +157,62 @@ namespace GradientSignal float ShapeAreaFalloffGradientComponent::GetValue(const GradientSampleParams& sampleParams) const { - AZ_PROFILE_FUNCTION(Entity); - float distance = 0.0f; LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, m_configuration.m_shapeEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::DistanceFromPoint, sampleParams.m_position); - // In the special case of 0 falloff, make sure that all points inside the shape (0 distance) return - // 1.0, and all points outside the shape return 0. - if (m_configuration.m_falloffWidth == 0.0f) + // Since this is outer falloff, distance should give us values from 1.0 at the minimum distance to 0.0 at the maximum distance. + // The statement is written specifically to handle the 0 falloff case as well. For 0 falloff, all points inside the shape + // (0 distance) return 1.0, and all points outside the shape return 0. This works because division by 0 gives infinity, which gets + // clamped by the GetMax() to 0. However, if distance == 0, it would give us NaN, so we have the separate conditional check to + // handle that case and clamp to 1.0. + return (distance <= 0.0f) ? 1.0f : AZ::GetMax(1.0f - (distance / m_configuration.m_falloffWidth), 0.0f); + } + + void ShapeAreaFalloffGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view outValues) const + { + if (positions.size() != outValues.size()) { - return (distance > 0.0f) ? 0.0f : 1.0f; + AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size()); + return; } - // Since this is outer falloff, distance should give us values from 1.0 at the minimum distance - // to 0.0 at the maximum distance. - return GetRatio(m_configuration.m_falloffWidth, 0.0f, distance); + bool shapeConnected = false; + const float falloffWidth = m_configuration.m_falloffWidth; + + LmbrCentral::ShapeComponentRequestsBus::Event( + m_configuration.m_shapeEntityId, + [falloffWidth, positions, &outValues, &shapeConnected](LmbrCentral::ShapeComponentRequestsBus::Events* shapeRequests) + { + shapeConnected = true; + + for (size_t index = 0; index < positions.size(); index++) + { + // The const_cast is necessary for now since array_view currently only supports const entries. + // If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed. + auto& outValue = const_cast(outValues[index]); + + float distance = shapeRequests->DistanceFromPoint(positions[index]); + + // Since this is outer falloff, distance should give us values from 1.0 at the minimum distance to 0.0 at the maximum + // distance. The statement is written specifically to handle the 0 falloff case as well. For 0 falloff, all points + // inside the shape (0 distance) return 1.0, and all points outside the shape return 0. This works because division by 0 + // gives infinity, which gets clamped by the GetMax() to 0. However, if distance == 0, it would give us NaN, so we have + // the separate conditional check to handle that case and clamp to 1.0. + outValue = (distance <= 0.0f) ? 1.0f : AZ::GetMax(1.0f - (distance / falloffWidth), 0.0f); + } + }); + + // If there's no shape, there's no falloff. + if (!shapeConnected) + { + for (size_t index = 0; index < positions.size(); index++) + { + // The const_cast is necessary for now since array_view currently only supports const entries. + // If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed. + auto& outValue = const_cast(outValues[index]); + outValue = 1.0f; + } + } } AZ::EntityId ShapeAreaFalloffGradientComponent::GetShapeEntityId() const From 34d74857f5e940b8e18a971e7b053fb6805c48b2 Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:55:36 -0800 Subject: [PATCH 46/49] [development] fix for a possible MSVC compiler bug (#6870) Replaced a variable with the name "interface" to avoid conflict with MSVC keyword Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AssetProcessor/native/tests/SourceFileRelocatorTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp index d7801abb3d..8d52ba23bd 100644 --- a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp @@ -793,9 +793,9 @@ namespace UnitTests TEST_F(SourceFileRelocatorTest, TestInterface) { - auto* interface = AZ::Interface::Get(); + auto* sourceFileRelocator = AZ::Interface::Get(); - ASSERT_NE(interface, nullptr); + ASSERT_NE(sourceFileRelocator, nullptr); } TEST_F(SourceFileRelocatorTest, Move_Real_Succeeds) From 193f529b9c8469930252b20d9fd15bd2bbc9bf60 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Thu, 13 Jan 2022 12:56:34 -0800 Subject: [PATCH 47/49] Add an LyShine client API target (#6829) * Reduce the need to link with LyShine static lib in other gems Signed-off-by: abrmich * Add UiBasics.Builders to VirtualGamepad.Builders alias Signed-off-by: abrmich * Add UiBasics to LyShine gem's metadata Signed-off-by: abrmich * Change include to a forward declaration Signed-off-by: abrmich --- Gems/GameStateSamples/Code/CMakeLists.txt | 2 +- Gems/LyShine/Code/CMakeLists.txt | 19 ++++---- Gems/LyShine/Code/Editor/GuideHelpers.cpp | 2 +- .../Code/Editor/ViewportCanvasBackground.cpp | 2 +- .../Code/Editor/ViewportDragInteraction.h | 2 +- Gems/LyShine/Code/Editor/ViewportHelpers.h | 2 +- Gems/LyShine/Code/Editor/ViewportIcon.cpp | 12 ++--- Gems/LyShine/Code/Editor/ViewportIcon.h | 2 +- Gems/LyShine/Code/Editor/ViewportWidget.cpp | 2 +- Gems/LyShine/Code/Include/LyShine/IDraw2d.h | 44 +++++++++++++++++++ Gems/LyShine/Code/Include/LyShine/ILyShine.h | 9 ++++ Gems/LyShine/Code/Source/Draw2d.cpp | 38 +--------------- .../Code/{Include/LyShine => Source}/Draw2d.h | 3 -- Gems/LyShine/Code/Source/LyShine.cpp | 10 ++++- Gems/LyShine/Code/Source/LyShine.h | 1 + Gems/LyShine/Code/Source/LyShineDebug.cpp | 22 +++++----- .../LyShine/Code/Source/UiCanvasComponent.cpp | 2 +- Gems/LyShine/Code/Source/UiCanvasManager.cpp | 6 +-- Gems/LyShine/Code/Source/UiFaderComponent.cpp | 2 +- Gems/LyShine/Code/Source/UiImageComponent.cpp | 2 +- .../Code/Source/UiImageSequenceComponent.cpp | 2 +- Gems/LyShine/Code/Source/UiMaskComponent.cpp | 2 +- Gems/LyShine/Code/Source/UiRenderer.cpp | 4 +- Gems/LyShine/Code/Source/UiTextComponent.cpp | 10 ++--- Gems/LyShine/Code/lyshine_static_files.cmake | 4 +- Gems/LyShine/gem.json | 3 +- Gems/LyShineExamples/Code/CMakeLists.txt | 4 +- .../Code/Source/UiCustomImageComponent.cpp | 2 +- Gems/MessagePopup/Code/CMakeLists.txt | 4 +- Gems/UiBasics/CMakeLists.txt | 1 + Gems/VirtualGamepad/Code/CMakeLists.txt | 4 +- 31 files changed, 125 insertions(+), 99 deletions(-) rename Gems/LyShine/Code/{Include/LyShine => Source}/Draw2d.h (98%) diff --git a/Gems/GameStateSamples/Code/CMakeLists.txt b/Gems/GameStateSamples/Code/CMakeLists.txt index 5d1d180de0..a07bef6a06 100644 --- a/Gems/GameStateSamples/Code/CMakeLists.txt +++ b/Gems/GameStateSamples/Code/CMakeLists.txt @@ -21,7 +21,7 @@ ly_add_target( INTERFACE Gem::GameState Gem::LocalUser - Gem::LyShine.Static + Gem::LyShine.Clients.API Gem::SaveData.Static Gem::MessagePopup.Static Legacy::CryCommon diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt index 4f2c4e90a7..2b550b0e36 100644 --- a/Gems/LyShine/Code/CMakeLists.txt +++ b/Gems/LyShine/Code/CMakeLists.txt @@ -58,10 +58,13 @@ ly_add_target( # by default, load the above "Gem::LyShine" module in Client and Server applications: ly_create_alias(NAME LyShine.Clients NAMESPACE Gem TARGETS Gem::LyShine) ly_create_alias(NAME LyShine.Servers NAMESPACE Gem TARGETS Gem::LyShine) +# create an alias for other gems to depend on an LyShine public API (may be converted to a target in the future): +ly_create_alias(NAME LyShine.Clients.API NAMESPACE Gem TARGETS Gem::LyShine.Clients) +ly_create_alias(NAME LyShine.Servers.API NAMESPACE Gem TARGETS Gem::LyShine.Servers) if (PAL_TRAIT_BUILD_HOST_TOOLS) ly_add_target( - NAME LyShine.Editor.Static STATIC + NAME LyShine.Tools.Static STATIC NAMESPACE Gem AUTOMOC AUTOUIC @@ -73,7 +76,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) . Source Editor - PUBLIC Include BUILD_DEPENDENCIES PRIVATE @@ -100,7 +102,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) ) ly_add_target( - NAME LyShine.Editor GEM_MODULE + NAME LyShine.Tools GEM_MODULE NAMESPACE Gem FILES_CMAKE lyshine_common_module_files.cmake @@ -112,18 +114,18 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) . Source Editor - PUBLIC Include BUILD_DEPENDENCIES PRIVATE Legacy::CryCommon AZ::AzToolsFramework - Gem::LyShine.Editor.Static + Gem::LyShine.Tools.Static Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor + Gem::UiBasics.Tools ) # by naming this target LyShine.Builders it ensures that it is loaded @@ -179,10 +181,9 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Gem::LyShine.Builders.Static Gem::LmbrCentral.Editor Gem::TextureAtlas.Editor + RUNTIME_DEPENDENCIES + Gem::UiBasics.Builders ) - - # by default, load the above "Gem::LyShine.Editor" module in dev tools: - ly_create_alias(NAME LyShine.Tools NAMESPACE Gem TARGETS Gem::LyShine.Editor) endif() ################################################################################ @@ -241,7 +242,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Legacy::CryCommon AZ::AssetBuilderSDK - Gem::LyShine.Editor.Static + Gem::LyShine.Tools.Static Gem::LyShine.Builders.Static Gem::LmbrCentral.Editor Gem::TextureAtlas diff --git a/Gems/LyShine/Code/Editor/GuideHelpers.cpp b/Gems/LyShine/Code/Editor/GuideHelpers.cpp index eb5966568e..b6d7acc57a 100644 --- a/Gems/LyShine/Code/Editor/GuideHelpers.cpp +++ b/Gems/LyShine/Code/Editor/GuideHelpers.cpp @@ -173,7 +173,7 @@ namespace GuideHelpers // the line is drawn as the inverse of the background color AZ::Color guideColor(1.0f, 1.0f, 1.0f, 1.0f); - CDraw2d::RenderState renderState; + IDraw2d::RenderState renderState; renderState.m_blendState.m_blendSource = AZ::RHI::BlendFactor::ColorDestInverse; renderState.m_blendState.m_blendDest = AZ::RHI::BlendFactor::Zero; diff --git a/Gems/LyShine/Code/Editor/ViewportCanvasBackground.cpp b/Gems/LyShine/Code/Editor/ViewportCanvasBackground.cpp index 668c4e8024..b1961de0a1 100644 --- a/Gems/LyShine/Code/Editor/ViewportCanvasBackground.cpp +++ b/Gems/LyShine/Code/Editor/ViewportCanvasBackground.cpp @@ -50,7 +50,7 @@ void ViewportCanvasBackground::Draw(Draw2dHelper& draw2d, const AZ::Vector2& can // now draw the same as Stretched but with UV's adjusted const AZ::Vector2 uvs[4] = { AZ::Vector2(0, 0), AZ::Vector2(uvScale.GetX(), 0), AZ::Vector2(uvScale.GetX(), uvScale.GetY()), AZ::Vector2(0, uvScale.GetY()) }; AZ::Color colorWhite(1.0f, 1.0f, 1.0f, 1.0f); - CDraw2d::VertexPosColUV verts[4]; + IDraw2d::VertexPosColUV verts[4]; for (int i = 0; i < 4; ++i) { verts[i].position = positions[i]; diff --git a/Gems/LyShine/Code/Editor/ViewportDragInteraction.h b/Gems/LyShine/Code/Editor/ViewportDragInteraction.h index 3b26b66308..6b48fc0133 100644 --- a/Gems/LyShine/Code/Editor/ViewportDragInteraction.h +++ b/Gems/LyShine/Code/Editor/ViewportDragInteraction.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include //! Abstract base class for drag interactions in the UI Editor viewport window. class ViewportDragInteraction diff --git a/Gems/LyShine/Code/Editor/ViewportHelpers.h b/Gems/LyShine/Code/Editor/ViewportHelpers.h index 8068f4af65..f2722fb6b8 100644 --- a/Gems/LyShine/Code/Editor/ViewportHelpers.h +++ b/Gems/LyShine/Code/Editor/ViewportHelpers.h @@ -7,7 +7,7 @@ */ #pragma once -#include +#include namespace ViewportHelpers { diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.cpp b/Gems/LyShine/Code/Editor/ViewportIcon.cpp index 3f0fdfaba0..33f2f40ffe 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.cpp +++ b/Gems/LyShine/Code/Editor/ViewportIcon.cpp @@ -6,7 +6,7 @@ * */ #include "EditorCommon.h" -#include +#include #include #include @@ -15,7 +15,7 @@ float ViewportIcon::m_dpiScaleFactor = 1.0f; ViewportIcon::ViewportIcon(const char* textureFilename) { - m_image = CDraw2d::LoadTexture(textureFilename); + m_image = Draw2dHelper::LoadTexture(textureFilename); } ViewportIcon::~ViewportIcon() @@ -48,7 +48,7 @@ void ViewportIcon::DrawImageAligned(Draw2dHelper& draw2d, AZ::Vector2& pivot, fl opacity); } -void ViewportIcon::DrawImageTiled(Draw2dHelper& draw2d, CDraw2d::VertexPosColUV* verts) +void ViewportIcon::DrawImageTiled(Draw2dHelper& draw2d, IDraw2d::VertexPosColUV* verts) { // Use default blending and rounding modes IDraw2d::Rounding rounding = IDraw2d::Rounding::Nearest; @@ -63,7 +63,7 @@ void ViewportIcon::DrawAxisAlignedBoundingBox(Draw2dHelper& draw2d, AZ::Vector2 float endTexCoordU = fabsf((bound1.GetX() - bound0.GetX()) * pixelLengthForDottedLineTexture); float endTexCoordV = fabsf((bound1.GetY() - bound0.GetY()) * pixelLengthForDottedLineTexture); - CDraw2d::VertexPosColUV verts[2]; + IDraw2d::VertexPosColUV verts[2]; { verts[0].color = dottedColor; verts[1].color = dottedColor; @@ -158,7 +158,7 @@ void ViewportIcon::Draw(Draw2dHelper& draw2d, AZ::Vector2 anchorPos, const AZ::M AZ::Matrix4x4 moveFromPivotSpaceMat = AZ::Matrix4x4::CreateTranslation(pivot3); AZ::Matrix4x4 newTransform = transform * moveFromPivotSpaceMat * rotMat * moveToPivotSpaceMat; - CDraw2d::VertexPosColUV verts[4]; + IDraw2d::VertexPosColUV verts[4]; // points are a clockwise quad static const AZ::Vector2 uvs[4] = { AZ::Vector2(0.0f, 0.0f), AZ::Vector2(1.0f, 0.0f), AZ::Vector2(1.0f, 1.0f), AZ::Vector2(0.0f, 1.0f) @@ -251,7 +251,7 @@ void ViewportIcon::DrawDistanceLine(Draw2dHelper& draw2d, AZ::Vector2 start, AZ: const float pixelLengthForDottedLineTexture = 8.0f; float endTexCoordU = length / pixelLengthForDottedLineTexture; - CDraw2d::VertexPosColUV verts[2]; + IDraw2d::VertexPosColUV verts[2]; verts[0].position = start; verts[0].color = dottedColor; diff --git a/Gems/LyShine/Code/Editor/ViewportIcon.h b/Gems/LyShine/Code/Editor/ViewportIcon.h index f815b244e9..fadd829bcf 100644 --- a/Gems/LyShine/Code/Editor/ViewportIcon.h +++ b/Gems/LyShine/Code/Editor/ViewportIcon.h @@ -20,7 +20,7 @@ public: void DrawImageAligned(Draw2dHelper& draw2d, AZ::Vector2& pivot, float opacity); - void DrawImageTiled(Draw2dHelper& draw2d, CDraw2d::VertexPosColUV* verts); + void DrawImageTiled(Draw2dHelper& draw2d, IDraw2d::VertexPosColUV* verts); void Draw(Draw2dHelper& draw2d, AZ::Vector2 anchorPos, const AZ::Matrix4x4& transform, float iconRot = 0.0f, AZ::Color color = AZ::Color(1.0f, 1.0f, 1.0f, 1.0f)) const; diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp index 993f57aeeb..b481e9cbf1 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp +++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp @@ -16,7 +16,6 @@ #include #include -#include #include "LyShine.h" #include "UiRenderer.h" @@ -27,6 +26,7 @@ #include "RulerWidget.h" #include "CanvasHelpers.h" #include "AssetDropHelpers.h" +#include "Draw2d.h" #include "QtHelpers.h" #include diff --git a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h index 620b8c23c9..77dc5ac601 100644 --- a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h @@ -482,6 +482,50 @@ public: // static member functions return nullptr; } + //! Helper to load a texture + static AZ::Data::Instance LoadTexture(const AZStd::string& pathName) + { + if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + { + return gEnv->pLyShine->LoadTexture(pathName); + } + + return nullptr; + } + + //! Given a position and size and an alignment return the top left corner of the aligned quad + static AZ::Vector2 Align(AZ::Vector2 position, AZ::Vector2 size, IDraw2d::HAlign horizontalAlignment, IDraw2d::VAlign verticalAlignment) + { + AZ::Vector2 result = AZ::Vector2::CreateZero(); + switch (horizontalAlignment) + { + case IDraw2d::HAlign::Left: + result.SetX(position.GetX()); + break; + case IDraw2d::HAlign::Center: + result.SetX(position.GetX() - size.GetX() * 0.5f); + break; + case IDraw2d::HAlign::Right: + result.SetX(position.GetX() - size.GetX()); + break; + } + + switch (verticalAlignment) + { + case IDraw2d::VAlign::Top: + result.SetY(position.GetY()); + break; + case IDraw2d::VAlign::Center: + result.SetY(position.GetY() - size.GetY() * 0.5f); + break; + case IDraw2d::VAlign::Bottom: + result.SetY(position.GetY() - size.GetY()); + break; + } + + return result; + } + //! Round the X and Y coordinates of a point using the given rounding policy template static T RoundXY(T value, IDraw2d::Rounding roundingType) diff --git a/Gems/LyShine/Code/Include/LyShine/ILyShine.h b/Gems/LyShine/Code/Include/LyShine/ILyShine.h index 7832497d8c..71ca6ec074 100644 --- a/Gems/LyShine/Code/Include/LyShine/ILyShine.h +++ b/Gems/LyShine/Code/Include/LyShine/ILyShine.h @@ -9,12 +9,18 @@ #include #include +#include class IDraw2d; class ISprite; struct IUiAnimationSystem; class UiEntityContext; +namespace AZ::RPI +{ + class Image; +} + // The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the LYSHINE_EXPORTS // symbol defined in the StdAfx.h. this symbol should not be defined on any project @@ -82,6 +88,9 @@ public: //! Check if a sprite's texture asset exists. The .sprite sidecar file is optional and is not checked virtual bool DoesSpriteTextureAssetExist(const AZStd::string& pathname) = 0; + //! Load an image asset by texture pathname + virtual AZ::Data::Instance LoadTexture(const AZStd::string& pathName) = 0; + //! Perform post-initialization (script system will be available) virtual void PostInit() = 0; diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index 2838ed4877..ff8c7f1c6d 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -6,7 +6,7 @@ * */ -#include +#include "Draw2d.h" #include #include "LyShinePassDataBus.h" @@ -215,7 +215,7 @@ void CDraw2d::DrawImageAligned(AZ::Data::Instance image, AZ::Vec HAlign horizontalAlignment, VAlign verticalAlignment, float opacity, float rotation, const AZ::Vector2* minMaxTexCoords, ImageOptions* imageOptions) { - AZ::Vector2 alignedPosition = Align(position, size, horizontalAlignment, verticalAlignment); + AZ::Vector2 alignedPosition = Draw2dHelper::Align(position, size, horizontalAlignment, verticalAlignment); DrawImage(image, alignedPosition, size, opacity, rotation, &position, minMaxTexCoords, imageOptions); } @@ -524,40 +524,6 @@ void CDraw2d::SetSortKey(int64_t key) // PUBLIC STATIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// -AZ::Vector2 CDraw2d::Align(AZ::Vector2 position, AZ::Vector2 size, - HAlign horizontalAlignment, VAlign verticalAlignment) -{ - AZ::Vector2 result = AZ::Vector2::CreateZero(); - switch (horizontalAlignment) - { - case HAlign::Left: - result.SetX(position.GetX()); - break; - case HAlign::Center: - result.SetX(position.GetX() - size.GetX() * 0.5f); - break; - case HAlign::Right: - result.SetX(position.GetX() - size.GetX()); - break; - } - - switch (verticalAlignment) - { - case VAlign::Top: - result.SetY(position.GetY()); - break; - case VAlign::Center: - result.SetY(position.GetY() - size.GetY() * 0.5f); - break; - case VAlign::Bottom: - result.SetY(position.GetY() - size.GetY()); - break; - } - - return result; -} - //////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Data::Instance CDraw2d::LoadTexture(const AZStd::string& pathName) { diff --git a/Gems/LyShine/Code/Include/LyShine/Draw2d.h b/Gems/LyShine/Code/Source/Draw2d.h similarity index 98% rename from Gems/LyShine/Code/Include/LyShine/Draw2d.h rename to Gems/LyShine/Code/Source/Draw2d.h index d138f41b03..6d400c6b55 100644 --- a/Gems/LyShine/Code/Include/LyShine/Draw2d.h +++ b/Gems/LyShine/Code/Source/Draw2d.h @@ -180,9 +180,6 @@ private: public: // static member functions - //! Given a position and size and an alignment return the top left corner of the aligned quad - static AZ::Vector2 Align(AZ::Vector2 position, AZ::Vector2 size, HAlign horizontalAlignment, VAlign verticalAlignment); - //! Helper to load a texture static AZ::Data::Instance LoadTexture(const AZStd::string& pathName); diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index 683d72f4ab..5a704329ef 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -43,11 +43,11 @@ #include "Sprite.h" #include "UiSerialize.h" #include "UiRenderer.h" +#include "Draw2d.h" #include #include #include -#include #if defined(LYSHINE_INTERNAL_UNIT_TEST) #include "TextMarkup.h" @@ -355,6 +355,12 @@ bool CLyShine::DoesSpriteTextureAssetExist(const AZStd::string& pathname) return CSprite::DoesSpriteTextureAssetExist(pathname); } +//////////////////////////////////////////////////////////////////////////////////////////////////// +AZ::Data::Instance CLyShine::LoadTexture(const AZStd::string& pathname) +{ + return CDraw2d::LoadTexture(pathname); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void CLyShine::PostInit() { @@ -691,7 +697,7 @@ void CLyShine::RenderUiCursor() AZ::RHI::Size cursorSize = m_uiCursorTexture->GetDescriptor().m_size; const AZ::Vector2 dimensions(aznumeric_cast(cursorSize.m_width), aznumeric_cast(cursorSize.m_height)); - CDraw2d::ImageOptions imageOptions; + IDraw2d::ImageOptions imageOptions; imageOptions.m_clamp = true; const float opacity = 1.0f; const float rotation = 0.0f; diff --git a/Gems/LyShine/Code/Source/LyShine.h b/Gems/LyShine/Code/Source/LyShine.h index 82f902a9f8..eab52258c1 100644 --- a/Gems/LyShine/Code/Source/LyShine.h +++ b/Gems/LyShine/Code/Source/LyShine.h @@ -72,6 +72,7 @@ public: ISprite* LoadSprite(const AZStd::string& pathname) override; ISprite* CreateSprite(const AZStd::string& renderTargetName) override; bool DoesSpriteTextureAssetExist(const AZStd::string& pathname) override; + AZ::Data::Instance LoadTexture(const AZStd::string& pathname) override; void PostInit() override; diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index bb57e70857..7e99652f45 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -7,7 +7,7 @@ */ #include "LyShineDebug.h" #include "IConsole.h" -#include +#include #include @@ -377,7 +377,7 @@ static void DebugDrawColoredBox(AZ::Vector2 pos, AZ::Vector2 size, AZ::Color col { IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); imageOptions.color = color.GetAsVector3(); auto whiteTexture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); draw2d->DrawImageAligned(whiteTexture, pos, size, horizontalAlignment, verticalAlignment, @@ -392,7 +392,7 @@ static void DebugDrawStringWithSizeBox(AZStd::string_view font, unsigned int eff { IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); - CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); + IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); if (!font.empty()) { textOptions.fontName = font; @@ -618,7 +618,7 @@ static AZ::Vector2 DebugDrawFontColorTestBox(AZ::Vector2 pos, const char* string float pointSize = 32.0f; const float spacing = 6.0f; - CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); + IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); textOptions.effectIndex = 1; // no drop shadow baked in textOptions.color = color; @@ -742,7 +742,7 @@ static void DebugDraw2dImageColor() AZ::Data::Instance texture = GetMonoAlphaTestTexture(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); draw2d->DrawText( "Testing image colors, image is black and white, top row is opacity=1, bottom row is opacity = 0.5", @@ -780,7 +780,7 @@ static void DebugDraw2dImageBlendMode() AZ::Data::Instance texture = GetColorAlphaTestTexture(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); draw2d->DrawText("Testing blend modes, src blend changes across x-axis, dst blend changes across y axis", AZ::Vector2(20, 20), 16); @@ -801,7 +801,7 @@ static void DebugDraw2dImageBlendMode() AZ::Vector2 pos(xStart + xSpacing * srcIndex, yStart + ySpacing * dstIndex); // first draw a background with varying color and alpha - CDraw2d::VertexPosColUV verts[4] = + IDraw2d::VertexPosColUV verts[4] = { { // top left AZ::Vector2(pos.GetX(), pos.GetY()), @@ -828,7 +828,7 @@ static void DebugDraw2dImageBlendMode() // Draw the image with this color - CDraw2d::RenderState renderState; + IDraw2d::RenderState renderState; renderState.m_blendState.m_blendSource = g_srcBlendModes[srcIndex]; renderState.m_blendState.m_blendDest = g_dstBlendModes[dstIndex]; draw2d->DrawImage(texture, pos, size, 1.0f, 0.0f, 0, 0, &imageOptions); @@ -845,7 +845,7 @@ static void DebugDraw2dImageUVs() AZ::Data::Instance texture = GetColorTestTexture(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); draw2d->DrawText( "Testing DrawImage with minMaxTexCoords. Full image, top left quadrant, middle section, full flipped", @@ -894,7 +894,7 @@ static void DebugDraw2dImagePixelRounding() AZ::Data::Instance texture = GetColorTestTexture(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); draw2d->DrawText("Testing DrawImage pixel rounding options", AZ::Vector2(20, 20), 16); @@ -933,7 +933,7 @@ static void DebugDraw2dLineBasic() { IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); - CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); + IDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); draw2d->DrawText("Testing DrawLine", AZ::Vector2(20, 20), 16); diff --git a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp index 0a21f92119..ea6e3681a3 100644 --- a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/LyShine/Code/Source/UiCanvasManager.cpp b/Gems/LyShine/Code/Source/UiCanvasManager.cpp index 70646246f0..fc373f17f6 100644 --- a/Gems/LyShine/Code/Source/UiCanvasManager.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasManager.cpp @@ -6,7 +6,7 @@ * */ #include "UiCanvasManager.h" -#include +#include #include "UiCanvasFileObject.h" #include "UiCanvasComponent.h" @@ -1020,7 +1020,7 @@ void UiCanvasManager::DebugDisplayCanvasData(int setting) const // local function to write a line of text (with a background rect) and increment Y offset AZStd::function WriteLine = [&](const char* buffer, const AZ::Vector3& color) { - CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); + IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); textOptions.color = color; AZ::Vector2 textSize = draw2d->GetTextSize(buffer, fontSize, &textOptions); AZ::Vector2 rectTopLeft = AZ::Vector2(xOffset - 2, yOffset); @@ -1174,7 +1174,7 @@ void UiCanvasManager::DebugDisplayDrawCallData() const // local function to write a line of text (with a background rect) and increment Y offset AZStd::function WriteLine = [&](const char* buffer, const AZ::Vector3& color) { - CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); + IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); textOptions.color = color; AZ::Vector2 textSize = draw2d->GetTextSize(buffer, fontSize, &textOptions); AZ::Vector2 rectTopLeft = AZ::Vector2(xOffset - 2, yOffset); diff --git a/Gems/LyShine/Code/Source/UiFaderComponent.cpp b/Gems/LyShine/Code/Source/UiFaderComponent.cpp index e1f5153e99..e84e5162b5 100644 --- a/Gems/LyShine/Code/Source/UiFaderComponent.cpp +++ b/Gems/LyShine/Code/Source/UiFaderComponent.cpp @@ -7,7 +7,7 @@ */ #include "UiFaderComponent.h" #include "RenderGraph.h" -#include +#include #include #include diff --git a/Gems/LyShine/Code/Source/UiImageComponent.cpp b/Gems/LyShine/Code/Source/UiImageComponent.cpp index baf210a629..a8b4210d6b 100644 --- a/Gems/LyShine/Code/Source/UiImageComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageComponent.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include diff --git a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp index 4e7d22d96a..3559dc6306 100644 --- a/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp +++ b/Gems/LyShine/Code/Source/UiImageSequenceComponent.cpp @@ -9,7 +9,7 @@ #include "Sprite.h" #include "RenderGraph.h" -#include +#include #include #include #include diff --git a/Gems/LyShine/Code/Source/UiMaskComponent.cpp b/Gems/LyShine/Code/Source/UiMaskComponent.cpp index 09975d1351..67ba8ee0fb 100644 --- a/Gems/LyShine/Code/Source/UiMaskComponent.cpp +++ b/Gems/LyShine/Code/Source/UiMaskComponent.cpp @@ -6,7 +6,7 @@ * */ #include "UiMaskComponent.h" -#include +#include #include #include diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 3b835dec92..a9c56ece90 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC MEMBER FUNCTIONS @@ -449,7 +449,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) // local function to write a line of text (with a background rect) and increment Y offset AZStd::function WriteLine = [&](const char* buffer, const AZ::Vector3& color) { - CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); + IDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); textOptions.color = color; AZ::Vector2 textSize = draw2d->GetTextSize(buffer, fontSize, &textOptions); AZ::Vector2 rectTopLeft = AZ::Vector2(xOffset - 2, yOffset); diff --git a/Gems/LyShine/Code/Source/UiTextComponent.cpp b/Gems/LyShine/Code/Source/UiTextComponent.cpp index 01cfd49e99..aa0235470a 100644 --- a/Gems/LyShine/Code/Source/UiTextComponent.cpp +++ b/Gems/LyShine/Code/Source/UiTextComponent.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include @@ -1105,7 +1105,7 @@ UiTextComponent::InlineImage::InlineImage(const AZStd::string& texturePathname, else { // Load the texture - m_texture = CDraw2d::LoadTexture(m_filepath); + m_texture = Draw2dHelper::LoadTexture(m_filepath); if (m_texture) { AZ::RHI::Size size = m_texture->GetDescriptor().m_size; @@ -1157,7 +1157,7 @@ bool UiTextComponent::InlineImage::OnAtlasUnloaded(const TextureAtlasNamespace:: else { // Load the texture - m_texture = CDraw2d::LoadTexture(m_filepath); + m_texture = Draw2dHelper::LoadTexture(m_filepath); } return true; } @@ -2675,7 +2675,7 @@ void UiTextComponent::GetClickableTextRects(UiClickableTextInterface::ClickableT } else { - alignedPosition = CDraw2d::Align(pos, drawBatchLine.lineSize, m_textHAlignment, IDraw2d::VAlign::Top); // y is already aligned + alignedPosition = Draw2dHelper::Align(pos, drawBatchLine.lineSize, m_textHAlignment, IDraw2d::VAlign::Top); // y is already aligned } alignedPosition.SetY(alignedPosition.GetY() + newlinePosYIncrement); @@ -4043,7 +4043,7 @@ void UiTextComponent::RenderDrawBatchLines( } else { - alignedPosition = CDraw2d::Align(pos, drawBatchLine.lineSize, m_textHAlignment, IDraw2d::VAlign::Top); // y is already aligned + alignedPosition = Draw2dHelper::Align(pos, drawBatchLine.lineSize, m_textHAlignment, IDraw2d::VAlign::Top); // y is already aligned } alignedPosition.SetY(alignedPosition.GetY() + newlinePosYIncrement); diff --git a/Gems/LyShine/Code/lyshine_static_files.cmake b/Gems/LyShine/Code/lyshine_static_files.cmake index 1adbe1b796..925658e756 100644 --- a/Gems/LyShine/Code/lyshine_static_files.cmake +++ b/Gems/LyShine/Code/lyshine_static_files.cmake @@ -7,8 +7,6 @@ # set(FILES - Source/Draw2d.cpp - Include/LyShine/Draw2d.h Include/LyShine/IDraw2d.h Include/LyShine/IRenderGraph.h Include/LyShine/ISprite.h @@ -88,6 +86,8 @@ set(FILES Include/LyShine/Bus/World/UiCanvasOnMeshBus.h Include/LyShine/Bus/World/UiCanvasRefBus.h Include/LyShine/Bus/Tools/UiSystemToolsBus.h + Source/Draw2d.cpp + Source/Draw2d.h Source/LyShine.cpp Source/LyShine.h Source/LyShinePassDataBus.h diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index f3c3fc2c67..8dcac6404a 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -24,6 +24,7 @@ "Atom_Bootstrap", "AtomFont", "TextureAtlas", - "AtomToolsFramework" + "AtomToolsFramework", + "UiBasics" ] } diff --git a/Gems/LyShineExamples/Code/CMakeLists.txt b/Gems/LyShineExamples/Code/CMakeLists.txt index 6e8f53d5cd..40032d6342 100644 --- a/Gems/LyShineExamples/Code/CMakeLists.txt +++ b/Gems/LyShineExamples/Code/CMakeLists.txt @@ -21,7 +21,7 @@ ly_add_target( Gem::LmbrCentral PUBLIC Legacy::CryCommon - Gem::LyShine.Static + Gem::LyShine.Clients.API ) ly_add_target( @@ -42,7 +42,7 @@ ly_add_target( # if enabled, LyShineExamples is used by all kinds of applications, however, the dependency to LmbrCentral is different # per application type -ly_create_alias(NAME LyShineExamples.Builders NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral.Editor) +ly_create_alias(NAME LyShineExamples.Builders NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::UiBasics.Builders Gem::LmbrCentral.Editor) ly_create_alias(NAME LyShineExamples.Tools NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral.Editor) ly_create_alias(NAME LyShineExamples.Clients NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral) ly_create_alias(NAME LyShineExamples.Servers NAMESPACE Gem TARGETS Gem::LyShineExamples Gem::LmbrCentral) diff --git a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp index 542e18b98c..e8b0e4370a 100644 --- a/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp +++ b/Gems/LyShineExamples/Code/Source/UiCustomImageComponent.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include diff --git a/Gems/MessagePopup/Code/CMakeLists.txt b/Gems/MessagePopup/Code/CMakeLists.txt index 51ec1a3bd1..2ed9c71632 100644 --- a/Gems/MessagePopup/Code/CMakeLists.txt +++ b/Gems/MessagePopup/Code/CMakeLists.txt @@ -19,7 +19,7 @@ ly_add_target( BUILD_DEPENDENCIES PUBLIC Legacy::CryCommon - Gem::LyShine + Gem::LyShine.Clients.API ) ly_add_target( @@ -39,4 +39,4 @@ ly_add_target( # MessagePopup is used only in client applications ly_create_alias(NAME MessagePopup.Clients NAMESPACE Gem TARGETS Gem::MessagePopup) - +ly_create_alias(NAME MessagePopup.Builders NAMESPACE Gem TARGETS Gem::UiBasics.Builders) diff --git a/Gems/UiBasics/CMakeLists.txt b/Gems/UiBasics/CMakeLists.txt index 8d71bd08c7..9c3cf01d26 100644 --- a/Gems/UiBasics/CMakeLists.txt +++ b/Gems/UiBasics/CMakeLists.txt @@ -9,4 +9,5 @@ # This will export its "SourcePaths" to the generated "cmake_dependencies..assetbuilder.setreg" if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_create_alias(NAME UiBasics.Builders NAMESPACE Gem) + ly_create_alias(NAME UiBasics.Tools NAMESPACE Gem) endif() diff --git a/Gems/VirtualGamepad/Code/CMakeLists.txt b/Gems/VirtualGamepad/Code/CMakeLists.txt index d32834633f..6700004a30 100644 --- a/Gems/VirtualGamepad/Code/CMakeLists.txt +++ b/Gems/VirtualGamepad/Code/CMakeLists.txt @@ -21,7 +21,7 @@ ly_add_target( AZ::AzCore AZ::AzFramework Legacy::CryCommon - Gem::LyShine + Gem::LyShine.Clients.API ) ly_add_target( @@ -42,4 +42,4 @@ ly_add_target( # the virtual gamepad is needed everywhere except servers: ly_create_alias(NAME VirtualGamepad.Clients NAMESPACE Gem TARGETS Gem::VirtualGamepad) ly_create_alias(NAME VirtualGamepad.Tools NAMESPACE Gem TARGETS Gem::VirtualGamepad) -ly_create_alias(NAME VirtualGamepad.Builders NAMESPACE Gem TARGETS Gem::VirtualGamepad) +ly_create_alias(NAME VirtualGamepad.Builders NAMESPACE Gem TARGETS Gem::VirtualGamepad Gem::UiBasics.Builders) From 42c523b27512f3697be50be637b18c400d9cec57 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Thu, 13 Jan 2022 16:07:44 -0800 Subject: [PATCH 48/49] Add SetEnv and UnSetEnv for environment variable to util (#6884) * Add SetEnv and UnSetEnv for environment variable to util * Move utils to AzTest * enable for ios and android * fix wrong file path --- .../Android/platform_android_files.cmake | 1 + .../AzTest/Utils_Unimplemented.cpp | 25 +++++++++++++++++++ .../Common/UnixLike/AzTest/Utils_UnixLike.cpp | 25 +++++++++++++++++++ .../Common/WinAPI/AzTest/Utils_WinAPI.cpp | 25 +++++++++++++++++++ .../Platform/Linux/platform_linux_files.cmake | 1 + .../Platform/Mac/platform_mac_files.cmake | 1 + .../Windows/platform_windows_files.cmake | 1 + .../Platform/iOS/platform_ios_files.cmake | 1 + Code/Framework/AzTest/AzTest/Utils.h | 13 ++++++++++ 9 files changed, 93 insertions(+) create mode 100644 Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/AzTest/Utils_Unimplemented.cpp create mode 100644 Code/Framework/AzTest/AzTest/Platform/Common/UnixLike/AzTest/Utils_UnixLike.cpp create mode 100644 Code/Framework/AzTest/AzTest/Platform/Common/WinAPI/AzTest/Utils_WinAPI.cpp diff --git a/Code/Framework/AzTest/AzTest/Platform/Android/platform_android_files.cmake b/Code/Framework/AzTest/AzTest/Platform/Android/platform_android_files.cmake index e0f47c8fa6..83ae97fa20 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Android/platform_android_files.cmake +++ b/Code/Framework/AzTest/AzTest/Platform/Android/platform_android_files.cmake @@ -8,6 +8,7 @@ set(FILES ../Common/UnixLike/AzTest/ColorizedOutput_UnixLike.cpp + ../Common/UnixLike/AzTest/Utils_UnixLike.cpp ScopedAutoTempDirectory_Android.cpp Platform_Android.cpp AzTest_Traits_Platform.h diff --git a/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/AzTest/Utils_Unimplemented.cpp b/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/AzTest/Utils_Unimplemented.cpp new file mode 100644 index 0000000000..0f91ea1020 --- /dev/null +++ b/Code/Framework/AzTest/AzTest/Platform/Common/Unimplemented/AzTest/Utils_Unimplemented.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Test + { + bool SetEnv([[maybe_unused]] const char* envname, [[maybe_unused]] const char* envvalue, [[maybe_unused]] bool overwrite) + { + return false; + } + + bool UnsetEnv([[maybe_unused]] const char* envname) + { + return false; + } + } // namespace Test +} // namespace AZ diff --git a/Code/Framework/AzTest/AzTest/Platform/Common/UnixLike/AzTest/Utils_UnixLike.cpp b/Code/Framework/AzTest/AzTest/Platform/Common/UnixLike/AzTest/Utils_UnixLike.cpp new file mode 100644 index 0000000000..8efdb5d082 --- /dev/null +++ b/Code/Framework/AzTest/AzTest/Platform/Common/UnixLike/AzTest/Utils_UnixLike.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Test + { + bool SetEnv(const char* envname, const char* envvalue, bool overwrite) + { + return setenv(envname, envvalue, overwrite) != -1; + } + + bool UnsetEnv(const char* envname) + { + return unsetenv(envname) != -1; + } + } +} diff --git a/Code/Framework/AzTest/AzTest/Platform/Common/WinAPI/AzTest/Utils_WinAPI.cpp b/Code/Framework/AzTest/AzTest/Platform/Common/WinAPI/AzTest/Utils_WinAPI.cpp new file mode 100644 index 0000000000..a06cbf7ffe --- /dev/null +++ b/Code/Framework/AzTest/AzTest/Platform/Common/WinAPI/AzTest/Utils_WinAPI.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Test + { + bool SetEnv(const char* envname, const char* envvalue, [[maybe_unused]] bool overwrite) + { + return _putenv_s(envname, envvalue); + } + + bool UnsetEnv(const char* envname) + { + return SetEnv(envname, "", 1); + } + } +} diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzTest/AzTest/Platform/Linux/platform_linux_files.cmake index 68ca9332c7..9c6692b409 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/platform_linux_files.cmake @@ -9,6 +9,7 @@ set(FILES ../Common/UnixLike/AzTest/ColorizedOutput_UnixLike.cpp ../Common/UnixLike/AzTest/ScopedAutoTempDirectory_UnixLike.cpp + ../Common/UnixLike/AzTest/Utils_UnixLike.cpp Platform_Linux.cpp AzTest_Traits_Platform.h AzTest_Traits_Linux.h diff --git a/Code/Framework/AzTest/AzTest/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzTest/AzTest/Platform/Mac/platform_mac_files.cmake index 3cbe35792c..cb641e2113 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzTest/AzTest/Platform/Mac/platform_mac_files.cmake @@ -9,6 +9,7 @@ set(FILES ../Common/UnixLike/AzTest/ColorizedOutput_UnixLike.cpp ../Common/UnixLike/AzTest/ScopedAutoTempDirectory_UnixLike.cpp + ../Common/UnixLike/AzTest/Utils_UnixLike.cpp Platform_Mac.cpp AzTest_Traits_Platform.h AzTest_Traits_Mac.h diff --git a/Code/Framework/AzTest/AzTest/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzTest/AzTest/Platform/Windows/platform_windows_files.cmake index 656b28a373..4730b383a2 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Windows/platform_windows_files.cmake +++ b/Code/Framework/AzTest/AzTest/Platform/Windows/platform_windows_files.cmake @@ -8,6 +8,7 @@ set(FILES ../Common/WinAPI/AzTest/ColorizedOutput_WinAPI.cpp + ../Common/WinAPI/AzTest/Utils_WinAPI.cpp Platform_Windows.cpp ScopedAutoTempDirectory_Windows.cpp AzTest_Traits_Platform.h diff --git a/Code/Framework/AzTest/AzTest/Platform/iOS/platform_ios_files.cmake b/Code/Framework/AzTest/AzTest/Platform/iOS/platform_ios_files.cmake index 64bbc27bc1..81875f71e5 100644 --- a/Code/Framework/AzTest/AzTest/Platform/iOS/platform_ios_files.cmake +++ b/Code/Framework/AzTest/AzTest/Platform/iOS/platform_ios_files.cmake @@ -9,6 +9,7 @@ set(FILES ../Common/UnixLike/AzTest/ColorizedOutput_UnixLike.cpp ../Common/Unimplemented/AzTest/ScopedAutoTempDirectory_Unimplemented.cpp + ../Common/UnixLike/AzTest/Utils_UnixLike.cpp Platform_iOS.cpp AzTest_Traits_Platform.h AzTest_Traits_iOS.h diff --git a/Code/Framework/AzTest/AzTest/Utils.h b/Code/Framework/AzTest/AzTest/Utils.h index 2778e163b3..e3b45e97f2 100644 --- a/Code/Framework/AzTest/AzTest/Utils.h +++ b/Code/Framework/AzTest/AzTest/Utils.h @@ -55,6 +55,19 @@ namespace AZ // Returns the path to the engine's root by cdup from the current execution path until engine.txt is found AZStd::string GetEngineRootPath(); + //! Create or modify environment variable. + //! @param envname The environment variable name + //! @param envvalue The environment variable name + //! @param overwrite If name does exist in the environment, then its value is changed to value if overwrite is nonzero; + //! if overwrite is zero, then the value of name is not changed + //! @returns Return true if successful, otherwise false + bool SetEnv(const char* envname, const char* envvalue, bool overwrite); + + //! Remove environment variable. + //! @param envname The environment variable name + //! @returns Return true if successful, otherwise false + bool UnsetEnv(const char* envname); + //! Provides a scoped object that will create a temporary operating-system specific folder on creation, and delete it and //! its contents on destruction. This class is only available on host platforms (Windows, Mac, and Linux) class ScopedAutoTempDirectory From a43cd9531328d006f8487f449555318750712115 Mon Sep 17 00:00:00 2001 From: moraaar Date: Fri, 14 Jan 2022 10:01:38 +0000 Subject: [PATCH 49/49] Fixed physics periodic tests on linux platform (#6881) - Fixed path separators, now levels will be correctly opened in linux. - Fixed casing of physics paths. - Fixed casing of files inside levels Joints_HingeNoLimitsConstrained and RigidBody_KinematicModeWorks. - Resaved a level that still used old test ids in its name but the python tests didn't match. Linux: Start 21: AutomatedTesting::PhysicsTests_Periodic.periodic::TEST_RUN 8/11 Test #21: AutomatedTesting::PhysicsTests_Periodic.periodic::TEST_RUN .................. Passed 609.87 sec Windows: Start 21: AutomatedTesting::PhysicsTests_Periodic.periodic::TEST_RUN 8/13 Test #21: AutomatedTesting::PhysicsTests_Periodic.periodic::TEST_RUN ....................... Passed 810.59 sec Signed-off-by: moraaar --- ...ameCollisionGroupSameCustomLayerCollide.py | 2 +- ...tipleForcesInSameComponentCombineForces.py | 2 +- ...DefaultLibraryUpdatedAcrossLevels_after.py | 4 +- ...efaultLibraryUpdatedAcrossLevels_before.py | 2 +- .../Material_LibraryUpdatedAcrossLevels.py | 6 +- ...iptCanvas_SpawnEntityWithPhysComponents.py | 2 +- .../Joints_HingeNoLimitsConstrained.ly | 4 +- .../0/0.ly | 3 + .../0/filelist.xml | 6 + .../0/level.pak | 3 + .../tags.txt | 0 .../terraintexture.pak | 0 .../1/1.ly | 3 + .../1/filelist.xml | 6 + .../1/level.pak | 3 + .../tags.txt | 0 .../terraintexture.pak | 0 ...5_Material_LibraryUpdatedAcrossLevels_0.ly | 3 - .../filelist.xml | 6 - .../level.pak | 3 - .../leveldata/Environment.xml | 14 - .../leveldata/GameTokens.xml | 5 - .../leveldata/TerrainTexture.xml | 7 - .../leveldata/TimeOfDay.xml | 356 ------------------ .../leveldata/VegetationMap.dat | 3 - .../terrain/terrain.pxheightfield | 3 - ...5_Material_LibraryUpdatedAcrossLevels_1.ly | 3 - .../filelist.xml | 6 - .../level.pak | 3 - .../leveldata/Environment.xml | 14 - .../leveldata/GameTokens.xml | 5 - .../leveldata/TerrainTexture.xml | 7 - .../leveldata/TimeOfDay.xml | 356 ------------------ .../leveldata/VegetationMap.dat | 3 - .../terrain/terrain.pxheightfield | 3 - .../RigidBody_KinematicModeWorks.ly | 4 +- 36 files changed, 36 insertions(+), 814 deletions(-) create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/0.ly create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/filelist.xml create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/level.pak rename AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/{C15425935_Material_LibraryUpdatedAcrossLevels_0 => 0}/tags.txt (100%) rename AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/{C15425935_Material_LibraryUpdatedAcrossLevels_0 => 0}/terraintexture.pak (100%) create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/1.ly create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/filelist.xml create mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/level.pak rename AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/{C15425935_Material_LibraryUpdatedAcrossLevels_1 => 1}/tags.txt (100%) rename AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/{C15425935_Material_LibraryUpdatedAcrossLevels_1 => 1}/terraintexture.pak (100%) delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/C15425935_Material_LibraryUpdatedAcrossLevels_0.ly delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/filelist.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/level.pak delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/Environment.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/GameTokens.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TerrainTexture.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TimeOfDay.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/VegetationMap.dat delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terrain/terrain.pxheightfield delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/C15425935_Material_LibraryUpdatedAcrossLevels_1.ly delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/filelist.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/level.pak delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/Environment.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/GameTokens.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TerrainTexture.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TimeOfDay.xml delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/VegetationMap.dat delete mode 100644 AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terrain/terrain.pxheightfield diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SameCollisionGroupSameCustomLayerCollide.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SameCollisionGroupSameCustomLayerCollide.py index 2e40c0b557..8e8fccd725 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SameCollisionGroupSameCustomLayerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/collider/Collider_SameCollisionGroupSameCustomLayerCollide.py @@ -127,7 +127,7 @@ def Collider_SameCollisionGroupSameCustomLayerCollide(): # Main Script # 1) Load the level helper.init_idle() - helper.open_level("physics", "Collider_SameCollisionGroupSameCustomLayerCollide") + helper.open_level("Physics", "Collider_SameCollisionGroupSameCustomLayerCollide") # 2) Enter Game Mode helper.enter_game_mode(Tests.enter_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_MultipleForcesInSameComponentCombineForces.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_MultipleForcesInSameComponentCombineForces.py index 7307f3a2fb..e5f6e236cf 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_MultipleForcesInSameComponentCombineForces.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/force_region/ForceRegion_MultipleForcesInSameComponentCombineForces.py @@ -162,7 +162,7 @@ def ForceRegion_MultipleForcesInSameComponentCombineForces(): helper.init_idle() # 1) Load Level - helper.open_level("physics", "ForceRegion_MultipleForcesInSameComponentCombineForces") + helper.open_level("Physics", "ForceRegion_MultipleForcesInSameComponentCombineForces") # 2) Enter Game Mode helper.enter_game_mode(Tests.enter_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_after.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_after.py index 1f8cfae498..41ccc8f2dd 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_after.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_after.py @@ -229,8 +229,8 @@ def Material_DefaultLibraryUpdatedAcrossLevels_after(): for test in test_list: # 1) Open the correct level is open helper.open_level( - "physics", - f"Material_DefaultLibraryUpdatedAcrossLevels\\{test.level}" + "Physics", + os.path.join("Material_DefaultLibraryUpdatedAcrossLevels", str(test.level)) ) # 2) Enter Game Mode diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_before.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_before.py index 15db5808e2..d86a016be1 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_before.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_DefaultLibraryUpdatedAcrossLevels_before.py @@ -189,7 +189,7 @@ def Material_DefaultLibraryUpdatedAcrossLevels_before(): # 1) Open the correct level is open helper.open_level( "Physics", - f"Material_DefaultLibraryUpdatedAcrossLevels\\{test.level}" + os.path.join("Material_DefaultLibraryUpdatedAcrossLevels", str(test.level)) ) # 2) Enter Game Mode diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_LibraryUpdatedAcrossLevels.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_LibraryUpdatedAcrossLevels.py index 0b647361f6..6eebfe60bc 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_LibraryUpdatedAcrossLevels.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/material/Material_LibraryUpdatedAcrossLevels.py @@ -252,10 +252,8 @@ def Material_LibraryUpdatedAcrossLevels(): for test in test_list: # 1) Open the correct level for the test helper.open_level( - "physics", - "Material_LibraryUpdatedAcrossLevels\\Material_LibraryUpdatedAcrossLevels_{}".format( - test.level_index - ), + "Physics", + os.path.join("Material_LibraryUpdatedAcrossLevels", str(test.level_index)) ) # 2) Open Game Mode diff --git a/AutomatedTesting/Gem/PythonTests/Physics/tests/script_canvas/ScriptCanvas_SpawnEntityWithPhysComponents.py b/AutomatedTesting/Gem/PythonTests/Physics/tests/script_canvas/ScriptCanvas_SpawnEntityWithPhysComponents.py index 5862b7586b..e6e29063d6 100644 --- a/AutomatedTesting/Gem/PythonTests/Physics/tests/script_canvas/ScriptCanvas_SpawnEntityWithPhysComponents.py +++ b/AutomatedTesting/Gem/PythonTests/Physics/tests/script_canvas/ScriptCanvas_SpawnEntityWithPhysComponents.py @@ -106,7 +106,7 @@ def ScriptCanvas_SpawnEntityWithPhysComponents(): # Main Script helper.init_idle() # 1) Open Level - helper.open_level("physics", "ScriptCanvas_SpawnEntityWithPhysComponents") + helper.open_level("Physics", "ScriptCanvas_SpawnEntityWithPhysComponents") # 2) Enter Game Mode helper.enter_game_mode(Tests.enter_game_mode) diff --git a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly index 9babf84ba4..18b503bab5 100644 --- a/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly +++ b/AutomatedTesting/Levels/Physics/Joints_HingeNoLimitsConstrained/Joints_HingeNoLimitsConstrained.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60276c07b45a734e4f71d695278167ea61e884f8b513906168c9642078ad5954 -size 6045 +oid sha256:0d004c329a7c5044a8fe05b6dbbf9b19de29c60acec75f13fdbc344a55aab8c7 +size 6404 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/0.ly b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/0.ly new file mode 100644 index 0000000000..b908d29eab --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/0.ly @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e89946c224d2e765931e8ba8e33133ac24651af321a404016d9a9ea8c323db6c +size 8563 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/filelist.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/filelist.xml new file mode 100644 index 0000000000..bb94b733b3 --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/filelist.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/level.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/level.pak new file mode 100644 index 0000000000..614e41c4b2 --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/level.pak @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a46437ba86135567d87d43af0c4d499bf3cfe321721dbaf6e3cda8e49427ee1 +size 40212 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/tags.txt b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/tags.txt similarity index 100% rename from AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/tags.txt rename to AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/tags.txt diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terraintexture.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/terraintexture.pak similarity index 100% rename from AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terraintexture.pak rename to AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/0/terraintexture.pak diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/1.ly b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/1.ly new file mode 100644 index 0000000000..216426fc2a --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/1.ly @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95996da3902d885060e700c573d13144bab246b930dfeedaed6066c13c879b11 +size 8737 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/filelist.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/filelist.xml new file mode 100644 index 0000000000..5f5b4928fc --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/filelist.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/level.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/level.pak new file mode 100644 index 0000000000..d920770374 --- /dev/null +++ b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/level.pak @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:894dfd4ab92aa4d1d6003aebf82cf7ff854a8b3684e010234e073879f88b64e5 +size 40210 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/tags.txt b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/tags.txt similarity index 100% rename from AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/tags.txt rename to AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/tags.txt diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terraintexture.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/terraintexture.pak similarity index 100% rename from AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terraintexture.pak rename to AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/1/terraintexture.pak diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/C15425935_Material_LibraryUpdatedAcrossLevels_0.ly b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/C15425935_Material_LibraryUpdatedAcrossLevels_0.ly deleted file mode 100644 index a5953fc44b..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/C15425935_Material_LibraryUpdatedAcrossLevels_0.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:969b77ee335d2a04524a27c765dd2f2bdee048661f3ff7be0766ba69d18d5842 -size 10409 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/filelist.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/filelist.xml deleted file mode 100644 index 9a78b1bfd5..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/level.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/level.pak deleted file mode 100644 index 8e6b3b29cb..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:547d7fdfcd959569b69d78eb6f63c7c19fc90840d69ca2d46eca8a3e82b8db75 -size 39337 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/Environment.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/Environment.xml deleted file mode 100644 index 4ba36f66ae..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/GameTokens.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/GameTokens.xml deleted file mode 100644 index 668a4583bd..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/GameTokens.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TerrainTexture.xml deleted file mode 100644 index f43df05b22..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TimeOfDay.xml deleted file mode 100644 index 456d609b8a..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/leveldata/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terrain/terrain.pxheightfield b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terrain/terrain.pxheightfield deleted file mode 100644 index 011356e521..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_0/terrain/terrain.pxheightfield +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19096597087688692a225c1955f73d22c46354995fca1df8dff107a90c2f17a2 -size 4202594 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/C15425935_Material_LibraryUpdatedAcrossLevels_1.ly b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/C15425935_Material_LibraryUpdatedAcrossLevels_1.ly deleted file mode 100644 index 9f48f08a1b..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/C15425935_Material_LibraryUpdatedAcrossLevels_1.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:29f752c141514cb4b50aea07e6b63de5c8f43149ddac37722e9ddb8b5f4a667d -size 9495 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/filelist.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/filelist.xml deleted file mode 100644 index 9a78b1bfd5..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/level.pak b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/level.pak deleted file mode 100644 index 8e6b3b29cb..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:547d7fdfcd959569b69d78eb6f63c7c19fc90840d69ca2d46eca8a3e82b8db75 -size 39337 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/Environment.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/Environment.xml deleted file mode 100644 index 4ba36f66ae..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/GameTokens.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/GameTokens.xml deleted file mode 100644 index 668a4583bd..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/GameTokens.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TerrainTexture.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TerrainTexture.xml deleted file mode 100644 index f43df05b22..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TimeOfDay.xml b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TimeOfDay.xml deleted file mode 100644 index 456d609b8a..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/VegetationMap.dat b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/leveldata/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terrain/terrain.pxheightfield b/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terrain/terrain.pxheightfield deleted file mode 100644 index 011356e521..0000000000 --- a/AutomatedTesting/Levels/Physics/Material_LibraryUpdatedAcrossLevels/C15425935_Material_LibraryUpdatedAcrossLevels_1/terrain/terrain.pxheightfield +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19096597087688692a225c1955f73d22c46354995fca1df8dff107a90c2f17a2 -size 4202594 diff --git a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly index d6052ffa7c..a4b28a9e96 100644 --- a/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly +++ b/AutomatedTesting/Levels/Physics/RigidBody_KinematicModeWorks/RigidBody_KinematicModeWorks.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb97ada674d123c67d7a32eb65e81fa66472cf51f748054c4a4297649f2a0f40 -size 5568 +oid sha256:0f645243fb623258ed4b063c5a18ea414560496f97d7234782ca97884e0ed8f0 +size 5961