Forráskód Böngészése

Added HashTable class, which has fixed bucket count and a limited API to optimize for performance.
Use HashTable to store Technique's passes to speed up querying for them.

Lasse Öörni 12 éve
szülő
commit
1f87bcb9f9

+ 182 - 0
Source/Engine/Container/HashTable.h

@@ -0,0 +1,182 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Allocator.h"
+#include "Vector.h"
+
+namespace Urho3D
+{
+
+/// Hash table with fixed bucket count. Does not support iteration. Should only be used when performance is critical, as HashMap is much more user-friendly.
+template <class T, unsigned U> class HashTable
+{
+public:
+    /// Hash table node.
+    struct Node
+    {
+        /// Construct.
+        Node(unsigned hash, const T& value) :
+            hash_(hash),
+            value_(value),
+            down_(0)
+        {
+        }
+        
+        /// Hash value.
+        unsigned hash_;
+        /// Node value.
+        T value_;
+        /// Next node in bucket.
+        Node* down_;
+    };
+    
+    /// Construct empty.
+    HashTable() :
+        allocator_(0)
+    {
+        for (unsigned i = 0; i < U; ++i)
+            ptrs_[i] = 0;
+    }
+    
+    /// Destruct.
+    ~HashTable()
+    {
+        Clear();
+        AllocatorUninitialize(allocator_);
+    }
+    
+    /// Insert by hash value. If value with same hash already exists, it is replaced.
+    void Insert(unsigned hash, const T& value)
+    {
+        unsigned bucket = hash & (U - 1);
+        
+        if (!allocator_)
+            allocator_ = AllocatorInitialize(sizeof(Node));
+        
+        Node* ptr = ptrs_[bucket];
+        while (ptr)
+        {
+            if (ptr->hash_ == hash)
+            {
+                ptr->value_ = value;
+                return;
+            }
+            ptr = ptr->down_;
+        }
+        
+        Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
+        new(newNode) Node(hash, value);
+        
+        ptr = ptrs_[bucket];
+        if (ptr)
+        {
+            ptrs_[bucket] = newNode;
+            newNode->down_ = ptr;
+        }
+        else
+            ptrs_[bucket] = newNode;
+        
+        return;
+    }
+    
+    /// Remove by hash value. Return true if was found and removed.
+    bool Erase(unsigned hash)
+    {
+        unsigned bucket = hash & (U - 1);
+        
+        Node* ptr = ptrs_[bucket];
+        while (ptr)
+        {
+            if (ptr->hash_ == hash)
+            {
+                (ptr)->~Node();
+                AllocatorFree(allocator_, ptr);
+                return true;
+            }
+            else
+                ptr = ptr->down_;
+        }
+        
+        return false;
+    }
+    
+    /// Remove all values.
+    void Clear()
+    {
+        for (unsigned i = 0; i < U; ++i)
+        {
+            Node* ptr = ptrs_[i];
+            while (ptr)
+            {
+                Node* next = ptr->down_;
+                (ptr)->~Node();
+                AllocatorFree(allocator_, ptr);
+                ptr = next;
+            }
+        }
+    }
+    
+    /// Find by hash value. Return pointer if was found or null if not found.
+    T* Find(unsigned hash) const
+    {
+        unsigned bucket = hash & (U - 1);
+        
+        Node* ptr = ptrs_[bucket];
+        while (ptr)
+        {
+            if (ptr->hash_ == hash)
+                return &ptr->value_;
+            else
+                ptr = ptr->down_;
+        }
+        
+        return 0;
+    }
+    
+    /// Return pointers to all values.
+    PODVector<T*> Values() const
+    {
+        PODVector<T*> ret;
+        
+        for (unsigned i = 0; i < U; ++i)
+        {
+            Node* ptr = ptrs_[i];
+            while (ptr)
+            {
+                ret.Push(&ptr->value_);
+                ptr = ptr->down_;
+            }
+        }
+        
+        return ret;
+    }
+    
+private:
+    /// Allocator.
+    AllocatorBlock* allocator_;
+    /// Bucket pointers, fixed size.
+    Node* ptrs_[U];
+};
+
+}

+ 10 - 19
Source/Engine/Graphics/Technique.cpp

@@ -162,6 +162,8 @@ bool Technique::Load(Deserializer& source)
     if (rootElem.HasAttribute("sm3"))
         isSM3_ = rootElem.GetBool("sm3");
     
+    unsigned numPasses = 0;
+    
     XMLElement passElem = rootElem.GetChild("pass");
     while (passElem)
     {
@@ -169,6 +171,7 @@ bool Technique::Load(Deserializer& source)
         {
             StringHash nameHash(passElem.GetAttribute("name"));
             Pass* newPass = CreatePass(nameHash);
+            ++numPasses;
             
             if (passElem.HasAttribute("vs"))
                 newPass->SetVertexShader(passElem.GetAttribute("vs"));
@@ -210,13 +213,8 @@ bool Technique::Load(Deserializer& source)
         passElem = passElem.GetNext("pass");
     }
     
-    // Rehash the pass map to ensure minimum load factor and fast queries
-    passes_.Rehash(NextPowerOfTwo(passes_.Size()));
-    
     // Calculate memory use
-    unsigned memoryUse = sizeof(Technique);
-    memoryUse += sizeof(HashMap<StringHash, SharedPtr<Pass> >) + passes_.Size() * sizeof(Pass);
-    
+    unsigned memoryUse = sizeof(Technique) + numPasses * sizeof(Pass);
     SetMemoryUse(memoryUse);
     return true;
 }
@@ -228,8 +226,10 @@ void Technique::SetIsSM3(bool enable)
 
 void Technique::ReleaseShaders()
 {
-    for (HashMap<StringHash, SharedPtr<Pass> >::Iterator i = passes_.Begin(); i != passes_.End(); ++i)
-        i->second_->ReleaseShaders();
+    PODVector<SharedPtr<Pass>*> allPasses = passes_.Values();
+    
+    for (unsigned i = 0; i < allPasses.Size(); ++i)
+        allPasses[i]->Get()->ReleaseShaders();
 }
 
 Pass* Technique::CreatePass(StringHash type)
@@ -239,23 +239,14 @@ Pass* Technique::CreatePass(StringHash type)
         return oldPass;
     
     SharedPtr<Pass> newPass(new Pass(type));
-    passes_[type] = newPass;
-    
-    // Rehash the pass map to ensure minimum load factor and fast queries
-    passes_.Rehash(NextPowerOfTwo(passes_.Size()));
+    passes_.Insert(type.Value(), newPass);
     
     return newPass;
 }
 
 void Technique::RemovePass(StringHash type)
 {
-    passes_.Erase(type);
-}
-
-Pass* Technique::GetPass(StringHash type) const
-{
-    HashMap<StringHash, SharedPtr<Pass> >::ConstIterator i = passes_.Find(type);
-    return i != passes_.End() ? i->second_ : (Pass*)0;
+    passes_.Erase(type.Value());
 }
 
 }

+ 11 - 7
Source/Engine/Graphics/Technique.h

@@ -23,6 +23,7 @@
 #pragma once
 
 #include "GraphicsDefs.h"
+#include "HashTable.h"
 #include "Resource.h"
 
 namespace Urho3D
@@ -141,20 +142,23 @@ public:
     /// Reset shader pointers in all passes.
     void ReleaseShaders();
     
-    /// Return all passes.
-    const HashMap<StringHash, SharedPtr<Pass> >& GetPasses() const { return passes_; }
-    /// Return whether has a pass.
-    bool HasPass(StringHash type) const { return passes_.Contains(type); }
-    /// Return a pass, or null if not found.
-    Pass* GetPass(StringHash type) const;
     /// Return whether requires %Shader %Model 3.
     bool IsSM3() const { return isSM3_; }
+    /// Return whether has a pass.
+    bool HasPass(StringHash type) const { return  passes_.Find(type.Value()) != 0; }
+    
+    /// Return a pass, or null if not found.
+    Pass* GetPass(StringHash type) const
+    {
+        SharedPtr<Pass>* passPtr = passes_.Find(type.Value());
+        return passPtr ? passPtr->Get() : 0;
+    }
     
 private:
     /// Require %Shader %Model 3 flag.
     bool isSM3_;
     /// Passes.
-    HashMap<StringHash, SharedPtr<Pass> > passes_;
+    HashTable<SharedPtr<Pass>, 16> passes_;
 };
 
 }