diff --git a/Tetragrama/EditorScene.h b/Tetragrama/EditorScene.h index 43fe507b..565eded4 100644 --- a/Tetragrama/EditorScene.h +++ b/Tetragrama/EditorScene.h @@ -29,10 +29,10 @@ namespace Tetragrama std::atomic_int SelectedSceneNode = -1; ZEngine::Core::Containers::Array HierarchiesNodeRef = {}; ZEngine::Core::Containers::Array Names = {}; - ZEngine::Core::Containers::HashMap NodeNames = {}; + ZEngine::Core::Containers::UnorderedHashMap NodeNames = {}; ZEngine::Helpers::Ref> PendingOnLoadHierarchies = nullptr; - ZEngine::Core::Containers::HashMap HashToAssetFile = {}; + ZEngine::Core::Containers::UnorderedHashMap HashToAssetFile = {}; ZEngine::Core::Containers::Array AssetFiles = {}; ZEngine::Core::Memory::ArenaAllocator LocalArena = {}; diff --git a/Tetragrama/Layers/ImguiLayer.h b/Tetragrama/Layers/ImguiLayer.h index c9cce6d4..862a73f7 100644 --- a/Tetragrama/Layers/ImguiLayer.h +++ b/Tetragrama/Layers/ImguiLayer.h @@ -2,8 +2,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -20,37 +20,37 @@ namespace Tetragrama::Layers ImguiLayer(cstring name = "ImGUI Layer") : ZEngine::Applications::Layer(name) {} virtual ~ImguiLayer(); - ZEngine::Core::Containers::Array NodeHierarchies = {}; - ZEngine::Core::Containers::Array NodeToRender = {}; - ZEngine::Core::Containers::HashMap NodeUIComponents = {}; - ZEngine::Core::Containers::HashMap KeyEntries = {}; + ZEngine::Core::Containers::Array NodeHierarchies = {}; + ZEngine::Core::Containers::Array NodeToRender = {}; + ZEngine::Core::Containers::UnorderedHashMap NodeUIComponents = {}; + ZEngine::Core::Containers::UnorderedHashMap KeyEntries = {}; - virtual void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, ZEngine::Applications::GameApplicationPtr app) override; - virtual void Deinitialize() override; + virtual void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, ZEngine::Applications::GameApplicationPtr app) override; + virtual void Deinitialize() override; - bool OnEvent(ZEngine::Core::CoreEvent& event) override; + bool OnEvent(ZEngine::Core::CoreEvent& event) override; - void Update(ZEngine::Core::TimeStep dt) override; + void Update(ZEngine::Core::TimeStep dt) override; - void Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) override; + void Render(ZEngine::Rendering::Renderers::GraphicRenderer* const renderer, ZEngine::Hardwares::CommandBuffer* const command_buffer) override; - int AddNode(Components::UIComponent* cmp, int parent, int depth); - virtual void AddUIComponent(Components::UIComponent* cmp, int parent, int depth); + int AddNode(Components::UIComponent* cmp, int parent, int depth); + virtual void AddUIComponent(Components::UIComponent* cmp, int parent, int depth); - bool OnKeyPressed(ZEngine::Windows::Events::KeyPressedEvent&) override; - bool OnKeyReleased(ZEngine::Windows::Events::KeyReleasedEvent&) override; + bool OnKeyPressed(ZEngine::Windows::Events::KeyPressedEvent&) override; + bool OnKeyReleased(ZEngine::Windows::Events::KeyReleasedEvent&) override; - bool OnMouseButtonPressed(ZEngine::Windows::Events::MouseButtonPressedEvent&) override; - bool OnMouseButtonReleased(ZEngine::Windows::Events::MouseButtonReleasedEvent&) override; - bool OnMouseButtonMoved(ZEngine::Windows::Events::MouseButtonMovedEvent&) override; - bool OnMouseButtonWheelMoved(ZEngine::Windows::Events::MouseButtonWheelEvent&) override; - bool OnTextInputRaised(ZEngine::Windows::Events::TextInputEvent&) override; + bool OnMouseButtonPressed(ZEngine::Windows::Events::MouseButtonPressedEvent&) override; + bool OnMouseButtonReleased(ZEngine::Windows::Events::MouseButtonReleasedEvent&) override; + bool OnMouseButtonMoved(ZEngine::Windows::Events::MouseButtonMovedEvent&) override; + bool OnMouseButtonWheelMoved(ZEngine::Windows::Events::MouseButtonWheelEvent&) override; + bool OnTextInputRaised(ZEngine::Windows::Events::TextInputEvent&) override; - bool OnWindowClosed(ZEngine::Windows::Events::WindowClosedEvent&) override; - bool OnWindowResized(ZEngine::Windows::Events::WindowResizedEvent&) override; - bool OnWindowMinimized(ZEngine::Windows::Events::WindowMinimizedEvent&) override; - bool OnWindowMaximized(ZEngine::Windows::Events::WindowMaximizedEvent&) override; - bool OnWindowRestored(ZEngine::Windows::Events::WindowRestoredEvent&) override; + bool OnWindowClosed(ZEngine::Windows::Events::WindowClosedEvent&) override; + bool OnWindowResized(ZEngine::Windows::Events::WindowResizedEvent&) override; + bool OnWindowMinimized(ZEngine::Windows::Events::WindowMinimizedEvent&) override; + bool OnWindowMaximized(ZEngine::Windows::Events::WindowMaximizedEvent&) override; + bool OnWindowRestored(ZEngine::Windows::Events::WindowRestoredEvent&) override; }; ZDEFINE_PTR(ImguiLayer); } // namespace Tetragrama::Layers diff --git a/ZEngine/ZEngine/Core/Containers/HashMap.h b/ZEngine/ZEngine/Core/Containers/HashMap.h index 4d74deec..e90292b0 100644 --- a/ZEngine/ZEngine/Core/Containers/HashMap.h +++ b/ZEngine/ZEngine/Core/Containers/HashMap.h @@ -1,9 +1,10 @@ -#pragma once +#pragma once #include #include #include #include #include +#include #include #include #include @@ -20,46 +21,41 @@ namespace ZEngine::Core::Containers }; template - struct HashEntry + struct OrderedHashEntry { - K key; - V value; - EntryState state = EntryState::Empty; + K key; + V value; + EntryState state = EntryState::Empty; + std::size_t prev = std::size_t(-1); + std::size_t next = std::size_t(-1); }; template - class HashMapIterator + class OrderedHashMapIterator { public: - using Entry = HashEntry; + using Entry = OrderedHashEntry; using EntryPointer = std::conditional_t; using value_type = std::conditional_t, std::pair>; using reference = value_type; using pointer = value_type*; - using iterator_category = std::input_iterator_tag; + using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; - // Constructs an iterator for the hash map's entries, starting at the given index. - // @param entries Value to the array of hash map entries. - // @param index Starting index for iteration. - HashMapIterator(EntryPointer entries, std::size_t index, std::size_t size) : m_entries(entries), m_index(index), m_size(size) + // Constructs an iterator for the hash map's entries, + // @param entries Pointer to the hash table slot array. + // @param index Current slot index; + OrderedHashMapIterator(EntryPointer entries, std::size_t index) : m_entries(entries), m_index(index) {} + OrderedHashMapIterator& operator++() { - advance_to_valid(); - } - - // Advances the iterator to the next occupied entry. - // @return Reference to the incremented iterator. - HashMapIterator& operator++() - { - ++m_index; - advance_to_valid(); + m_index = m_entries[m_index].next; return *this; } // Checks if two iterators are not equal based on their index. // @param other The iterator to compare with. // @return True if the iterators point to different indices, false otherwise. - bool operator!=(const HashMapIterator& other) const + bool operator!=(const OrderedHashMapIterator& other) const { return m_index != other.m_index; } @@ -67,7 +63,7 @@ namespace ZEngine::Core::Containers // Checks if two iterators are equal based on their index. // @param other The iterator to compare with. // @return True if the iterators point to the same index, false otherwise. - bool operator==(const HashMapIterator& other) const + bool operator==(const OrderedHashMapIterator& other) const { return m_index == other.m_index; } @@ -94,29 +90,25 @@ namespace ZEngine::Core::Containers return std::addressof(**this); } - private: - // Advances the iterator to the next occupied entry, skipping empty or deleted entries. - void advance_to_valid() + // Returns a const reference to the key at the current position. + const K& key() const { - while (m_index < m_size && m_entries[m_index].state != EntryState::Occupied) - { - ++m_index; - } + return m_entries[m_index].key; } + private: EntryPointer m_entries; std::size_t m_index; - std::size_t m_size; }; template class HashMap { public: - using Entry = HashEntry; + using Entry = OrderedHashEntry; using size_type = std::size_t; - using iterator = HashMapIterator; - using const_iterator = HashMapIterator; + using iterator = OrderedHashMapIterator; + using const_iterator = OrderedHashMapIterator; // Initializes the hash map with an allocator, initial capacity, and load factor. // @param allocator Pointer to the arena allocator for memory management. @@ -132,6 +124,8 @@ namespace ZEngine::Core::Containers m_entries.push({}); } m_size = 0; + m_head = size_type(-1); + m_tail = size_type(-1); } // Inserts a key-value pair into the hash map, updating the value if the key exists. @@ -151,6 +145,7 @@ namespace ZEngine::Core::Containers entry.key = key; entry.value = value; entry.state = EntryState::Occupied; + link_tail(index); ++m_size; } else @@ -171,6 +166,7 @@ namespace ZEngine::Core::Containers entry.key = key; entry.value = V{}; entry.state = EntryState::Occupied; + link_tail(index); ++m_size; } return entry.value; @@ -208,6 +204,14 @@ namespace ZEngine::Core::Containers return (index != size_type(-1)) ? &m_entries[index].value : nullptr; } + // Returns a pointer to the stored key if found, nullptr otherwise. + // Useful for callers that need a stable pointer into the map's key storage. + const K* find_key(const K& key) const + { + size_type index = probe_for_key(key); + return (index != size_type(-1)) ? &m_entries[index].key : nullptr; + } + // Checks if a key exists in the hash map. // @param key The key to check. // @return True if the key exists, false otherwise. @@ -216,28 +220,66 @@ namespace ZEngine::Core::Containers return find(key) != nullptr; } - // Removes a key-value pair from the hash map. - // @param key The key to remove. - // @note Marks the entry as Deleted; does not shrink the table. + // Removes the entry for a key, preserving insertion order of remaining entries. + // @note Marks the slot as Deleted; does not shrink the table. void remove(const K& key) { size_type index = probe_for_key(key); if (index != size_type(-1)) { + unlink(index); m_entries[index].state = EntryState::Deleted; --m_size; } } - // Clears all entries in the hash map, resetting it to an empty state. - // @note Sets all entries to Empty; does not change capacity. + // Clears all entries, resetting insertion-order state without changing capacity. void clear() { - for (auto& entry : m_entries) + for (size_type i = 0; i < m_entries.size(); ++i) { - entry.state = EntryState::Empty; + m_entries[i].state = EntryState::Empty; + m_entries[i].prev = size_type(-1); + m_entries[i].next = size_type(-1); } m_size = 0; + m_head = size_type(-1); + m_tail = size_type(-1); + } + + void sort_keys() + { + if (m_size < 2) + { + return; + } + + auto scratch = ZGetScratch(m_allocator); + + Array indices; + indices.init(scratch.Arena, m_size); + + size_type cur = m_head; + while (cur != size_type(-1)) + { + indices.push(cur); + cur = m_entries[cur].next; + } + + // Sort indices by key. + std::sort(indices.data(), indices.data() + indices.size(), [this](size_type a, size_type b) { return key_less(m_entries[a].key, m_entries[b].key); }); + + // Rebuild linked list in the new order. + m_head = indices[0]; + m_tail = indices[indices.size() - 1]; + + for (size_type i = 0; i < indices.size(); ++i) + { + m_entries[indices[i]].prev = (i > 0) ? indices[i - 1] : size_type(-1); + m_entries[indices[i]].next = (i + 1 < indices.size()) ? indices[i + 1] : size_type(-1); + } + + ZReleaseScratch(scratch); } // Checks if the hash map is empty. @@ -266,14 +308,14 @@ namespace ZEngine::Core::Containers // @note Iterators are invalidated by insert, remove, or reserve operations. iterator begin() { - return iterator(m_entries.data(), 0, m_entries.size()); + return iterator(m_entries.data(), m_head); } // Returns an iterator to the end of the hash map. // @return Iterator representing the past-the-end position. iterator end() { - return iterator(m_entries.data(), m_entries.size(), m_entries.size()); + return iterator(m_entries.data(), size_type(-1)); } // Returns a const iterator to the first occupied entry. @@ -281,14 +323,14 @@ namespace ZEngine::Core::Containers // @note Iterators are invalidated by insert, remove, or reserve operations. const_iterator begin() const { - return const_iterator(m_entries.data(), 0, m_entries.size()); + return const_iterator(m_entries.data(), m_head); } // Returns a const iterator to the end of the hash map. // @return Const iterator representing the past-the-end position. const_iterator end() const { - return const_iterator(m_entries.data(), m_entries.size(), m_entries.size()); + return const_iterator(m_entries.data(), size_type(-1)); } // Returns a const iterator to the first occupied entry (alias for begin() const). @@ -317,13 +359,51 @@ namespace ZEngine::Core::Containers } private: - // Checks if the hash map needs to grow based on the load factor and resizes if necessary. - // @note Triggers rehashing if (m_size + 1) / capacity > load_factor. + // Appends slot index to the tail of the insertion-order linked list. + void link_tail(size_type index) + { + m_entries[index].prev = m_tail; + m_entries[index].next = size_type(-1); + if (m_tail != size_type(-1)) + { + m_entries[m_tail].next = index; + } + else + { + m_head = index; + } + m_tail = index; + } + + // Removes slot index from the insertion-order linked list. + void unlink(size_type index) + { + auto& entry = m_entries[index]; + if (entry.prev != size_type(-1)) + { + m_entries[entry.prev].next = entry.next; + } + else + { + m_head = entry.next; + } + if (entry.next != size_type(-1)) + { + m_entries[entry.next].prev = entry.prev; + } + else + { + m_tail = entry.prev; + } + entry.prev = size_type(-1); + entry.next = size_type(-1); + } + void maybe_grow() { if (static_cast(m_size + 1) / m_entries.size() > m_load_factor) { - size_type new_capacity = std::max(16, static_cast(m_entries.size() * 1.5f)); // Growth factor 1.5 + size_type new_capacity = std::max(16, static_cast(m_entries.size() * 1.5f)); rehash(new_capacity); } } @@ -341,28 +421,49 @@ namespace ZEngine::Core::Containers } } + bool key_less(const K& a, const K& b) const + { + if constexpr (std::is_same_v) + { + return Helpers::secure_strcmp(a, b) < 0; + } + else + { + return a < b; + } + } + // Rehashes the hash map to a new capacity, reinserting all occupied entries. // @param new_capacity The new number of slots. // @note Moves the old entries to avoid copying and skips Deleted entries. void rehash(size_type new_capacity) { - Array old_entries = m_entries; // Move to avoid copying + Array old_entries = m_entries; + size_type old_head = m_head; + m_entries = Array{}; m_entries.init(m_allocator, new_capacity); for (size_type i = 0; i < new_capacity; ++i) { m_entries.push({}); } - m_size = 0; + m_size = 0; - for (size_type i = 0; i < old_entries.size(); ++i) + m_head = size_type(-1); + m_tail = size_type(-1); + + size_type cur = old_head; + while (cur != size_type(-1)) { - if (old_entries[i].state == EntryState::Occupied) - { - size_type index = probe_for_insert(old_entries[i].key); - m_entries[index] = old_entries[i]; // Direct assignment - ++m_size; - } + size_type old_next = old_entries[cur].next; + size_type index = probe_for_insert(old_entries[cur].key); + auto& entry = m_entries[index]; + entry.key = old_entries[cur].key; + entry.value = old_entries[cur].value; + entry.state = EntryState::Occupied; + link_tail(index); + ++m_size; + cur = old_next; } } @@ -447,20 +548,12 @@ namespace ZEngine::Core::Containers } } - // Computes a secondary hash for double hashing to determine probe step size. - // @param key The key to hash. - // @return A non-zero step size for probing. - size_type double_hash(const K& key) const - { - size_type h = hash(key); - // Ensure step is non-zero and relatively prime to capacity - return (h % m_entries.size()) | 1; // Odd step size - } - Memory::ArenaAllocator* m_allocator = nullptr; Array m_entries; size_type m_size = 0; float m_load_factor = 0.75f; + size_type m_head = size_type(-1); + size_type m_tail = size_type(-1); }; // Computes a hash value for a C-string using rapidhash. diff --git a/ZEngine/ZEngine/Core/Containers/HashSet.h b/ZEngine/ZEngine/Core/Containers/HashSet.h new file mode 100644 index 00000000..31e52288 --- /dev/null +++ b/ZEngine/ZEngine/Core/Containers/HashSet.h @@ -0,0 +1,196 @@ +#pragma once +#include + +namespace ZEngine::Core::Containers +{ + template + class HashSetKeyIterator + { + public: + using value_type = K; + using reference = const K&; + using pointer = const K*; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + + explicit HashSetKeyIterator(MapIt it) : m_it(it) {} + + // Advances the iterator to the next entry in insertion order. + // @return Reference to the incremented iterator. + HashSetKeyIterator& operator++() + { + ++m_it; + return *this; + } + + // Checks if two iterators are not equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to different indices, false otherwise. + bool operator==(const HashSetKeyIterator& other) const + { + return m_it == other.m_it; + } + + // Checks if two iterators are equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to the same index, false otherwise. + bool operator!=(const HashSetKeyIterator& other) const + { + return m_it != other.m_it; + } + + // Dereferences the iterator to return a const reference to the current key. + // @return Const reference to the key at the current position. + const K& operator*() const + { + return m_it.key(); + } + + // Provides pointer-like access to the current key. + // @return Const pointer to the key at the current position. + const K* operator->() const + { + return &m_it.key(); + } + + private: + MapIt m_it; + }; + + template + class HashSet + { + public: + using MapType = HashMap; + using size_type = std::size_t; + using iterator = HashSetKeyIterator; + using const_iterator = HashSetKeyIterator; + + // @param allocator Arena allocator for memory management. + // @param initial_capacity Initial number of slots (default: 16). + void init(Memory::ArenaAllocator* allocator, size_type initial_capacity = 16) + { + m_map.init(allocator, initial_capacity); + } + + // Inserts a key into the set. + // New keys are appended to the end of the insertion-order sequence. + // Resizes the set if the load factor would be exceeded. + // @param key The key to insert. + void insert(const K& key) + { + m_map.insert(key, true); + } + + // Finds a key in the set. + // @param key The key to look up. + // @return Const pointer to the stored key if found, nullptr otherwise. + const K* find(const K& key) const + { + return m_map.find_key(key); + } + + // Checks if a key exists in the hash set. + // @param key The key to check. + // @return True if the key exists, false otherwise. + bool contains(const K& key) const + { + return m_map.contains(key); + } + + // Removes a key from the set, preserving insertion order of remaining keys. + // @param key The key to remove. + // @note Marks the slot as Deleted. + void remove(const K& key) + { + m_map.remove(key); + } + + // Clears all entries, resetting insertion-order state without changing capacity. + // @note Sets all entries to Empty; does not change capacity. + void clear() + { + m_map.clear(); + } + + // Reorders the iteration sequence so keys are visited in ascending order (operator<). + // For const char* keys, strcmp ordering is used. + // Does not affect slot positions or lookup performance — only the linked-list order. + void sort_keys() + { + m_map.sort_keys(); + } + + // Checks if the hash set is empty. + // @return True if the set contains no keys, false otherwise. + bool empty() const + { + return m_map.empty(); + } + + // Returns the number of keys in the hash set. + // @return The number of occupied entries. + size_type size() const + { + return m_map.size(); + } + + // Returns the current capacity of the hash set. + // @return The number of slots in the underlying array. + size_type capacity() const + { + return m_map.capacity(); + } + + // Returns an iterator to the first entry in the current iteration order. + // @note Iterators are invalidated by insert, remove, or reserve operations. + iterator begin() + { + return iterator(m_map.begin()); + } + + // Returns an iterator to the end of the hash set. + // @return Iterator representing the past-the-end position. + iterator end() + { + return iterator(m_map.end()); + } + + // Returns a const iterator to the first entry in the current iteration order. + // @note Iterators are invalidated by insert, remove, or reserve operations. + const_iterator begin() const + { + return const_iterator(m_map.begin()); + } + + // Returns a const iterator to the end of the hash set. + // @return Const iterator representing the past-the-end position. + const_iterator end() const + { + return const_iterator(m_map.end()); + } + + // Returns a const iterator to the first entry (alias for begin() const). + const_iterator cbegin() const + { + return begin(); + } + + // Returns a const iterator to the end (alias for end() const). + const_iterator cend() const + { + return end(); + } + + // Ensures the hash set has at least the specified capacity. + // @param new_capacity Desired minimum number of slots. + // @note Rehashes the set if the new capacity is greater than the current capacity. + void reserve(size_type n) + { + m_map.reserve(n); + } + + private: + MapType m_map; + }; +} // namespace ZEngine::Core::Containers diff --git a/ZEngine/ZEngine/Core/Containers/UnorderedHashMap.h b/ZEngine/ZEngine/Core/Containers/UnorderedHashMap.h new file mode 100644 index 00000000..47c3b6d8 --- /dev/null +++ b/ZEngine/ZEngine/Core/Containers/UnorderedHashMap.h @@ -0,0 +1,476 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ZEngine::Core::Containers +{ + enum class EntryState + { + Empty, + Occupied, + Deleted + }; + + template + struct HashEntry + { + K key; + V value; + EntryState state = EntryState::Empty; + }; + + template + class HashMapIterator + { + public: + using Entry = HashEntry; + using EntryPointer = std::conditional_t; + using value_type = std::conditional_t, std::pair>; + using reference = value_type; + using pointer = value_type*; + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + + // Constructs an iterator for the hash map's entries, starting at the given index. + // @param entries Value to the array of hash map entries. + // @param index Starting index for iteration. + HashMapIterator(EntryPointer entries, std::size_t index, std::size_t size) : m_entries(entries), m_index(index), m_size(size) + { + advance_to_valid(); + } + + // Advances the iterator to the next occupied entry. + // @return Reference to the incremented iterator. + HashMapIterator& operator++() + { + ++m_index; + advance_to_valid(); + return *this; + } + + // Checks if two iterators are not equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to different indices, false otherwise. + bool operator!=(const HashMapIterator& other) const + { + return m_index != other.m_index; + } + + // Checks if two iterators are equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to the same index, false otherwise. + bool operator==(const HashMapIterator& other) const + { + return m_index == other.m_index; + } + + // Dereferences the iterator to return a key-value pair for the current entry. + // @return A pair containing references to the key and value (const or non-const based on IsConst). + value_type operator*() const + { + const auto& entry = m_entries[m_index]; + if constexpr (IsConst) + { + return {entry.key, entry.value}; + } + else + { + return {entry.key, const_cast(entry.value)}; + } + } + + // Provides pointer-like access to the current key-value pair. + // @return A pointer to a temporary key-value pair. + pointer operator->() const + { + return std::addressof(**this); + } + + // Returns a const reference to the key at the current position. + const K& key() const + { + return m_entries[m_index].key; + } + + private: + // Advances the iterator to the next occupied entry, skipping empty or deleted entries. + void advance_to_valid() + { + while (m_index < m_size && m_entries[m_index].state != EntryState::Occupied) + { + ++m_index; + } + } + + EntryPointer m_entries; + std::size_t m_index; + std::size_t m_size; + }; + + template + class UnorderedHashMap + { + public: + using Entry = HashEntry; + using size_type = std::size_t; + using iterator = HashMapIterator; + using const_iterator = HashMapIterator; + + // Initializes the hash map with an allocator, initial capacity, and load factor. + // @param allocator Pointer to the arena allocator for memory management. + // @param initial_capacity Initial number of slots (default: 16). + // @param load_factor Maximum load factor before resizing (default: 0.75). + void init(Memory::ArenaAllocator* allocator, size_type initial_capacity = 16) + { + m_allocator = allocator; + m_load_factor = 0.75f; + m_entries.init(m_allocator, initial_capacity); + for (size_type i = 0; i < initial_capacity; ++i) + { + m_entries.push({}); + } + m_size = 0; + } + + // Inserts a key-value pair into the hash map, updating the value if the key exists. + // Resizes the map if the load factor would be exceeded. + // @param key The key to insert. + // @param value The value to associate with the key. + // @throws std::runtime_error if the table is full and cannot be resized. + void insert(const K& key, const V& value) + { + maybe_grow(); + + size_type index = probe_for_insert(key); + auto& entry = m_entries[index]; + + if (entry.state == EntryState::Empty || entry.state == EntryState::Deleted) + { + entry.key = key; + entry.value = value; + entry.state = EntryState::Occupied; + ++m_size; + } + else + { + entry.value = value; + } + } + + V& operator[](const K& key) + { + maybe_grow(); + + size_type index = probe_for_insert(key); + auto& entry = m_entries[index]; + + if (entry.state == EntryState::Empty || entry.state == EntryState::Deleted) + { + entry.key = key; + entry.value = V{}; + entry.state = EntryState::Occupied; + ++m_size; + } + return entry.value; + } + + // Retrieves a const reference to the value associated with a key. + // @param key The key to look up. + // @return Const reference to the value. + // @throws std::out_of_range if the key is not found. + const V& at(const K& key) const + { + size_type index = probe_for_key(key); + if (index == size_type(-1)) + { + throw std::out_of_range("Key not found in UnorderedHashMap"); + } + return m_entries[index].value; + } + + // Finds the value associated with a key. + // @param key The key to look up. + // @return Pointer to the value if found, nullptr otherwise. + V* find(const K& key) + { + size_type index = probe_for_key(key); + return (index != size_type(-1)) ? &m_entries[index].value : nullptr; + } + + // Finds the value associated with a key (const version). + // @param key The key to look up. + // @return Const pointer to the value if found, nullptr otherwise. + const V* find(const K& key) const + { + size_type index = probe_for_key(key); + return (index != size_type(-1)) ? &m_entries[index].value : nullptr; + } + + // Returns a pointer to the stored key if found, nullptr otherwise. + const K* find_key(const K& key) const + { + size_type index = probe_for_key(key); + return (index != size_type(-1)) ? &m_entries[index].key : nullptr; + } + + // Checks if a key exists in the hash map. + // @param key The key to check. + // @return True if the key exists, false otherwise. + bool contains(const K& key) const + { + return find(key) != nullptr; + } + + // Removes a key-value pair from the hash map. + // @param key The key to remove. + // @note Marks the entry as Deleted; does not shrink the table. + void remove(const K& key) + { + size_type index = probe_for_key(key); + if (index != size_type(-1)) + { + m_entries[index].state = EntryState::Deleted; + --m_size; + } + } + + // Clears all entries in the hash map, resetting it to an empty state. + // @note Sets all entries to Empty; does not change capacity. + void clear() + { + for (auto& entry : m_entries) + { + entry.state = EntryState::Empty; + } + m_size = 0; + } + + // Checks if the hash map is empty. + // @return True if the map contains no key-value pairs, false otherwise. + bool empty() const + { + return m_size == 0; + } + + // Returns the number of key-value pairs in the hash map. + // @return The number of occupied entries. + size_type size() const + { + return m_size; + } + + // Returns the current capacity of the hash map. + // @return The number of slots in the underlying array. + size_type capacity() const + { + return m_entries.size(); + } + + // Returns an iterator to the first occupied entry. + // @return Iterator pointing to the first key-value pair or end() if empty. + // @note Iterators are invalidated by insert, remove, or reserve operations. + iterator begin() + { + return iterator(m_entries.data(), 0, m_entries.size()); + } + + // Returns an iterator to the end of the hash map. + // @return Iterator representing the past-the-end position. + iterator end() + { + return iterator(m_entries.data(), m_entries.size(), m_entries.size()); + } + + // Returns a const iterator to the first occupied entry. + // @return Const iterator pointing to the first key-value pair or end() if empty. + // @note Iterators are invalidated by insert, remove, or reserve operations. + const_iterator begin() const + { + return const_iterator(m_entries.data(), 0, m_entries.size()); + } + + // Returns a const iterator to the end of the hash map. + // @return Const iterator representing the past-the-end position. + const_iterator end() const + { + return const_iterator(m_entries.data(), m_entries.size(), m_entries.size()); + } + + // Returns a const iterator to the first occupied entry (alias for begin() const). + // @return Const iterator pointing to the first key-value pair or end() if empty. + const_iterator cbegin() const + { + return begin(); + } + + // Returns a const iterator to the end of the hash map (alias for end() const). + // @return Const iterator representing the past-the-end position. + const_iterator cend() const + { + return end(); + } + + // Ensures the hash map has at least the specified capacity. + // @param new_capacity Desired minimum number of slots. + // @note Rehashes the map if the new capacity is greater than the current capacity. + void reserve(size_type new_capacity) + { + if (new_capacity > m_entries.size()) + { + rehash(new_capacity); + } + } + + private: + // Checks if the hash map needs to grow based on the load factor and resizes if necessary. + // @note Triggers rehashing if (m_size + 1) / capacity > load_factor. + void maybe_grow() + { + if (static_cast(m_size + 1) / m_entries.size() > m_load_factor) + { + size_type new_capacity = std::max(16, static_cast(m_entries.size() * 1.5f)); // Growth factor 1.5 + rehash(new_capacity); + } + } + + // Compare keys, specialized for const char* + bool key_equals(const K& a, const K& b) const + { + if constexpr (std::is_same_v) + { + return Helpers::secure_strcmp(a, b) == 0; + } + else + { + return a == b; + } + } + + // Rehashes the hash map to a new capacity, reinserting all occupied entries. + // @param new_capacity The new number of slots. + // @note Moves the old entries to avoid copying and skips Deleted entries. + void rehash(size_type new_capacity) + { + Array old_entries = m_entries; // Move to avoid copying + m_entries = Array{}; + m_entries.init(m_allocator, new_capacity); + for (size_type i = 0; i < new_capacity; ++i) + { + m_entries.push({}); + } + m_size = 0; + + for (size_type i = 0; i < old_entries.size(); ++i) + { + if (old_entries[i].state == EntryState::Occupied) + { + size_type index = probe_for_insert(old_entries[i].key); + m_entries[index] = old_entries[i]; // Direct assignment + ++m_size; + } + } + } + + // Probes for a key using quadratic probing. + // @param key The key to look up. + // @return Index of the key if found, or size_type(-1) if not found. + size_type probe_for_key(const K& key) const + { + size_type index = hash(key) % m_entries.size(); + size_type i = 0; + + do + { + const auto& entry = m_entries[index]; + if (entry.state == EntryState::Empty) + { + return size_type(-1); + } + if (entry.state == EntryState::Occupied && key_equals(entry.key, key)) + { + return index; + } + ++i; + index = (index + i) % m_entries.size(); + } while (i < m_entries.size()); + + return size_type(-1); + } + + // Probes for a slot to insert a key, preferring deleted slots if available. + // @param key The key to insert. + // @return Index of the slot to use for insertion or the existing key. + // @throws std::runtime_error if the table is full and no slot is found. + size_type probe_for_insert(const K& key) + { + size_type index = hash(key) % m_entries.size(); + size_type first_deleted = size_type(-1); + size_type i = 0; + + do + { + auto& entry = m_entries[index]; + if (entry.state == EntryState::Occupied && key_equals(entry.key, key)) + { + return index; + } + + if (entry.state == EntryState::Empty) + { + return (first_deleted != size_type(-1)) ? first_deleted : index; + } + + if (entry.state == EntryState::Deleted && first_deleted == size_type(-1)) + { + first_deleted = index; + } + + ++i; + index = (index + i) % m_entries.size(); + } while (i < m_entries.size()); + + if (first_deleted != size_type(-1)) + { + return first_deleted; + } + + throw std::runtime_error("UnorderedHashMap probe failed: table full"); + } + + // Computes the hash value for a key using the provided hasher. + // @param key The key to hash. + // @return The hash value. + size_type hash(const K& key) const + { + if constexpr (std::is_same_v) + { + return rapidhash(key, Helpers::secure_strlen(key)); + } + else + { + return rapidhash(&key, sizeof(K)); + } + } + + Memory::ArenaAllocator* m_allocator = nullptr; + Array m_entries; + size_type m_size = 0; + float m_load_factor = 0.75f; + }; + + // Computes a hash value for a C-string using rapidhash. + // @param str The null-terminated string to hash. + // @return The hash value. + inline uint64_t hash_compute(const char* str) + { + return rapidhash(str, Helpers::secure_strlen(str)); + } +} // namespace ZEngine::Core::Containers \ No newline at end of file diff --git a/ZEngine/ZEngine/Core/Containers/UnorderedHashSet.h b/ZEngine/ZEngine/Core/Containers/UnorderedHashSet.h new file mode 100644 index 00000000..672b0cf5 --- /dev/null +++ b/ZEngine/ZEngine/Core/Containers/UnorderedHashSet.h @@ -0,0 +1,185 @@ +#pragma once +#include + +namespace ZEngine::Core::Containers +{ + template + class UnorderedHashSetKeyIterator + { + public: + using value_type = K; + using reference = const K&; + using pointer = const K*; + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + + explicit UnorderedHashSetKeyIterator(MapIt it) : m_it(it) {} + + // Advances the iterator to the next occupied entry. + // @return Reference to the incremented iterator. + UnorderedHashSetKeyIterator& operator++() + { + ++m_it; + return *this; + } + + // Checks if two iterators are not equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to different indices, false otherwise. + bool operator!=(const UnorderedHashSetKeyIterator& other) const + { + return m_it != other.m_it; + } + + // Checks if two iterators are equal based on their index. + // @param other The iterator to compare with. + // @return True if the iterators point to the same index, false otherwise. + bool operator==(const UnorderedHashSetKeyIterator& other) const + { + return m_it == other.m_it; + } + + const K& operator*() const + { + return m_it.key(); + } + const K* operator->() const + { + return &m_it.key(); + } + + private: + MapIt m_it; + }; + + template + class UnorderedHashSet + { + public: + using MapType = UnorderedHashMap; + using size_type = std::size_t; + using iterator = UnorderedHashSetKeyIterator; + using const_iterator = UnorderedHashSetKeyIterator; + + // Initializes the hash set with an allocator and initial slot capacity. + // @param allocator Arena allocator for memory management. + // @param initial_capacity Initial number of slots (default: 16). + void init(Memory::ArenaAllocator* allocator, size_type initial_capacity = 16) + { + m_map.init(allocator, initial_capacity); + } + + // Inserts a key into the set. No-op if the key already exists. + // Resizes the set if the load factor would be exceeded. + // @param key The key to insert. + void insert(const K& key) + { + m_map.insert(key, true); + } + + // Finds a key in the set. + // @param key The key to look up. + // @return Const pointer to the stored key if found, nullptr otherwise. + const K* find(const K& key) const + { + return m_map.find_key(key); + } + + // Checks if a key exists in the hash set. + // @param key The key to check. + // @return True if the key exists, false otherwise. + bool contains(const K& key) const + { + return m_map.contains(key); + } + + // Removes a key from the set. + // @param key The key to remove. + // @note Marks the slot as Deleted; does not shrink the table. + void remove(const K& key) + { + m_map.remove(key); + } + + // Clears all entries in the hash set, resetting it to an empty state. + // @note Sets all entries to Empty; does not change capacity. + void clear() + { + m_map.clear(); + } + + // Checks if the hash set is empty. + // @return True if the set contains no keys, false otherwise. + bool empty() const + { + return m_map.empty(); + } + + // Returns the number of keys in the hash set. + // @return The number of occupied entries. + size_type size() const + { + return m_map.size(); + } + + // Returns the current capacity of the hash set. + // @return The number of slots in the underlying array. + size_type capacity() const + { + return m_map.capacity(); + } + + // Returns an iterator to the first occupied entry. + // @return Iterator pointing to the first key or end() if empty. + // @note Iterators are invalidated by insert, remove, or reserve operations. + iterator begin() + { + return iterator(m_map.begin()); + } + + // Returns an iterator to the end of the hash set. + // @return Iterator representing the past-the-end position. + iterator end() + { + return iterator(m_map.end()); + } + + // Returns a const iterator to the first occupied entry. + // @return Const iterator pointing to the first key or end() if empty. + // @note Iterators are invalidated by insert, remove, or reserve operations. + const_iterator begin() const + { + return const_iterator(m_map.begin()); + } + + // Returns a const iterator to the end of the hash set. + // @return Const iterator representing the past-the-end position. + const_iterator end() const + { + return const_iterator(m_map.end()); + } + + // Returns a const iterator to the first entry (alias for begin() const). + const_iterator cbegin() const + { + return begin(); + } + + // Returns a const iterator to the end (alias for end() const). + const_iterator cend() const + { + return end(); + } + + // Ensures the hash set has at least the specified capacity. + // @param new_capacity Desired minimum number of slots. + // @note Rehashes the set if the new capacity is greater than the current capacity. + void reserve(size_type n) + { + m_map.reserve(n); + } + + private: + MapType m_map; + }; +} // namespace ZEngine::Core::Containers diff --git a/ZEngine/ZEngine/Hardwares/DeviceSwapchain.cpp b/ZEngine/ZEngine/Hardwares/DeviceSwapchain.cpp index 17cc4596..2490d548 100644 --- a/ZEngine/ZEngine/Hardwares/DeviceSwapchain.cpp +++ b/ZEngine/ZEngine/Hardwares/DeviceSwapchain.cpp @@ -384,10 +384,10 @@ namespace ZEngine::Hardwares VkPipelineStageFlags StageMask = 0; }; - Array wait_semaphores = {}; - Array wait_values = {}; - Array stage_flags = {}; - HashMap max_val_timeline_semaphores = {}; + Array wait_semaphores = {}; + Array wait_values = {}; + Array stage_flags = {}; + UnorderedHashMap max_val_timeline_semaphores = {}; wait_semaphores.init(scratch.Arena, 10); stage_flags.init(scratch.Arena, 10); diff --git a/ZEngine/ZEngine/Hardwares/VulkanDevice.h b/ZEngine/ZEngine/Hardwares/VulkanDevice.h index 1af3792c..d88eda40 100644 --- a/ZEngine/ZEngine/Hardwares/VulkanDevice.h +++ b/ZEngine/ZEngine/Hardwares/VulkanDevice.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -586,78 +586,78 @@ namespace ZEngine::Hardwares */ struct VulkanDevice { - bool HasSeperateTransfertQueueFamily = false; - bool PhysicalDeviceSupportSampledImageBindless = false; - bool PhysicalDeviceSupportStorageBufferBindless = false; - bool PhysicalDeviceSupportTimelineSemaphore = false; - const char* ApplicationName = "Tetragrama"; - const char* EngineName = "ZEngine"; - uint32_t WorkerThreadCount = 1; - uint32_t GraphicFamilyIndex = std::numeric_limits::max(); - uint32_t TransferFamilyIndex = std::numeric_limits::max(); - - uint32_t WriteDescriptorSetIndex = 0; - uint32_t MaxGlobalTexture = 600; - VkInstance Instance = VK_NULL_HANDLE; - VkSurfaceKHR Surface = VK_NULL_HANDLE; - VkSurfaceFormatKHR SurfaceFormat = {}; - VkPresentModeKHR PresentMode = {}; - VkPhysicalDeviceProperties2 PhysicalDeviceProperties = {}; - VkPhysicalDeviceVulkan12Properties PhysicalDeviceVulkan12Properties = {}; - VkDevice LogicalDevice = VK_NULL_HANDLE; - VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE; - VkPhysicalDeviceFeatures2 PhysicalDeviceFeature = {}; - VkPhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties = {}; - VkSampler GlobalLinearWrapSampler = VK_NULL_HANDLE; - VkDescriptorPool GlobalDescriptorPoolHandle = VK_NULL_HANDLE; - VmaAllocator VmaAllocatorValue = nullptr; - VkDescriptorImageInfo GlobalLinearWrapSamplerImageInfo = {}; - CommandBufferManagerPtr CommandBufferMgr = {}; - DeviceSwapchainPtr SwapchainPtr = {}; - Core::Containers::Array DefaultDepthFormats = {}; - - Core::Containers::HashMap> ShaderCaches = {}; - Core::Containers::HashMap> ShaderReservedDescriptorSetMap = {}; //> - Core::Containers::HashMap ShaderReservedDescriptorSetLayoutMap = {}; // - Core::Containers::HashMap> ShaderReservedLayoutBindingSpecificationMap = {}; - std::set WriteBindlessDescriptorSetRequests = {}; - std::unordered_set ShaderReservedBindingSets = {}; - Rendering::Textures::TextureHandleManager GlobalTextures = {}; - Helpers::HandleManager Image2DBufferManager = {}; - Helpers::ThreadSafeQueue TextureHandleToUpdates = {}; - Helpers::ThreadSafeQueue TextureHandleToDispose = {}; - Helpers::ThreadSafeQueue AsyncGPUOperations = {}; - Helpers::HandleManager ShaderManager = {}; - Helpers::HandleManager VertexBufferSetManager = {}; - Helpers::HandleManager StorageBufferSetManager = {}; - Helpers::HandleManager IndirectBufferSetManager = {}; - Helpers::HandleManager IndexBufferSetManager = {}; - Helpers::HandleManager UniformBufferSetManager = {}; - Helpers::HandleManager DirtyResources = {}; - Helpers::HandleManager DirtyBuffers = {}; - Helpers::HandleManager DirtyBufferImages = {}; - std::atomic_bool RunningDirtyCollector = {}; - std::mutex Mutex = {}; - Windows::CoreWindow* CurrentWindow = nullptr; - ZEngine::Core::Memory::ArenaAllocator* Arena = nullptr; - AsyncResourceLoaderPtr AsyncResLoader = nullptr; - - void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, Windows::CoreWindow* const window, uint32_t worker_thread_count); - void Deinitialize(); - void Dispose(); - void QueueSubmit(CommandBuffer* const command_buffer, Rendering::Primitives::Semaphore* const signal_semaphore, uint32_t wait_flag, uint64_t signal_value, uint64_t wait_value, Rendering::Primitives::Semaphore* const wait_timeline); - bool QueueSubmit(const VkPipelineStageFlags wait_stage_flag, CommandBuffer* const command_buffer, Rendering::Primitives::Semaphore* const signal_semaphore = nullptr, Rendering::Primitives::Fence* const fence = nullptr); - void EnqueueAsyncGPUOperation(const AsyncGPUOperationHandle& handle); - void EnqueueForDeletion(Rendering::DeviceResourceType resource_type, void* const resource_handle); - void EnqueueForDeletion(Rendering::DeviceResourceType resource_type, DirtyResource resource); - void EnqueueBufferForDeletion(BufferView& buffer); - void EnqueueBufferImageForDeletion(BufferImage& buffer); - QueueView GetQueue(Rendering::QueueType type); - void QueueWait(Rendering::QueueType type); - void QueueWaitAll(); - void MapAndCopyToMemory(BufferView& buffer, size_t data_size, const void* data); - BufferView CreateBuffer(VkDeviceSize byte_size, VkBufferUsageFlags buffer_usage, VmaAllocationCreateFlags vma_create_flags = 0); - VkPipelineStageFlags CopyBuffer(CommandBuffer* const command_buffer, const BufferView& source, const BufferView& destination, VkDeviceSize byte_size, VkDeviceSize src_buffer_offset = 0u, VkDeviceSize dst_buffer_offset = 0u); + bool HasSeperateTransfertQueueFamily = false; + bool PhysicalDeviceSupportSampledImageBindless = false; + bool PhysicalDeviceSupportStorageBufferBindless = false; + bool PhysicalDeviceSupportTimelineSemaphore = false; + const char* ApplicationName = "Tetragrama"; + const char* EngineName = "ZEngine"; + uint32_t WorkerThreadCount = 1; + uint32_t GraphicFamilyIndex = std::numeric_limits::max(); + uint32_t TransferFamilyIndex = std::numeric_limits::max(); + + uint32_t WriteDescriptorSetIndex = 0; + uint32_t MaxGlobalTexture = 600; + VkInstance Instance = VK_NULL_HANDLE; + VkSurfaceKHR Surface = VK_NULL_HANDLE; + VkSurfaceFormatKHR SurfaceFormat = {}; + VkPresentModeKHR PresentMode = {}; + VkPhysicalDeviceProperties2 PhysicalDeviceProperties = {}; + VkPhysicalDeviceVulkan12Properties PhysicalDeviceVulkan12Properties = {}; + VkDevice LogicalDevice = VK_NULL_HANDLE; + VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE; + VkPhysicalDeviceFeatures2 PhysicalDeviceFeature = {}; + VkPhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties = {}; + VkSampler GlobalLinearWrapSampler = VK_NULL_HANDLE; + VkDescriptorPool GlobalDescriptorPoolHandle = VK_NULL_HANDLE; + VmaAllocator VmaAllocatorValue = nullptr; + VkDescriptorImageInfo GlobalLinearWrapSamplerImageInfo = {}; + CommandBufferManagerPtr CommandBufferMgr = {}; + DeviceSwapchainPtr SwapchainPtr = {}; + Core::Containers::Array DefaultDepthFormats = {}; + + Core::Containers::UnorderedHashMap> ShaderCaches = {}; + Core::Containers::UnorderedHashMap> ShaderReservedDescriptorSetMap = {}; //> + Core::Containers::UnorderedHashMap ShaderReservedDescriptorSetLayoutMap = {}; // + Core::Containers::UnorderedHashMap> ShaderReservedLayoutBindingSpecificationMap = {}; + std::set WriteBindlessDescriptorSetRequests = {}; + std::unordered_set ShaderReservedBindingSets = {}; + Rendering::Textures::TextureHandleManager GlobalTextures = {}; + Helpers::HandleManager Image2DBufferManager = {}; + Helpers::ThreadSafeQueue TextureHandleToUpdates = {}; + Helpers::ThreadSafeQueue TextureHandleToDispose = {}; + Helpers::ThreadSafeQueue AsyncGPUOperations = {}; + Helpers::HandleManager ShaderManager = {}; + Helpers::HandleManager VertexBufferSetManager = {}; + Helpers::HandleManager StorageBufferSetManager = {}; + Helpers::HandleManager IndirectBufferSetManager = {}; + Helpers::HandleManager IndexBufferSetManager = {}; + Helpers::HandleManager UniformBufferSetManager = {}; + Helpers::HandleManager DirtyResources = {}; + Helpers::HandleManager DirtyBuffers = {}; + Helpers::HandleManager DirtyBufferImages = {}; + std::atomic_bool RunningDirtyCollector = {}; + std::mutex Mutex = {}; + Windows::CoreWindow* CurrentWindow = nullptr; + ZEngine::Core::Memory::ArenaAllocator* Arena = nullptr; + AsyncResourceLoaderPtr AsyncResLoader = nullptr; + + void Initialize(ZEngine::Core::Memory::ArenaAllocator* arena, Windows::CoreWindow* const window, uint32_t worker_thread_count); + void Deinitialize(); + void Dispose(); + void QueueSubmit(CommandBuffer* const command_buffer, Rendering::Primitives::Semaphore* const signal_semaphore, uint32_t wait_flag, uint64_t signal_value, uint64_t wait_value, Rendering::Primitives::Semaphore* const wait_timeline); + bool QueueSubmit(const VkPipelineStageFlags wait_stage_flag, CommandBuffer* const command_buffer, Rendering::Primitives::Semaphore* const signal_semaphore = nullptr, Rendering::Primitives::Fence* const fence = nullptr); + void EnqueueAsyncGPUOperation(const AsyncGPUOperationHandle& handle); + void EnqueueForDeletion(Rendering::DeviceResourceType resource_type, void* const resource_handle); + void EnqueueForDeletion(Rendering::DeviceResourceType resource_type, DirtyResource resource); + void EnqueueBufferForDeletion(BufferView& buffer); + void EnqueueBufferImageForDeletion(BufferImage& buffer); + QueueView GetQueue(Rendering::QueueType type); + void QueueWait(Rendering::QueueType type); + void QueueWaitAll(); + void MapAndCopyToMemory(BufferView& buffer, size_t data_size, const void* data); + BufferView CreateBuffer(VkDeviceSize byte_size, VkBufferUsageFlags buffer_usage, VmaAllocationCreateFlags vma_create_flags = 0); + VkPipelineStageFlags CopyBuffer(CommandBuffer* const command_buffer, const BufferView& source, const BufferView& destination, VkDeviceSize byte_size, VkDeviceSize src_buffer_offset = 0u, VkDeviceSize dst_buffer_offset = 0u); BufferImage CreateImage(uint32_t width, uint32_t height, VkImageType image_type, VkImageViewType image_view_type, VkFormat image_format, VkImageTiling image_tiling, VkImageLayout image_initial_layout, VkImageUsageFlags image_usage, VkSharingMode image_sharing_mode, VkSampleCountFlagBits image_sample_count, VkMemoryPropertyFlags requested_properties, VkImageAspectFlagBits image_aspect_flag, uint32_t layer_count = 1U, VkImageCreateFlags image_create_flag_bit = 0); VkFormat FindSupportedFormat(Core::Containers::ArrayView format_collection, VkImageTiling image_tiling, VkFormatFeatureFlags feature_flags); VkFormat FindDepthFormat(); @@ -680,15 +680,15 @@ namespace ZEngine::Hardwares Rendering::Renderers::RenderPasses::RenderPass* CreateRenderPass(const Rendering::Specifications::RenderPassSpecification& spec); private: - VulkanLayer m_layer = {}; - Core::Containers::HashMap m_queue_map = {}; - VkDebugUtilsMessengerEXT m_debug_messenger{VK_NULL_HANDLE}; - PFN_vkCreateDebugUtilsMessengerEXT __createDebugMessengerPtr{VK_NULL_HANDLE}; - PFN_vkDestroyDebugUtilsMessengerEXT __destroyDebugMessengerPtr{VK_NULL_HANDLE}; - void __cleanupDirtyResource(); - void __cleanupBufferDirtyResource(); - void __cleanupBufferImageDirtyResource(); - static VKAPI_ATTR VkBool32 VKAPI_CALL __debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); + VulkanLayer m_layer = {}; + Core::Containers::UnorderedHashMap m_queue_map = {}; + VkDebugUtilsMessengerEXT m_debug_messenger{VK_NULL_HANDLE}; + PFN_vkCreateDebugUtilsMessengerEXT __createDebugMessengerPtr{VK_NULL_HANDLE}; + PFN_vkDestroyDebugUtilsMessengerEXT __destroyDebugMessengerPtr{VK_NULL_HANDLE}; + void __cleanupDirtyResource(); + void __cleanupBufferDirtyResource(); + void __cleanupBufferImageDirtyResource(); + static VKAPI_ATTR VkBool32 VKAPI_CALL __debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); }; ZDEFINE_PTR(VulkanDevice); diff --git a/ZEngine/ZEngine/Helpers/SerializerCommonHelper.h b/ZEngine/ZEngine/Helpers/SerializerCommonHelper.h index b750b13b..7b7256a5 100644 --- a/ZEngine/ZEngine/Helpers/SerializerCommonHelper.h +++ b/ZEngine/ZEngine/Helpers/SerializerCommonHelper.h @@ -1,7 +1,7 @@ #pragma once #include -#include #include +#include #include #include #include @@ -66,7 +66,7 @@ namespace ZEngine::Helpers } template - static void WriteBinaryHashMap(std::ostream& writer, ZEngine::Core::Containers::HashMap& map) + static void WriteBinaryHashMap(std::ostream& writer, ZEngine::Core::Containers::UnorderedHashMap& map) { size_t size = map.size(); WriteBinary(writer, size); @@ -134,7 +134,7 @@ namespace ZEngine::Helpers } template - static void ReadHashMap(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, ZEngine::Core::Containers::HashMap& map) + static void ReadHashMap(ZEngine::Core::Memory::ArenaAllocator* arena, std::istream& in, ZEngine::Core::Containers::UnorderedHashMap& map) { size_t size = 0; ReadBinary(in, size); diff --git a/ZEngine/ZEngine/Importers/AssetTypes.h b/ZEngine/ZEngine/Importers/AssetTypes.h index 49e7c9f8..64aa8828 100644 --- a/ZEngine/ZEngine/Importers/AssetTypes.h +++ b/ZEngine/ZEngine/Importers/AssetTypes.h @@ -1,7 +1,7 @@ #pragma once #include -#include #include +#include #include #include #include @@ -65,16 +65,16 @@ namespace ZEngine::Importers struct AssetNodeHierarchy { - uuids::uuid NodeHierarchyUUID = {}; - uuids::uuid MeshUUID = {}; - Core::Containers::Array Hierarchies = {}; - Core::Containers::Array LocalTransforms = {}; - Core::Containers::Array GlobalTransforms = {}; - Core::Containers::Array Names = {}; - Core::Containers::Array MaterialNames = {}; - Core::Containers::HashMap NodeNames = {}; - Core::Containers::HashMap NodeMeshes = {}; - Core::Containers::HashMap NodeMaterials = {}; + uuids::uuid NodeHierarchyUUID = {}; + uuids::uuid MeshUUID = {}; + Core::Containers::Array Hierarchies = {}; + Core::Containers::Array LocalTransforms = {}; + Core::Containers::Array GlobalTransforms = {}; + Core::Containers::Array Names = {}; + Core::Containers::Array MaterialNames = {}; + Core::Containers::UnorderedHashMap NodeNames = {}; + Core::Containers::UnorderedHashMap NodeMeshes = {}; + Core::Containers::UnorderedHashMap NodeMaterials = {}; }; struct AssetNodeRef diff --git a/ZEngine/ZEngine/Logging/Logger.cpp b/ZEngine/ZEngine/Logging/Logger.cpp index cbbecf15..e770cf24 100644 --- a/ZEngine/ZEngine/Logging/Logger.cpp +++ b/ZEngine/ZEngine/Logging/Logger.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -13,13 +13,13 @@ using namespace ZEngine::Core::Memory; namespace ZEngine::Logging { - static std::atomic_uint32_t g_cookie = 0; - static spdlog::sink_ptr s_sink = nullptr; - static std::recursive_mutex s_mutex = {}; - static ZEngine::Core::Containers::Array> s_logger_collection = {}; - static ZEngine::Core::Containers::HashMap s_log_event_handlers = {}; + static std::atomic_uint32_t g_cookie = 0; + static spdlog::sink_ptr s_sink = nullptr; + static std::recursive_mutex s_mutex = {}; + static ZEngine::Core::Containers::Array> s_logger_collection = {}; + static ZEngine::Core::Containers::UnorderedHashMap s_log_event_handlers = {}; - void Logger::Initialize(void* arena, const LoggerConfiguration& configuration) + void Logger::Initialize(void* arena, const LoggerConfiguration& configuration) { s_logger_collection.init(reinterpret_cast(arena), 1); s_log_event_handlers.init(reinterpret_cast(arena), 3); diff --git a/ZEngine/ZEngine/Managers/AssetManager.h b/ZEngine/ZEngine/Managers/AssetManager.h index 685c85c1..a4ac7433 100644 --- a/ZEngine/ZEngine/Managers/AssetManager.h +++ b/ZEngine/ZEngine/Managers/AssetManager.h @@ -1,7 +1,7 @@ #pragma once #include -#include #include +#include #include #include #include @@ -21,64 +21,64 @@ namespace ZEngine::Managers struct AssetManager { - using AssetHandle = uint32_t; + using AssetHandle = uint32_t; - Core::Memory::ArenaAllocator Arena = {}; - Core::Memory::ArenaAllocator ThreadLocalArena = {}; + Core::Memory::ArenaAllocator Arena = {}; + Core::Memory::ArenaAllocator ThreadLocalArena = {}; - cstring CurrentWorkingSpacePath = ""; + cstring CurrentWorkingSpacePath = ""; - std::atomic_bool IsLoading = false; - std::atomic_bool RequestShutdown = false; + std::atomic_bool IsLoading = false; + std::atomic_bool RequestShutdown = false; - Core::Containers::Array NodeHierarchies = {}; - Core::Containers::Array Meshes = {}; - Core::Containers::Array Materials = {}; - Core::Containers::Array Textures = {}; + Core::Containers::Array NodeHierarchies = {}; + Core::Containers::Array Meshes = {}; + Core::Containers::Array Materials = {}; + Core::Containers::Array Textures = {}; - Core::Containers::Array GPUMeshMaterials = {}; + Core::Containers::Array GPUMeshMaterials = {}; - Core::Containers::HashMap UUIDToHandle = {}; - Core::Containers::HashMap HandleToUUID = {}; - Core::Containers::HashMap MeshToNodeHierarchy = {}; - Core::Containers::HashMap NodeHierarchyToMesh = {}; + Core::Containers::UnorderedHashMap UUIDToHandle = {}; + Core::Containers::UnorderedHashMap HandleToUUID = {}; + Core::Containers::UnorderedHashMap MeshToNodeHierarchy = {}; + Core::Containers::UnorderedHashMap NodeHierarchyToMesh = {}; - Core::Containers::HashMap UUIDToTextureHandle = {}; + Core::Containers::UnorderedHashMap UUIDToTextureHandle = {}; - Hardwares::StorageBufferSetHandle MaterialBufferHandle = {}; + Hardwares::StorageBufferSetHandle MaterialBufferHandle = {}; - std::mutex Mut; - std::condition_variable Cond; - Helpers::ThreadSafeQueue PendingAssetFiles = {}; + std::mutex Mut; + std::condition_variable Cond; + Helpers::ThreadSafeQueue PendingAssetFiles = {}; - Helpers::ThreadSafeQueue PendingAssetMeshes = {}; - Helpers::ThreadSafeQueue PendingAssetNodeHierarchies = {}; - Helpers::ThreadSafeQueue PendingAssetMaterials = {}; - Helpers::ThreadSafeQueue> PendingAssetTextures = {}; + Helpers::ThreadSafeQueue PendingAssetMeshes = {}; + Helpers::ThreadSafeQueue PendingAssetNodeHierarchies = {}; + Helpers::ThreadSafeQueue PendingAssetMaterials = {}; + Helpers::ThreadSafeQueue> PendingAssetTextures = {}; - Hardwares::VulkanDevice* Device = nullptr; + Hardwares::VulkanDevice* Device = nullptr; - Importers::AssetMesh* GetMeshAsset(const uuids::uuid& id); - Importers::AssetNodeHierarchy* GetMeshNodeHierarchy(const uuids::uuid& id); - AssetHandle GetMeshNodeHierarchyHandle(const uuids::uuid& id); - AssetHandle GetMaterialHandleFromUUID(const uuids::uuid& material_uuid); + Importers::AssetMesh* GetMeshAsset(const uuids::uuid& id); + Importers::AssetNodeHierarchy* GetMeshNodeHierarchy(const uuids::uuid& id); + AssetHandle GetMeshNodeHierarchyHandle(const uuids::uuid& id); + AssetHandle GetMaterialHandleFromUUID(const uuids::uuid& material_uuid); - Importers::AssetTexture* LoadTextureFileAsAsset(cstring file, bool absolute); + Importers::AssetTexture* LoadTextureFileAsAsset(cstring file, bool absolute); - static AssetManager* Instance(); + static AssetManager* Instance(); - static AssetHandle CreateHandle(uint32_t, AssetType); - static uint32_t ReadAssetHandleIndex(AssetHandle); - static AssetType ReadAssetHandleType(AssetHandle); + static AssetHandle CreateHandle(uint32_t, AssetType); + static uint32_t ReadAssetHandleIndex(AssetHandle); + static AssetType ReadAssetHandleType(AssetHandle); - static void Initialize(Core::Memory::ArenaAllocator* arena, Hardwares::VulkanDevice* device, cstring working_space_path); - static void Run(); - static void Shutdown(); + static void Initialize(Core::Memory::ArenaAllocator* arena, Hardwares::VulkanDevice* device, cstring working_space_path); + static void Run(); + static void Shutdown(); - static bool IsLoadingAsset(); - static AssetHandle RegisterAsset(AssetType type, const uuids::uuid& uid, uint32_t asset_id); + static bool IsLoadingAsset(); + static AssetHandle RegisterAsset(AssetType type, const uuids::uuid& uid, uint32_t asset_id); - static void LoadAssetFile(const Importers::AssetImporterOutput& file); + static void LoadAssetFile(const Importers::AssetImporterOutput& file); template static T* GetAsset(K key) diff --git a/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.cpp b/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.cpp index 8b10ed64..d7d8aec5 100644 --- a/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.cpp +++ b/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.cpp @@ -31,7 +31,7 @@ namespace ZEngine::Rendering::Renderers void RenderGraph::Setup() { - for (auto [name, _] : NodeMap) // Todo HashMap needs to support for (auto& [key, val]) {....} + for (auto [name, _] : NodeMap) // Todo UnorderedHashMap needs to support for (auto& [key, val]) {....} { NodeMap[name].EdgeNodes.init(Device->Arena, 5); NodeMap[name].CallbackPass->Setup(Device, name, ResourceBuilder, ResourceInspector); @@ -61,11 +61,11 @@ namespace ZEngine::Rendering::Renderers /* * Topological Sorting */ - auto scratch = ZGetScratch(Device->Arena); + auto scratch = ZGetScratch(Device->Arena); - Array sorted_nodes = {}; - HashMap visited_nodes = {}; - Array stack = {}; + Array sorted_nodes = {}; + UnorderedHashMap visited_nodes = {}; + Array stack = {}; sorted_nodes.init(scratch.Arena, 6); stack.init(scratch.Arena, 6); diff --git a/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.h b/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.h index 8697ff5f..1e784b78 100644 --- a/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.h +++ b/ZEngine/ZEngine/Rendering/Renderers/RenderGraph.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include #include #include @@ -101,26 +101,26 @@ namespace ZEngine::Rendering::Renderers RenderGraph() {} ~RenderGraph() {} - Hardwares::VulkanDevicePtr Device = nullptr; + Hardwares::VulkanDevicePtr Device = nullptr; - Core::Containers::Array SortedNodesMap = {}; - Core::Containers::HashMap NodeMap = {}; - Core::Containers::HashMap ResourceMap = {}; + Core::Containers::Array SortedNodesMap = {}; + Core::Containers::UnorderedHashMap NodeMap = {}; + Core::Containers::UnorderedHashMap ResourceMap = {}; - RenderGraphResourceBuilderPtr ResourceBuilder = nullptr; - RenderGraphResourceInspectorPtr ResourceInspector = nullptr; - RenderPasses::RenderPassBuilder* RenderPassBuilder = nullptr; + RenderGraphResourceBuilderPtr ResourceBuilder = nullptr; + RenderGraphResourceInspectorPtr ResourceInspector = nullptr; + RenderPasses::RenderPassBuilder* RenderPassBuilder = nullptr; - Scenes::SceneDataPtr SceneData = nullptr; + Scenes::SceneDataPtr SceneData = nullptr; - void Initialize(Hardwares::VulkanDevicePtr device, Scenes::SceneDataPtr data = nullptr); + void Initialize(Hardwares::VulkanDevicePtr device, Scenes::SceneDataPtr data = nullptr); - void Setup(); - void Compile(); - void Execute(Hardwares::CommandBufferPtr const command_buffer); - void Resize(uint32_t width, uint32_t height); - void Dispose(); - void AddCallbackPass(cstring pass_name, IRenderGraphCallbackPass* const pass_callback, bool enabled = true); + void Setup(); + void Compile(); + void Execute(Hardwares::CommandBufferPtr const command_buffer); + void Resize(uint32_t width, uint32_t height); + void Dispose(); + void AddCallbackPass(cstring pass_name, IRenderGraphCallbackPass* const pass_callback, bool enabled = true); }; struct RenderGraphResourceInspector diff --git a/ZEngine/ZEngine/Rendering/Renderers/RendererPasses.cpp b/ZEngine/ZEngine/Rendering/Renderers/RendererPasses.cpp index c96e4771..df55817e 100644 --- a/ZEngine/ZEngine/Rendering/Renderers/RendererPasses.cpp +++ b/ZEngine/ZEngine/Rendering/Renderers/RendererPasses.cpp @@ -76,7 +76,9 @@ namespace ZEngine::Rendering::Renderers .EnablePipelineDepthTest(true) .UseShader("initial") .Detach(); - *output_pass = device->CreateRenderPass(pass_spec); + // clang-format off + *output_pass = device->CreateRenderPass(pass_spec); + // clang-format on (*output_pass)->Bake(); } } @@ -147,7 +149,9 @@ namespace ZEngine::Rendering::Renderers .EnablePipelineDepthTest(true) .UseShader("initial") .Detach(); - *output_pass = device->CreateRenderPass(pass_spec); + // clang-format off + *output_pass = device->CreateRenderPass(pass_spec); + // clang-format on (*output_pass)->Bake(); } } diff --git a/ZEngine/ZEngine/Rendering/Scenes/GraphicScene.h b/ZEngine/ZEngine/Rendering/Scenes/GraphicScene.h index be0198ba..f1c65a14 100644 --- a/ZEngine/ZEngine/Rendering/Scenes/GraphicScene.h +++ b/ZEngine/ZEngine/Rendering/Scenes/GraphicScene.h @@ -25,21 +25,21 @@ namespace ZEngine::Rendering::Scenes struct RenderScene { - std::atomic_bool MeshAllocationDirty[3] = {false, false, false}; - std::atomic_bool TransformBufferDirty[3] = {false, false, false}; + std::atomic_bool MeshAllocationDirty[3] = {false, false, false}; + std::atomic_bool TransformBufferDirty[3] = {false, false, false}; - uint32_t CurrentTransformOffset = 0; - uint32_t CurrentVertexOffset = 0; - uint32_t CurrentIndexOffset = 0; + uint32_t CurrentTransformOffset = 0; + uint32_t CurrentVertexOffset = 0; + uint32_t CurrentIndexOffset = 0; - Core::Containers::Array Hierarchies = {}; - Core::Containers::Array LocalTransforms = {}; - Core::Containers::Array GlobalTransforms = {}; + Core::Containers::Array Hierarchies = {}; + Core::Containers::Array LocalTransforms = {}; + Core::Containers::Array GlobalTransforms = {}; - Core::Containers::Array Vertices = {}; - Core::Containers::Array Indices = {}; - Core::Containers::HashMap MeshAllocations = {}; - Core::Containers::HashMap NodeSubMeshesAllocations = {}; + Core::Containers::Array Vertices = {}; + Core::Containers::Array Indices = {}; + Core::Containers::UnorderedHashMap MeshAllocations = {}; + Core::Containers::UnorderedHashMap NodeSubMeshesAllocations = {}; }; ZDEFINE_PTR(RenderScene); diff --git a/ZEngine/ZEngine/Rendering/Shaders/Shader.h b/ZEngine/ZEngine/Rendering/Shaders/Shader.h index c12c3aec..9ae43b69 100644 --- a/ZEngine/ZEngine/Rendering/Shaders/Shader.h +++ b/ZEngine/ZEngine/Rendering/Shaders/Shader.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include #include @@ -13,23 +13,23 @@ namespace ZEngine::Rendering::Shaders Shader(); ~Shader(); - void Initialize(Hardwares::VulkanDevice* device, const Specifications::ShaderSpecification& spec); - void Dispose(); - Specifications::LayoutBindingSpecification GetLayoutBindingSpecification(cstring name); - - VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; - Specifications::ShaderSpecification m_specification = {}; - Core::Memory::ArenaAllocator LocalArena = {}; - - Core::Containers::Array PushConstantSpecifications = {}; - Core::Containers::Array ShaderCreateInfos = {}; - Core::Containers::Array ShaderModules = {}; - Core::Containers::Array SetLayouts = {}; - Core::Containers::Array LayoutBindingSpecifications = {}; - Core::Containers::Array PushConstants = {}; - Core::Containers::HashMap> DescriptorSetMap = {}; //> - Core::Containers::HashMap InternalDescriptorSetLayoutMap = {}; // - Core::Containers::HashMap> LayoutBindingSpecificationMap = {}; + void Initialize(Hardwares::VulkanDevice* device, const Specifications::ShaderSpecification& spec); + void Dispose(); + Specifications::LayoutBindingSpecification GetLayoutBindingSpecification(cstring name); + + VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; + Specifications::ShaderSpecification m_specification = {}; + Core::Memory::ArenaAllocator LocalArena = {}; + + Core::Containers::Array PushConstantSpecifications = {}; + Core::Containers::Array ShaderCreateInfos = {}; + Core::Containers::Array ShaderModules = {}; + Core::Containers::Array SetLayouts = {}; + Core::Containers::Array LayoutBindingSpecifications = {}; + Core::Containers::Array PushConstants = {}; + Core::Containers::UnorderedHashMap> DescriptorSetMap = {}; //> + Core::Containers::UnorderedHashMap InternalDescriptorSetLayoutMap = {}; // + Core::Containers::UnorderedHashMap> LayoutBindingSpecificationMap = {}; private: void CreateModule(); diff --git a/ZEngine/ZEngine/Rendering/Specifications/AttachmentSpecification.h b/ZEngine/ZEngine/Rendering/Specifications/AttachmentSpecification.h index 7f4b55a2..6060bf4d 100644 --- a/ZEngine/ZEngine/Rendering/Specifications/AttachmentSpecification.h +++ b/ZEngine/ZEngine/Rendering/Specifications/AttachmentSpecification.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include @@ -26,11 +26,11 @@ namespace ZEngine::Rendering::Specifications struct AttachmentSpecification { - PipelineBindPoint BindPoint; - Core::Containers::HashMap ColorsMap; - Core::Containers::HashMap DependenciesMap; - Core::Containers::Array ColorAttachements; - Core::Containers::Array SubpassSpecifications; - Core::Containers::Array SubpassDependencies; + PipelineBindPoint BindPoint; + Core::Containers::UnorderedHashMap ColorsMap; + Core::Containers::UnorderedHashMap DependenciesMap; + Core::Containers::Array ColorAttachements; + Core::Containers::Array SubpassSpecifications; + Core::Containers::Array SubpassDependencies; }; } // namespace ZEngine::Rendering::Specifications diff --git a/ZEngine/ZEngine/Rendering/Specifications/RenderPassSpecification.h b/ZEngine/ZEngine/Rendering/Specifications/RenderPassSpecification.h index b1e859f9..cdf823ba 100644 --- a/ZEngine/ZEngine/Rendering/Specifications/RenderPassSpecification.h +++ b/ZEngine/ZEngine/Rendering/Specifications/RenderPassSpecification.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include #include @@ -9,12 +9,12 @@ namespace ZEngine::Rendering::Specifications { struct RenderPassSpecification { - const char* DebugName = {}; - bool SwapchainAsRenderTarget = false; - Specifications::GraphicRendererPipelineSpecification PipelineSpecification = {}; - Core::Containers::Array Inputs = {}; - Core::Containers::HashMap InputTextures = {}; - Core::Containers::Array Outputs = {}; - Core::Containers::Array ExternalOutputs = {}; + const char* DebugName = {}; + bool SwapchainAsRenderTarget = false; + Specifications::GraphicRendererPipelineSpecification PipelineSpecification = {}; + Core::Containers::Array Inputs = {}; + Core::Containers::UnorderedHashMap InputTextures = {}; + Core::Containers::Array Outputs = {}; + Core::Containers::Array ExternalOutputs = {}; }; } // namespace ZEngine::Rendering::Specifications diff --git a/ZEngine/tests/Containers/hashset_test.cpp b/ZEngine/tests/Containers/hashset_test.cpp new file mode 100644 index 00000000..3c580f01 --- /dev/null +++ b/ZEngine/tests/Containers/hashset_test.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +using namespace ZEngine::Core::Containers; +using namespace ZEngine::Core::Memory; + +class UnorderedHashSetTest : public ::testing::Test +{ +protected: + void SetUp() override + { + allocator.Initialize(2000); + } + void TearDown() override + { + allocator.Shutdown(); + } + ArenaAllocator allocator; +}; + +TEST_F(UnorderedHashSetTest, InitialState) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.capacity(), 10); + EXPECT_TRUE(set.empty()); +} + +TEST_F(UnorderedHashSetTest, InsertAndContains) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + EXPECT_TRUE(set.contains(1)); + EXPECT_TRUE(set.contains(2)); + EXPECT_FALSE(set.contains(3)); +} + +TEST_F(UnorderedHashSetTest, InsertDuplicateDoesNotGrow) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(1); + EXPECT_EQ(set.size(), 1); +} + +TEST_F(UnorderedHashSetTest, Remove) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + EXPECT_EQ(set.size(), 2); + set.remove(1); + EXPECT_EQ(set.size(), 1); + EXPECT_FALSE(set.contains(1)); + EXPECT_TRUE(set.contains(2)); +} + +TEST_F(UnorderedHashSetTest, RemoveNonExistentIsNoOp) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(1); + set.remove(99); + EXPECT_EQ(set.size(), 1); + EXPECT_TRUE(set.contains(1)); +} + +TEST_F(UnorderedHashSetTest, Find) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(42); + const int* found = set.find(42); + ASSERT_NE(found, nullptr); + EXPECT_EQ(*found, 42); + EXPECT_EQ(set.find(99), nullptr); +} + +TEST_F(UnorderedHashSetTest, Clear) +{ + UnorderedHashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + set.insert(3); + EXPECT_EQ(set.size(), 3); + set.clear(); + EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.empty()); + EXPECT_FALSE(set.contains(1)); + EXPECT_FALSE(set.contains(2)); + EXPECT_FALSE(set.contains(3)); +} + +TEST_F(UnorderedHashSetTest, Resize) +{ + UnorderedHashSet set; + set.init(&allocator, 2); + for (int i = 0; i < 10; ++i) + { + set.insert(i); + } + EXPECT_EQ(set.size(), 10); + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(set.contains(i)); + } + EXPECT_GT(set.capacity(), 2); +} + +TEST_F(UnorderedHashSetTest, CollisionHandling) +{ + UnorderedHashSet set; + set.init(&allocator, 2); + set.insert(1); + set.insert(3); + set.insert(5); + EXPECT_TRUE(set.contains(1)); + EXPECT_TRUE(set.contains(3)); + EXPECT_TRUE(set.contains(5)); +} + +TEST_F(UnorderedHashSetTest, Iteration) +{ + UnorderedHashSet set; + set.init(&allocator, 8); + set.insert(10); + set.insert(20); + set.insert(30); + + std::unordered_set expected = {10, 20, 30}; + for (const int& key : set) + { + auto it = expected.find(key); + ASSERT_NE(it, expected.end()); + expected.erase(it); + } + EXPECT_TRUE(expected.empty()); +} + +TEST_F(UnorderedHashSetTest, StringKeys) +{ + UnorderedHashSet set; + set.init(&allocator, 8); + + String a, b, c; + a.init(&allocator, "alpha"); + b.init(&allocator, "beta"); + c.init(&allocator, "gamma"); + + set.insert(a); + set.insert(b); + + EXPECT_TRUE(set.contains(a)); + EXPECT_TRUE(set.contains(b)); + EXPECT_FALSE(set.contains(c)); + + set.remove(a); + EXPECT_FALSE(set.contains(a)); + EXPECT_EQ(set.size(), 1); +} diff --git a/ZEngine/tests/Containers/ordered_hashmap_test.cpp b/ZEngine/tests/Containers/ordered_hashmap_test.cpp new file mode 100644 index 00000000..81c0e5b1 --- /dev/null +++ b/ZEngine/tests/Containers/ordered_hashmap_test.cpp @@ -0,0 +1,333 @@ +#include +#include +#include +#include +#include + +using namespace ZEngine::Core::Containers; +using namespace ZEngine::Core::Memory; + +class OrderedHashMapTest : public ::testing::Test +{ +protected: + void SetUp() override + { + allocator.Initialize(2000); + } + void TearDown() override + { + allocator.Shutdown(); + } + ArenaAllocator allocator; +}; + +TEST_F(OrderedHashMapTest, InitialState) +{ + HashMap map; + map.init(&allocator, 10); + EXPECT_EQ(map.size(), 0); + EXPECT_EQ(map.capacity(), 10); + EXPECT_TRUE(map.empty()); +} + +TEST_F(OrderedHashMapTest, Contains) +{ + HashMap map; + map.init(&allocator, 10); + map.insert(1, 10); + EXPECT_TRUE(map.contains(1)); + EXPECT_FALSE(map.contains(2)); +} + +TEST_F(OrderedHashMapTest, BracketOperator) +{ + HashMap map; + map.init(&allocator, 10); + map[1] = 10; + EXPECT_EQ(map[1], 10); + + map[1] = 20; + EXPECT_EQ(map[1], 20); + + EXPECT_EQ(map[2], 0); + EXPECT_TRUE(map.contains(2)); +} + +TEST_F(OrderedHashMapTest, Remove) +{ + HashMap map; + map.init(&allocator, 10); + map.insert(1, 10); + map.insert(2, 20); + EXPECT_EQ(map.size(), 2); + map.remove(1); + EXPECT_EQ(map.size(), 1); + EXPECT_FALSE(map.contains(1)); + EXPECT_TRUE(map.contains(2)); +} + +TEST_F(OrderedHashMapTest, Find) +{ + HashMap map; + map.init(&allocator, 10); + map.insert(1, 10); + int* value = map.find(1); + ASSERT_NE(value, nullptr); + EXPECT_EQ(*value, 10); + int* non_existent = map.find(2); + EXPECT_EQ(non_existent, nullptr); +} + +TEST_F(OrderedHashMapTest, Clear) +{ + HashMap map; + map.init(&allocator, 10); + + map.insert(1, 10); + map.insert(2, 20); + map.insert(3, 30); + + EXPECT_EQ(map.size(), 3); + + map.clear(); + + EXPECT_EQ(map.size(), 0); + EXPECT_TRUE(map.empty()); + + EXPECT_FALSE(map.contains(1)); + EXPECT_FALSE(map.contains(2)); + EXPECT_FALSE(map.contains(3)); +} + +TEST_F(OrderedHashMapTest, Resize) +{ + HashMap map; + map.init(&allocator, 2); + + for (int i = 0; i < 10; ++i) + { + map.insert(i, i * 10); + } + + EXPECT_EQ(map.size(), 10); + + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(map.contains(i)); + EXPECT_EQ(map[i], i * 10); + } + + EXPECT_GT(map.capacity(), 2); +} + +TEST_F(OrderedHashMapTest, OverwriteValue) +{ + HashMap map; + map.init(&allocator, 10); + + String str1; + str1.init(&allocator, "first"); + + String str2; + str2.init(&allocator, "updated"); + + map.insert(1, str1); + EXPECT_STREQ(map[1].c_str(), str1.c_str()); + + map.insert(1, str2); + EXPECT_STREQ(map[1].c_str(), str2.c_str()); +} + +TEST_F(OrderedHashMapTest, CollisionHandling) +{ + HashMap map; + map.init(&allocator, 2); + + map.insert(1, 10); + map.insert(3, 30); + map.insert(5, 50); + + EXPECT_TRUE(map.contains(1)); + EXPECT_TRUE(map.contains(3)); + EXPECT_TRUE(map.contains(5)); + + EXPECT_EQ(map[1], 10); + EXPECT_EQ(map[3], 30); + EXPECT_EQ(map[5], 50); +} + +// --- Insertion-order tests --- + +TEST_F(OrderedHashMapTest, InsertionOrderPreserved) +{ + HashMap map; + map.init(&allocator, 16); + + map.insert(10, 100); + map.insert(20, 200); + map.insert(30, 300); + + std::vector keys; + for (auto [key, value] : map) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 10); + EXPECT_EQ(keys[1], 20); + EXPECT_EQ(keys[2], 30); +} + +TEST_F(OrderedHashMapTest, InsertionOrderAfterRemove) +{ + HashMap map; + map.init(&allocator, 16); + + map.insert(1, 10); + map.insert(2, 20); + map.insert(3, 30); + map.remove(2); + map.insert(4, 40); + + std::vector keys; + for (auto [key, value] : map) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 1); + EXPECT_EQ(keys[1], 3); + EXPECT_EQ(keys[2], 4); +} + +TEST_F(OrderedHashMapTest, InsertionOrderPreservedAfterResize) +{ + HashMap map; + map.init(&allocator, 2); + + for (int i = 0; i < 10; ++i) + map.insert(i, i * 10); + + std::vector keys; + for (auto [key, value] : map) + keys.push_back(key); + + ASSERT_EQ(keys.size(), 10u); + for (int i = 0; i < 10; ++i) + EXPECT_EQ(keys[i], i); +} + +TEST_F(OrderedHashMapTest, UpdateDoesNotChangeOrder) +{ + HashMap map; + map.init(&allocator, 16); + + map.insert(1, 10); + map.insert(2, 20); + map.insert(3, 30); + map.insert(2, 999); + + std::vector keys; + for (auto [key, value] : map) + keys.push_back(key); + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 1); + EXPECT_EQ(keys[1], 2); + EXPECT_EQ(keys[2], 3); + EXPECT_EQ(map[2], 999); +} + +// --- sort_keys() tests --- + +TEST_F(OrderedHashMapTest, SortKeysAscending) +{ + HashMap map; + map.init(&allocator, 16); + map.insert(30, 300); + map.insert(10, 100); + map.insert(20, 200); + + map.sort_keys(); + + std::vector keys; + for (auto [key, value] : map) + keys.push_back(key); + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 10); + EXPECT_EQ(keys[1], 20); + EXPECT_EQ(keys[2], 30); +} + +TEST_F(OrderedHashMapTest, SortKeysPreservesValues) +{ + HashMap map; + map.init(&allocator, 16); + map.insert(3, 300); + map.insert(1, 100); + map.insert(2, 200); + + map.sort_keys(); + + std::vector> entries; + for (auto [key, value] : map) + { + entries.push_back({key, value}); + } + + ASSERT_EQ(entries.size(), 3u); + EXPECT_EQ(entries[0].first, 1); + EXPECT_EQ(entries[0].second, 100); + EXPECT_EQ(entries[1].first, 2); + EXPECT_EQ(entries[1].second, 200); + EXPECT_EQ(entries[2].first, 3); + EXPECT_EQ(entries[2].second, 300); +} + +TEST_F(OrderedHashMapTest, SortKeysDoesNotAffectLookup) +{ + HashMap map; + map.init(&allocator, 16); + map.insert(30, 300); + map.insert(10, 100); + map.insert(20, 200); + + map.sort_keys(); + + EXPECT_EQ(*map.find(10), 100); + EXPECT_EQ(*map.find(20), 200); + EXPECT_EQ(*map.find(30), 300); +} + +TEST_F(OrderedHashMapTest, SortKeysOnEmptyMap) +{ + HashMap map; + map.init(&allocator, 16); + map.sort_keys(); + EXPECT_TRUE(map.empty()); +} + +TEST_F(OrderedHashMapTest, InsertAfterSortAppendsTail) +{ + HashMap map; + map.init(&allocator, 16); + map.insert(30, 300); + map.insert(10, 100); + map.sort_keys(); // order: 10, 30 + + map.insert(20, 200); // appended after sort + + std::vector keys; + for (auto [key, value] : map) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 10); + EXPECT_EQ(keys[1], 30); + EXPECT_EQ(keys[2], 20); +} diff --git a/ZEngine/tests/Containers/ordered_hashset_test.cpp b/ZEngine/tests/Containers/ordered_hashset_test.cpp new file mode 100644 index 00000000..22403790 --- /dev/null +++ b/ZEngine/tests/Containers/ordered_hashset_test.cpp @@ -0,0 +1,287 @@ +#include +#include +#include +#include + +using namespace ZEngine::Core::Containers; +using namespace ZEngine::Core::Memory; + +class OrderedHashSetTest : public ::testing::Test +{ +protected: + void SetUp() override + { + allocator.Initialize(2000); + } + void TearDown() override + { + allocator.Shutdown(); + } + ArenaAllocator allocator; +}; + +TEST_F(OrderedHashSetTest, InitialState) +{ + HashSet set; + set.init(&allocator, 10); + EXPECT_EQ(set.size(), 0); + EXPECT_EQ(set.capacity(), 10); + EXPECT_TRUE(set.empty()); +} + +TEST_F(OrderedHashSetTest, InsertAndContains) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + EXPECT_TRUE(set.contains(1)); + EXPECT_TRUE(set.contains(2)); + EXPECT_FALSE(set.contains(3)); +} + +TEST_F(OrderedHashSetTest, InsertDuplicateDoesNotGrow) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(1); + EXPECT_EQ(set.size(), 1); +} + +TEST_F(OrderedHashSetTest, Remove) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + EXPECT_EQ(set.size(), 2); + set.remove(1); + EXPECT_EQ(set.size(), 1); + EXPECT_FALSE(set.contains(1)); + EXPECT_TRUE(set.contains(2)); +} + +TEST_F(OrderedHashSetTest, RemoveNonExistentIsNoOp) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(1); + set.remove(99); + EXPECT_EQ(set.size(), 1); + EXPECT_TRUE(set.contains(1)); +} + +TEST_F(OrderedHashSetTest, Find) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(42); + const int* found = set.find(42); + ASSERT_NE(found, nullptr); + EXPECT_EQ(*found, 42); + EXPECT_EQ(set.find(99), nullptr); +} + +TEST_F(OrderedHashSetTest, Clear) +{ + HashSet set; + set.init(&allocator, 10); + set.insert(1); + set.insert(2); + set.insert(3); + EXPECT_EQ(set.size(), 3); + set.clear(); + EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.empty()); + EXPECT_FALSE(set.contains(1)); + EXPECT_FALSE(set.contains(2)); + EXPECT_FALSE(set.contains(3)); +} + +TEST_F(OrderedHashSetTest, Resize) +{ + HashSet set; + set.init(&allocator, 2); + for (int i = 0; i < 10; ++i) + { + set.insert(i); + } + EXPECT_EQ(set.size(), 10); + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(set.contains(i)); + } + EXPECT_GT(set.capacity(), 2); +} + +TEST_F(OrderedHashSetTest, CollisionHandling) +{ + HashSet set; + set.init(&allocator, 2); + set.insert(1); + set.insert(3); + set.insert(5); + EXPECT_TRUE(set.contains(1)); + EXPECT_TRUE(set.contains(3)); + EXPECT_TRUE(set.contains(5)); +} + +// --- Insertion-order tests --- + +TEST_F(OrderedHashSetTest, InsertionOrderPreserved) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(30); + set.insert(10); + set.insert(20); + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 30); + EXPECT_EQ(keys[1], 10); + EXPECT_EQ(keys[2], 20); +} + +TEST_F(OrderedHashSetTest, InsertionOrderAfterRemove) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(1); + set.insert(2); + set.insert(3); + set.remove(2); + set.insert(4); + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 1); + EXPECT_EQ(keys[1], 3); + EXPECT_EQ(keys[2], 4); +} + +TEST_F(OrderedHashSetTest, InsertionOrderPreservedAfterResize) +{ + HashSet set; + set.init(&allocator, 2); + for (int i = 0; i < 10; ++i) + { + set.insert(i); + } + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 10u); + for (int i = 0; i < 10; ++i) + { + EXPECT_EQ(keys[i], i); + } +} + +TEST_F(OrderedHashSetTest, DuplicateInsertDoesNotChangeOrder) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(1); + set.insert(2); + set.insert(3); + set.insert(2); + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 1); + EXPECT_EQ(keys[1], 2); + EXPECT_EQ(keys[2], 3); +} + +// --- sort_keys() tests --- + +TEST_F(OrderedHashSetTest, SortKeysAscending) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(30); + set.insert(10); + set.insert(20); + + set.sort_keys(); + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 10); + EXPECT_EQ(keys[1], 20); + EXPECT_EQ(keys[2], 30); +} + +TEST_F(OrderedHashSetTest, SortKeysDoesNotAffectLookup) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(30); + set.insert(10); + set.insert(20); + + set.sort_keys(); + + EXPECT_TRUE(set.contains(10)); + EXPECT_TRUE(set.contains(20)); + EXPECT_TRUE(set.contains(30)); + EXPECT_NE(set.find(10), nullptr); + EXPECT_NE(set.find(20), nullptr); + EXPECT_NE(set.find(30), nullptr); +} + +TEST_F(OrderedHashSetTest, SortKeysOnEmptySet) +{ + HashSet set; + set.init(&allocator, 16); + set.sort_keys(); + EXPECT_TRUE(set.empty()); +} + +TEST_F(OrderedHashSetTest, InsertAfterSortAppendsTail) +{ + HashSet set; + set.init(&allocator, 16); + set.insert(30); + set.insert(10); + set.sort_keys(); + + set.insert(20); + + std::vector keys; + for (const int& key : set) + { + keys.push_back(key); + } + + ASSERT_EQ(keys.size(), 3u); + EXPECT_EQ(keys[0], 10); + EXPECT_EQ(keys[1], 30); + EXPECT_EQ(keys[2], 20); +} diff --git a/ZEngine/tests/Containers/hashmap_test.cpp b/ZEngine/tests/Containers/unordered_hashmap_test.cpp similarity index 91% rename from ZEngine/tests/Containers/hashmap_test.cpp rename to ZEngine/tests/Containers/unordered_hashmap_test.cpp index e6626e5c..48fd22db 100644 --- a/ZEngine/tests/Containers/hashmap_test.cpp +++ b/ZEngine/tests/Containers/unordered_hashmap_test.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include @@ -22,7 +22,7 @@ class HashMapTest : public ::testing::Test TEST_F(HashMapTest, InitialState) { - HashMap array; + UnorderedHashMap array; array.init(&allocator, 10); EXPECT_EQ(array.size(), 0); EXPECT_EQ(array.capacity(), 10); @@ -31,7 +31,7 @@ TEST_F(HashMapTest, InitialState) TEST_F(HashMapTest, Contains) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); map.insert(1, 10); EXPECT_TRUE(map.contains(1)); @@ -40,7 +40,7 @@ TEST_F(HashMapTest, Contains) TEST_F(HashMapTest, BracketOperator) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); map[1] = 10; EXPECT_EQ(map[1], 10); @@ -54,7 +54,7 @@ TEST_F(HashMapTest, BracketOperator) TEST_F(HashMapTest, Remove) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); map.insert(1, 10); map.insert(2, 20); @@ -67,7 +67,7 @@ TEST_F(HashMapTest, Remove) TEST_F(HashMapTest, Find) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); map.insert(1, 10); int* value = map.find(1); @@ -79,7 +79,7 @@ TEST_F(HashMapTest, Find) TEST_F(HashMapTest, Clear) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); // Insert multiple elements @@ -101,7 +101,7 @@ TEST_F(HashMapTest, Clear) TEST_F(HashMapTest, Resize) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 2); for (int i = 0; i < 10; ++i) @@ -122,7 +122,7 @@ TEST_F(HashMapTest, Resize) TEST_F(HashMapTest, OverwriteValue) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 10); String str1; @@ -140,7 +140,7 @@ TEST_F(HashMapTest, OverwriteValue) TEST_F(HashMapTest, CollisionHandling) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 2); map.insert(1, 10); @@ -158,7 +158,7 @@ TEST_F(HashMapTest, CollisionHandling) TEST_F(HashMapTest, ViewIteration) { - HashMap map; + UnorderedHashMap map; map.init(&allocator, 8); map.insert(10, 100); @@ -195,7 +195,7 @@ TEST_F(HashMapTest, UserDefinedStructViewIterations) } }; - HashMap map; + UnorderedHashMap map; map.init(&allocator, 8); String str1;