diff --git a/Source/Engine/Content/Assets/Animation.cpp b/Source/Engine/Content/Assets/Animation.cpp index b2de85bee..226074a72 100644 --- a/Source/Engine/Content/Assets/Animation.cpp +++ b/Source/Engine/Content/Assets/Animation.cpp @@ -83,7 +83,7 @@ void Animation::ClearCache() // Free memory MappingCache.Clear(); - MappingCache.Cleanup(); + MappingCache.SetCapacity(0); } const Animation::NodeToChannel* Animation::GetMapping(SkinnedModel* obj) diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index ae88d378b..2de3cecc9 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -378,10 +378,10 @@ public: // Insert ASSERT(pos.FreeSlotIndex != -1); - auto bucket = &_allocation.Get()[pos.FreeSlotIndex]; - bucket->Occupy(key); + Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex]; + bucket.Occupy(key); _elementsCount++; - return bucket->Value; + return bucket.Value; } /// @@ -481,7 +481,7 @@ public: #endif void ClearDelete() { - for (auto i = Begin(); i.IsNotEnd(); ++i) + for (Iterator i = Begin(); i.IsNotEnd(); ++i) { if (i->Value) Delete(i->Value); @@ -555,14 +555,6 @@ public: SetCapacity(capacity, preserveContents); } - /// - /// Cleanup collection data (changes size to 0 without data preserving). - /// - FORCE_INLINE void Cleanup() - { - SetCapacity(0, false); - } - /// /// Swaps the contents of collection with the other object without copy operation. Performs fast internal data exchange. /// @@ -640,7 +632,7 @@ public: void Add(const Iterator& i) { ASSERT(&i._collection != this && i); - Bucket& bucket = *i; + const Bucket& bucket = *i; Add(bucket.Key, bucket.Value); } @@ -693,7 +685,7 @@ public: int32 RemoveValue(const ValueType& value) { int32 result = 0; - for (auto i = Begin(); i.IsNotEnd(); ++i) + for (Iterator i = Begin(); i.IsNotEnd(); ++i) { if (i->Value == value) { @@ -714,16 +706,11 @@ public: template Iterator Find(const KeyComparableType& key) const { - if (HasItems()) - { - const Bucket* data = _allocation.Get(); - for (int32 i = 0; i < _size; i++) - { - if (data[i].IsOccupied() && data[i].Key == key) - return Iterator(*this, i); - } - } - return End(); + if (IsEmpty()) + return End(); + FindPositionResult pos; + FindPosition(key, pos); + return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); } /// @@ -794,7 +781,7 @@ public: { Clear(); SetCapacity(other.Capacity(), false); - for (auto i = other.Begin(); i != other.End(); ++i) + for (Iterator i = other.Begin(); i != other.End(); ++i) Add(i); ASSERT(Count() == other.Count()); ASSERT(Capacity() == other.Capacity()); @@ -807,7 +794,7 @@ public: template void GetKeys(Array& result) const { - for (auto i = Begin(); i.IsNotEnd(); ++i) + for (Iterator i = Begin(); i.IsNotEnd(); ++i) result.Add(i->Key); } @@ -818,7 +805,7 @@ public: template void GetValues(Array& result) const { - for (auto i = Begin(); i.IsNotEnd(); ++i) + for (Iterator i = Begin(); i.IsNotEnd(); ++i) result.Add(i->Value); } @@ -893,7 +880,7 @@ protected: while (checksCount < _size) { // Empty bucket - auto& bucket = data[bucketIndex]; + const Bucket& bucket = data[bucketIndex]; if (bucket.IsEmpty()) { // Found place to insert diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h index 5d105bcae..eb10a5015 100644 --- a/Source/Engine/Core/Collections/HashSet.h +++ b/Source/Engine/Core/Collections/HashSet.h @@ -2,78 +2,62 @@ #pragma once -#include "Engine/Core/Core.h" -#include "Engine/Core/Math/Math.h" -#include "Engine/Platform/Platform.h" -#include "HashFunctions.h" -#include "Config.h" +#include "Engine/Core/Memory/Memory.h" +#include "Engine/Core/Memory/Allocation.h" +#include "Engine/Core/Collections/HashFunctions.h" +#include "Engine/Core/Collections/Config.h" /// /// Template for unordered set of values (without duplicates with O(1) lookup access). /// /// The type of elements in the set. -template +/// The type of memory allocator. +template API_CLASS(InBuild) class HashSet { friend HashSet; - public: /// - /// Describes single portion of space for the item in a hash map + /// Describes single portion of space for the item in a hash map. /// struct Bucket { friend HashSet; - public: - enum State : byte { Empty, Deleted, Occupied, }; - - public: - + + /// The item. T Item; private: - State _state; - public: - - Bucket() - : _state(Empty) - { - } - - ~Bucket() - { - } - - public: - void Free() { + if (_state == Occupied) + Memory::DestructItem(&Item); _state = Empty; } void Delete() { _state = Deleted; + Memory::DestructItem(&Item); } - void Occupy(const T& item) + template + void Occupy(const ItemType& item) { - Item = item; + Memory::ConstructItems(&Item, &item, 1); _state = Occupied; } - public: - FORCE_INLINE bool IsEmpty() const { return _state == Empty; @@ -94,13 +78,15 @@ public: return _state != Occupied; } }; + + typedef typename AllocationType::template Data AllocationData; private: int32 _elementsCount = 0; int32 _deletedCount = 0; - int32 _tableSize = 0; - Bucket* _table = nullptr; + int32 _size = 0; + AllocationData _allocation; public: @@ -117,10 +103,27 @@ public: /// The initial capacity. HashSet(int32 capacity) { - ASSERT(capacity >= 0); SetCapacity(capacity); } + /// + /// Initializes a new instance of the class. + /// + /// The other collection to move. + HashSet(HashSet&& other) noexcept + : _elementsCount(other._elementsCount) + , _deletedCount(other._deletedCount) + , _size(other._size) + { + _elementsCount = other._elementsCount; + _deletedCount = other._deletedCount; + _size = other._size; + other._elementsCount = 0; + other._deletedCount = 0; + other._size = 0; + _allocation.Swap(other._allocation); + } + /// /// Initializes a new instance of the class. /// @@ -137,18 +140,39 @@ public: /// The reference to this. HashSet& operator=(const HashSet& other) { - // Ensure we're not trying to set to itself if (this != &other) Clone(other); return *this; } + /// + /// Moves the data from the other collection. + /// + /// The other collection to move. + /// The reference to this. + HashSet& operator=(HashSet&& other) noexcept + { + if (this != &other) + { + Clear(); + _allocation.Free(); + _elementsCount = other._elementsCount; + _deletedCount = other._deletedCount; + _size = other._size; + other._elementsCount = 0; + other._deletedCount = 0; + other._size = 0; + _allocation.Swap(other._allocation); + } + return *this; + } + /// /// Finalizes an instance of the class. /// ~HashSet() { - Cleanup(); + SetCapacity(0, false); } public: @@ -156,7 +180,6 @@ public: /// /// Gets the amount of the elements in the collection. /// - /// The amount of elements in the collection. FORCE_INLINE int32 Count() const { return _elementsCount; @@ -165,16 +188,14 @@ public: /// /// Gets the amount of the elements that can be contained by the collection. /// - /// The capacity of the collection. FORCE_INLINE int32 Capacity() const { - return _tableSize; + return _size; } /// /// Returns true if collection is empty. /// - /// True if is empty, otherwise false. FORCE_INLINE bool IsEmpty() const { return _elementsCount == 0; @@ -183,7 +204,6 @@ public: /// /// Returns true if collection has one or more elements. /// - /// True if isn't empty, otherwise false. FORCE_INLINE bool HasItems() const { return _elementsCount != 0; @@ -197,9 +217,7 @@ public: struct Iterator { friend HashSet; - private: - HashSet& _collection; int32 _index; @@ -223,41 +241,37 @@ public: { } + Iterator(Iterator&& i) + : _collection(i._collection) + , _index(i._index) + { + } + public: - /// - /// Checks if iterator is in the end of the collection - /// - /// True if is in the end, otherwise false FORCE_INLINE bool IsEnd() const { return _index == _collection.Capacity(); } - /// - /// Checks if iterator is not in the end of the collection - /// - /// True if is not in the end, otherwise false FORCE_INLINE bool IsNotEnd() const { return _index != _collection.Capacity(); } - public: - FORCE_INLINE Bucket& operator*() const { - return _collection._table[_index]; + return _collection._allocation.Get()[_index]; } FORCE_INLINE Bucket* operator->() const { - return &_collection._table[_index]; + return &_collection._allocation.Get()[_index]; } FORCE_INLINE explicit operator bool() const { - return _index >= 0 && _index < _collection._tableSize; + return _index >= 0 && _index < _collection._size; } FORCE_INLINE bool operator !() const @@ -275,17 +289,16 @@ public: return _index != v._index || &_collection != &v._collection; } - public: - Iterator& operator++() { const int32 capacity = _collection.Capacity(); if (_index != capacity) { + const Bucket* data = _collection._allocation.Get(); do { _index++; - } while (_index != capacity && _collection._table[_index].IsNotOccupied()); + } while (_index != capacity && data[_index].IsNotOccupied()); } return *this; } @@ -301,10 +314,11 @@ public: { if (_index > 0) { + const Bucket* data = _collection._allocation.Get(); do { _index--; - } while (_index > 0 && _collection._table[_index].IsNotOccupied()); + } while (_index > 0 && data[_index].IsNotOccupied()); } return *this; } @@ -324,22 +338,25 @@ public: /// void Clear() { - if (_table) + if (_elementsCount + _deletedCount != 0) { - // Free all buckets - // Note: this will not clear allocated objects space! - for (int32 i = 0; i < _tableSize; i++) - _table[i].Free(); + Bucket* data = _allocation.Get(); + for (int32 i = 0; i < _size; i++) + data[i].Free(); _elementsCount = _deletedCount = 0; } } /// - /// Clear the collection and delete value objects. + /// Clears the collection and delete value objects. + /// Note: collection must contain pointers to the objects that have public destructor and be allocated using New method. /// +#if defined(_MSC_VER) + template::Value>::Type> +#endif void ClearDelete() { - for (auto i = Begin(); i.IsNotEnd(); ++i) + for (Iterator i = Begin(); i.IsNotEnd(); ++i) { if (i->Value) ::Delete(i->Value); @@ -354,86 +371,63 @@ public: /// Enable/disable preserving collection contents during resizing void SetCapacity(int32 capacity, bool preserveContents = true) { - // Validate input - ASSERT(capacity >= 0); - - // Check if capacity won't change if (capacity == Capacity()) return; - - // Cache previous state - auto oldTable = _table; - auto oldTableSize = _tableSize; - - // Clear elements counters - const auto oldElementsCount = _elementsCount; + ASSERT(capacity >= 0); + AllocationData oldAllocation; + oldAllocation.Swap(_allocation); + const int32 oldSize = _size; + const int32 oldElementsCount = _elementsCount; _deletedCount = _elementsCount = 0; - - // Check if need to create a new table - if (capacity > 0) + if (capacity != 0 && (capacity & (capacity - 1)) != 0) { - // Align capacity value - if (Math::IsPowerOfTwo(capacity) == false) - capacity = Math::RoundUpToPowerOf2(capacity); - - // Allocate new table - _table = NewArray(capacity); - _tableSize = capacity; - - // Check if preserve content - if (oldElementsCount != 0 && preserveContents) + // Align capacity value to the next power of two (if it's not) + capacity++; + capacity |= capacity >> 1; + capacity |= capacity >> 2; + capacity |= capacity >> 4; + capacity |= capacity >> 8; + capacity |= capacity >> 16; + capacity = capacity + 1; + } + if (capacity) + { + _allocation.Allocate(capacity); + Bucket* data = _allocation.Get(); + for (int32 i = 0; i < capacity; i++) + data[i]._state = Bucket::Empty; + } + _size = capacity; + Bucket* oldData = oldAllocation.Get(); + if (oldElementsCount != 0 && preserveContents) + { + // TODO; move keys and values on realloc + for (int32 i = 0; i < oldSize; i++) { - // Try to preserve all values in the collection - for (int32 i = 0; i < oldTableSize; i++) - { - if (oldTable[i].IsOccupied()) - Add(oldTable[i].Item); - } + if (oldData[i].IsOccupied()) + Add(oldData[i].Item); } } - else + if (oldElementsCount != 0) { - // Clear data - _table = nullptr; - _tableSize = 0; - } - ASSERT(preserveContents == false || _elementsCount == oldElementsCount); - - // Delete old table - if (oldTable) - { - DeleteArray(oldTable, oldTableSize); + for (int32 i = 0; i < oldSize; i++) + oldData[i].Free(); } } /// - /// Increases collection capacity by given extra size (content will be preserved) + /// Ensures that collection has given capacity. /// - /// Extra size to enlarge collection - FORCE_INLINE void IncreaseCapacity(int32 extraSize) - { - ASSERT(extraSize >= 0); - SetCapacity(Capacity() + extraSize); - } - - /// - /// Ensures that collection has given capacity - /// - /// Minimum required capacity - void EnsureCapacity(int32 minCapacity) + /// The minimum required capacity. + /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. + void EnsureCapacity(int32 minCapacity, bool preserveContents = true) { if (Capacity() >= minCapacity) return; - int32 num = Capacity() == 0 ? DICTIONARY_DEFAULT_CAPACITY : Capacity() * 2; - SetCapacity(Math::Clamp(num, minCapacity, MAX_int32 - 1410)); - } - - /// - /// Cleanup collection data (changes size to 0 without data preserving) - /// - FORCE_INLINE void Cleanup() - { - SetCapacity(0, false); + if (minCapacity < DICTIONARY_DEFAULT_CAPACITY) + minCapacity = DICTIONARY_DEFAULT_CAPACITY; + const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity); + SetCapacity(capacity, preserveContents); } public: @@ -443,7 +437,8 @@ public: /// /// The element to add to the set. /// True if element has been added to the collection, otherwise false if the element is already present. - bool Add(const T& item) + template + bool Add(const ItemType& item) { // Ensure to have enough memory for the next item (in case of new element insertion) EnsureCapacity(_elementsCount + _deletedCount + 1); @@ -453,12 +448,12 @@ public: FindPosition(item, pos); // Check if object has been already added - if (pos.ObjectIndex != INVALID_INDEX) + if (pos.ObjectIndex != -1) return false; // Insert - ASSERT(pos.FreeSlotIndex != INVALID_INDEX); - auto bucket = &_table[pos.FreeSlotIndex]; + ASSERT(pos.FreeSlotIndex != -1); + Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex]; bucket->Occupy(item); _elementsCount++; @@ -472,7 +467,7 @@ public: void Add(const Iterator& i) { ASSERT(&i._collection != this && i); - Bucket& bucket = *i; + const Bucket& bucket = *i; Add(bucket.Item); } @@ -481,22 +476,20 @@ public: /// /// The element to remove. /// True if cannot remove item from the collection because cannot find it, otherwise false. - bool Remove(const T& item) + template + bool Remove(const ItemType& item) { if (IsEmpty()) return true; - FindPositionResult pos; FindPosition(item, pos); - - if (pos.ObjectIndex != INVALID_INDEX) + if (pos.ObjectIndex != -1) { - _table[pos.ObjectIndex].Delete(); + _allocation.Get()[pos.ObjectIndex].Delete(); _elementsCount--; _deletedCount++; return true; } - return false; } @@ -510,8 +503,8 @@ public: ASSERT(&i._collection == this); if (i) { - ASSERT(_table[i._index].IsOccupied()); - _table[i._index].Delete(); + ASSERT(_allocation.Get()[i._index].IsOccupied()); + _allocation.Get()[i._index].Delete(); _elementsCount--; _deletedCount++; return true; @@ -526,15 +519,14 @@ public: /// /// Item to find /// Iterator for the found element or End if cannot find it - Iterator Find(const T& item) const + template + Iterator Find(const ItemType& item) const { if (IsEmpty()) return End(); - FindPositionResult pos; FindPosition(item, pos); - - return pos.ObjectIndex != INVALID_INDEX ? Iterator(*this, pos.ObjectIndex) : End(); + return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); } /// @@ -542,15 +534,14 @@ public: /// /// The item to locate. /// True if value has been found in a collection, otherwise false - bool Contains(const T& item) const + template + bool Contains(const ItemType& item) const { if (IsEmpty()) return false; - FindPositionResult pos; FindPosition(item, pos); - - return pos.ObjectIndex != INVALID_INDEX; + return pos.ObjectIndex != -1; } public: @@ -561,41 +552,26 @@ public: /// Other collection to clone void Clone(const HashSet& other) { - // Clear previous data Clear(); - - // Update capacity SetCapacity(other.Capacity(), false); - - // Clone items - for (auto i = other.Begin(); i != other.End(); ++i) + for (Iterator i = other.Begin(); i != other.End(); ++i) Add(i); - - // Check ASSERT(Count() == other.Count()); ASSERT(Capacity() == other.Capacity()); } public: - /// - /// Gets iterator for beginning of the collection. - /// - /// Iterator for beginning of the collection. Iterator Begin() const { - Iterator i(*this, INVALID_INDEX); + Iterator i(*this, -1); ++i; return i; } - /// - /// Gets iterator for ending of the collection. - /// - /// Iterator for ending of the collection. Iterator End() const { - return Iterator(*this, _tableSize); + return Iterator(*this, _size); } Iterator begin() @@ -607,7 +583,7 @@ public: FORCE_INLINE Iterator end() { - return Iterator(*this, _tableSize); + return Iterator(*this, _size); } const Iterator begin() const @@ -619,11 +595,14 @@ public: FORCE_INLINE const Iterator end() const { - return Iterator(*this, _tableSize); + return Iterator(*this, _size); } protected: + /// + /// The result container of the set item lookup searching. + /// struct FindPositionResult { int32 ObjectIndex; @@ -632,42 +611,43 @@ protected: /// /// Returns a pair of positions: 1st where the object is, 2nd where - /// it would go if you wanted to insert it. 1st is INVALID_INDEX - /// if object is not found; 2nd is INVALID_INDEX if it is. + /// it would go if you wanted to insert it. 1st is -1 + /// if object is not found; 2nd is -1 if it is. /// Note: because of deletions where-to-insert is not trivial: it's the /// first deleted bucket we see, as long as we don't find the item later /// /// The item to find /// Pair of values: where the object is and where it would go if you wanted to insert it - void FindPosition(const T& item, FindPositionResult& result) const + template + void FindPosition(const ItemType& item, FindPositionResult& result) const { - ASSERT(_table); - - const int32 tableSizeMinusOne = _tableSize - 1; + ASSERT(_size); + const int32 tableSizeMinusOne = _size - 1; int32 bucketIndex = GetHash(item) & tableSizeMinusOne; - int32 insertPos = INVALID_INDEX; + int32 insertPos = -1; int32 numChecks = 0; - result.FreeSlotIndex = INVALID_INDEX; - - while (numChecks < _tableSize) + const Bucket* data = _allocation.Get(); + result.FreeSlotIndex = -1; + while (numChecks < _size) { // Empty bucket - if (_table[bucketIndex].IsEmpty()) + const Bucket& bucket = data[bucketIndex]; + if (bucket.IsEmpty()) { // Found place to insert - result.ObjectIndex = INVALID_INDEX; - result.FreeSlotIndex = insertPos == INVALID_INDEX ? bucketIndex : insertPos; + result.ObjectIndex = -1; + result.FreeSlotIndex = insertPos == -1 ? bucketIndex : insertPos; return; } // Deleted bucket - if (_table[bucketIndex].IsDeleted()) + if (bucket.IsDeleted()) { // Keep searching but mark to insert - if (insertPos == INVALID_INDEX) + if (insertPos == -1) insertPos = bucketIndex; } // Occupied bucket by target item - else if (_table[bucketIndex].Item == item) + else if (bucket.Item == item) { // Found item result.ObjectIndex = bucketIndex; @@ -675,10 +655,9 @@ protected: } numChecks++; - bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_tableSize, numChecks)) & tableSizeMinusOne; + bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_size, numChecks)) & tableSizeMinusOne; } - - result.ObjectIndex = INVALID_INDEX; + result.ObjectIndex = -1; result.FreeSlotIndex = insertPos; } }; diff --git a/Source/Engine/Particles/ParticleSystem.cpp b/Source/Engine/Particles/ParticleSystem.cpp index 6a1f32327..745bca545 100644 --- a/Source/Engine/Particles/ParticleSystem.cpp +++ b/Source/Engine/Particles/ParticleSystem.cpp @@ -461,7 +461,7 @@ void ParticleSystem::unload(bool isReloading) FramesPerSecond = 0.0f; DurationFrames = 0; Emitters.Resize(0); - EmittersParametersOverrides.Cleanup(); + EmittersParametersOverrides.SetCapacity(0); Tracks.Resize(0); } diff --git a/Source/flax.natvis b/Source/flax.natvis index 4cc1397d8..a38626357 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -61,24 +61,24 @@ - + - {{ Size={_elementsCount} Capacity={_tableSize} }} - - _elementsCount - _tableSize - - - _elementsCount - - - - _table[i] - - i++ - - - + {{ Size={_elementsCount} Capacity={_size} }} + + _elementsCount + _size + + + _elementsCount + + + + _allocation._data[i].Item + + i++ + + +