Browse Source

To restore lost performance, move less critical Node variables to an implementation struct to reduce Node instance memory size, and restore old unsafe PODVector operation that does not allow self-insertion.

Lasse Öörni 9 years ago
parent
commit
fee755e942
3 changed files with 142 additions and 121 deletions
  1. 77 72
      Source/Urho3D/Container/Vector.h
  2. 30 27
      Source/Urho3D/Scene/Node.cpp
  3. 35 22
      Source/Urho3D/Scene/Node.h

+ 77 - 72
Source/Urho3D/Container/Vector.h

@@ -571,7 +571,7 @@ private:
     }
     }
 };
 };
 
 
-/// %Vector template class for POD types. Does not call constructors or destructors and uses block move.
+/// %Vector template class for POD types. Does not call constructors or destructors and uses block move. Is intentionally (for performance reasons) unsafe for self-insertion.
 template <class T> class PODVector : public VectorBase
 template <class T> class PODVector : public VectorBase
 {
 {
 public:
 public:
@@ -601,7 +601,8 @@ public:
     /// Construct with initial data.
     /// Construct with initial data.
     PODVector(const T* data, unsigned size)
     PODVector(const T* data, unsigned size)
     {
     {
-        InsertElements(0, data, data + size);
+        Resize(size);
+        CopyElements(Buffer(), data, size);
     }
     }
 
 
     /// Construct from another vector.
     /// Construct from another vector.
@@ -631,8 +632,8 @@ public:
         // In case of self-assignment do nothing
         // In case of self-assignment do nothing
         if (&rhs != this)
         if (&rhs != this)
         {
         {
-            Clear();
-            InsertElements(0, rhs.Begin(), rhs.End());
+            Resize(rhs.size_);
+            CopyElements(Buffer(), rhs.Buffer(), rhs.size_);
         }
         }
         return *this;
         return *this;
     }
     }
@@ -732,13 +733,19 @@ public:
     /// Add an element at the end.
     /// Add an element at the end.
     void Push(const T& value)
     void Push(const T& value)
     {
     {
-        InsertElements(size_, &value, &value + 1);
+        if (size_ < capacity_)
+            ++size_;
+        else
+            Resize(size_ + 1);
+        Back() = value;
     }
     }
 
 
     /// Add another vector at the end.
     /// Add another vector at the end.
     void Push(const PODVector<T>& vector)
     void Push(const PODVector<T>& vector)
     {
     {
-        InsertElements(size_, vector.Begin(), vector.End());
+        unsigned oldSize = size_;
+        Resize(size_ + vector.size_);
+        CopyElements(Buffer() + oldSize, vector.Buffer(), vector.size_);
     }
     }
 
 
     /// Remove the last element.
     /// Remove the last element.
@@ -751,41 +758,78 @@ public:
     /// Insert an element at position.
     /// Insert an element at position.
     void Insert(unsigned pos, const T& value)
     void Insert(unsigned pos, const T& value)
     {
     {
-        InsertElements(pos, &value, &value + 1);
+        if (pos > size_)
+            pos = size_;
+
+        unsigned oldSize = size_;
+        Resize(size_ + 1);
+        MoveRange(pos + 1, pos, oldSize - pos);
+        Buffer()[pos] = value;
     }
     }
 
 
     /// Insert another vector at position.
     /// Insert another vector at position.
     void Insert(unsigned pos, const PODVector<T>& vector)
     void Insert(unsigned pos, const PODVector<T>& vector)
     {
     {
-        InsertElements(pos, vector.Begin(), vector.End());
+        if (pos > size_)
+            pos = size_;
+
+        unsigned oldSize = size_;
+        Resize(size_ + vector.size_);
+        MoveRange(pos + vector.size_, pos, oldSize - pos);
+        CopyElements(Buffer() + pos, vector.Buffer(), vector.size_);
     }
     }
 
 
     /// Insert an element by iterator.
     /// Insert an element by iterator.
     Iterator Insert(const Iterator& dest, const T& value)
     Iterator Insert(const Iterator& dest, const T& value)
     {
     {
         unsigned pos = (unsigned)(dest - Begin());
         unsigned pos = (unsigned)(dest - Begin());
-        return InsertElements(pos, &value, &value + 1);
+        if (pos > size_)
+            pos = size_;
+        Insert(pos, value);
+
+        return Begin() + pos;
     }
     }
 
 
     /// Insert a vector by iterator.
     /// Insert a vector by iterator.
     Iterator Insert(const Iterator& dest, const PODVector<T>& vector)
     Iterator Insert(const Iterator& dest, const PODVector<T>& vector)
     {
     {
         unsigned pos = (unsigned)(dest - Begin());
         unsigned pos = (unsigned)(dest - Begin());
-        return InsertElements(pos, vector.Begin(), vector.End());
+        if (pos > size_)
+            pos = size_;
+        Insert(pos, vector);
+
+        return Begin() + pos;
     }
     }
 
 
     /// Insert a vector partially by iterators.
     /// Insert a vector partially by iterators.
     Iterator Insert(const Iterator& dest, const ConstIterator& start, const ConstIterator& end)
     Iterator Insert(const Iterator& dest, const ConstIterator& start, const ConstIterator& end)
     {
     {
         unsigned pos = (unsigned)(dest - Begin());
         unsigned pos = (unsigned)(dest - Begin());
-        return InsertElements(pos, start, end);
+        if (pos > size_)
+            pos = size_;
+        unsigned length = (unsigned)(end - start);
+        Resize(size_ + length);
+        MoveRange(pos + length, pos, size_ - pos - length);
+        CopyElements(Buffer() + pos, &(*start), length);
+
+        return Begin() + pos;
     }
     }
 
 
     /// Insert elements.
     /// Insert elements.
     Iterator Insert(const Iterator& dest, const T* start, const T* end)
     Iterator Insert(const Iterator& dest, const T* start, const T* end)
     {
     {
         unsigned pos = (unsigned)(dest - Begin());
         unsigned pos = (unsigned)(dest - Begin());
-        return InsertElements(pos, start, end);
+        if (pos > size_)
+            pos = size_;
+        unsigned length = (unsigned)(end - start);
+        Resize(size_ + length);
+        MoveRange(pos + length, pos, size_ - pos - length);
+
+        T* destPtr = Buffer() + pos;
+        for (const T* i = start; i != end; ++i)
+            *destPtr++ = *i;
+
+        return Begin() + pos;
     }
     }
 
 
     /// Erase a range of elements.
     /// Erase a range of elements.
@@ -829,7 +873,7 @@ public:
         // Return if the range is illegal
         // Return if the range is illegal
         if (shiftStartIndex > size_ || !length)
         if (shiftStartIndex > size_ || !length)
             return;
             return;
-      
+
         unsigned newSize = size_ - length;
         unsigned newSize = size_ - length;
         unsigned trailingCount = size_ - shiftStartIndex;
         unsigned trailingCount = size_ - shiftStartIndex;
         if (trailingCount <= length)
         if (trailingCount <= length)
@@ -877,18 +921,27 @@ public:
     /// Resize the vector.
     /// Resize the vector.
     void Resize(unsigned newSize)
     void Resize(unsigned newSize)
     {
     {
-        PODVector<T> tempBuffer;
-        Resize(newSize, tempBuffer);
-    }
+        if (newSize > capacity_)
+        {
+            if (!capacity_)
+                capacity_ = newSize;
+            else
+            {
+                while (capacity_ < newSize)
+                    capacity_ += (capacity_ + 1) >> 1;
+            }
 
 
-    /// Resize the vector and fill new elements with default value.
-    void Resize(unsigned newSize, const T& value)
-    {
-        unsigned oldSize = Size();
-        PODVector<T> tempBuffer;
-        Resize(newSize, tempBuffer);
-        for (unsigned i = oldSize; i < newSize; ++i)
-            At(i) = value;
+            unsigned char* newBuffer = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
+            // Move the data into the new buffer and delete the old
+            if (buffer_)
+            {
+                CopyElements(reinterpret_cast<T*>(newBuffer), Buffer(), size_);
+                delete[] buffer_;
+            }
+            buffer_ = newBuffer;
+        }
+
+        size_ = newSize;
     }
     }
 
 
     /// Set new capacity.
     /// Set new capacity.
@@ -984,54 +1037,6 @@ public:
     T* Buffer() const { return reinterpret_cast<T*>(buffer_); }
     T* Buffer() const { return reinterpret_cast<T*>(buffer_); }
 
 
 private:
 private:
-    /// Resize the vector. Current buffer will be stored in tempBuffer in case of reallocation.
-    void Resize(unsigned newSize, PODVector<T>& tempBuffer)
-    {
-        if (newSize > capacity_)
-        {
-            Swap(tempBuffer);
-            size_ = tempBuffer.size_;
-            capacity_ = tempBuffer.capacity_;
-
-            if (!capacity_)
-                capacity_ = newSize;
-            else
-            {
-                while (capacity_ < newSize)
-                    capacity_ += (capacity_ + 1) >> 1;
-            }
-
-            buffer_ = AllocateBuffer((unsigned)(capacity_ * sizeof(T)));
-            // Move the data into the new buffer
-            if (tempBuffer.Buffer())
-            {
-                CopyElements(Buffer(), tempBuffer.Buffer(), size_);
-            }
-        }
-
-        size_ = newSize;
-    }
-
-    /// Insert elements.
-    template <typename RandomIteratorT>
-    Iterator InsertElements(unsigned pos, RandomIteratorT start, RandomIteratorT end)
-    {
-        assert(start <= end);
-
-        if (pos > size_)
-            pos = size_;
-        unsigned length = (unsigned)(end - start);
-        PODVector<T> tempBuffer;
-        Resize(size_ + length, tempBuffer);
-        MoveRange(pos + length, pos, size_ - pos - length);
-
-        T* destPtr = Buffer() + pos;
-        for (RandomIteratorT i = start; i != end; ++i)
-            *destPtr++ = *i;
-
-        return Begin() + pos;
-    }
-
     /// Move a range of elements within the vector.
     /// Move a range of elements within the vector.
     void MoveRange(unsigned dest, unsigned src, unsigned count)
     void MoveRange(unsigned dest, unsigned src, unsigned count)
     {
     {

+ 30 - 27
Source/Urho3D/Scene/Node.cpp

@@ -47,20 +47,21 @@ namespace Urho3D
 
 
 Node::Node(Context* context) :
 Node::Node(Context* context) :
     Animatable(context),
     Animatable(context),
-    networkUpdate_(false),
     worldTransform_(Matrix3x4::IDENTITY),
     worldTransform_(Matrix3x4::IDENTITY),
     dirty_(false),
     dirty_(false),
     enabled_(true),
     enabled_(true),
     enabledPrev_(true),
     enabledPrev_(true),
+    networkUpdate_(false),
     parent_(0),
     parent_(0),
     scene_(0),
     scene_(0),
     id_(0),
     id_(0),
     position_(Vector3::ZERO),
     position_(Vector3::ZERO),
     rotation_(Quaternion::IDENTITY),
     rotation_(Quaternion::IDENTITY),
     scale_(Vector3::ONE),
     scale_(Vector3::ONE),
-    worldRotation_(Quaternion::IDENTITY),
-    owner_(0)
+    worldRotation_(Quaternion::IDENTITY)
 {
 {
+    impl_ = new NodeImpl();
+    impl_->owner_ = 0;
 }
 }
 
 
 Node::~Node()
 Node::~Node()
@@ -71,6 +72,8 @@ Node::~Node()
     // Remove from the scene
     // Remove from the scene
     if (scene_)
     if (scene_)
         scene_->NodeRemoved(this);
         scene_->NodeRemoved(this);
+
+    delete impl_;
 }
 }
 
 
 void Node::RegisterObject(Context* context)
 void Node::RegisterObject(Context* context)
@@ -320,10 +323,10 @@ bool Node::SaveJSON(Serializer& dest, const String& indentation) const
 
 
 void Node::SetName(const String& name)
 void Node::SetName(const String& name)
 {
 {
-    if (name != name_)
+    if (name != impl_->name_)
     {
     {
-        name_ = name;
-        nameHash_ = name_;
+        impl_->name_ = name;
+        impl_->nameHash_ = name;
 
 
         MarkNetworkUpdate();
         MarkNetworkUpdate();
 
 
@@ -355,7 +358,7 @@ void Node::AddTag(const String& tag)
         return;
         return;
     
     
     // Add tag
     // Add tag
-    tags_.Push(tag);
+    impl_->tags_.Push(tag);
 
 
     // Cache
     // Cache
     scene_->NodeTagAdded(this, tag);
     scene_->NodeTagAdded(this, tag);
@@ -387,7 +390,7 @@ void Node::AddTags(const StringVector& tags)
 
 
 bool Node::RemoveTag(const String& tag)
 bool Node::RemoveTag(const String& tag)
 {
 {
-    bool removed = tags_.Remove(tag);
+    bool removed = impl_->tags_.Remove(tag);
 
 
     // Nothing to do
     // Nothing to do
     if (!removed)
     if (!removed)
@@ -416,21 +419,21 @@ void Node::RemoveAllTags()
     // Clear old scene cache
     // Clear old scene cache
     if (scene_)
     if (scene_)
     {
     {
-        for (unsigned i = 0; i < tags_.Size(); ++i)
+        for (unsigned i = 0; i < impl_->tags_.Size(); ++i)
         {
         {
-            scene_->NodeTagRemoved(this, tags_[i]);
+            scene_->NodeTagRemoved(this, impl_->tags_[i]);
 
 
             // Send event
             // Send event
             using namespace NodeTagRemoved;
             using namespace NodeTagRemoved;
             VariantMap& eventData = GetEventDataMap();
             VariantMap& eventData = GetEventDataMap();
             eventData[P_SCENE] = scene_;
             eventData[P_SCENE] = scene_;
             eventData[P_NODE] = this;
             eventData[P_NODE] = this;
-            eventData[P_TAG] = tags_[i];
+            eventData[P_TAG] = impl_->tags_[i];
             scene_->SendEvent(E_NODETAGREMOVED, eventData);
             scene_->SendEvent(E_NODETAGREMOVED, eventData);
         }
         }
     }
     }
 
 
-    tags_.Clear();
+    impl_->tags_.Clear();
 
 
     // Sync
     // Sync
     MarkNetworkUpdate();
     MarkNetworkUpdate();
@@ -724,7 +727,7 @@ void Node::SetEnabledRecursive(bool enable)
 
 
 void Node::SetOwner(Connection* owner)
 void Node::SetOwner(Connection* owner)
 {
 {
-    owner_ = owner;
+    impl_->owner_ = owner;
 }
 }
 
 
 void Node::MarkDirty()
 void Node::MarkDirty()
@@ -1329,7 +1332,7 @@ bool Node::HasComponent(StringHash type) const
 
 
 bool Node::HasTag(const String& tag) const
 bool Node::HasTag(const String& tag) const
 {
 {
-    return tags_.Contains(tag);
+    return impl_->tags_.Contains(tag);
 }
 }
 
 
 const Variant& Node::GetVar(StringHash key) const
 const Variant& Node::GetVar(StringHash key) const
@@ -1456,21 +1459,21 @@ const Vector3& Node::GetNetPositionAttr() const
 
 
 const PODVector<unsigned char>& Node::GetNetRotationAttr() const
 const PODVector<unsigned char>& Node::GetNetRotationAttr() const
 {
 {
-    attrBuffer_.Clear();
-    attrBuffer_.WritePackedQuaternion(rotation_);
-    return attrBuffer_.GetBuffer();
+    impl_->attrBuffer_.Clear();
+    impl_->attrBuffer_.WritePackedQuaternion(rotation_);
+    return impl_->attrBuffer_.GetBuffer();
 }
 }
 
 
 const PODVector<unsigned char>& Node::GetNetParentAttr() const
 const PODVector<unsigned char>& Node::GetNetParentAttr() const
 {
 {
-    attrBuffer_.Clear();
+    impl_->attrBuffer_.Clear();
     Scene* scene = GetScene();
     Scene* scene = GetScene();
     if (scene && parent_ && parent_ != scene)
     if (scene && parent_ && parent_ != scene)
     {
     {
         // If parent is replicated, can write the ID directly
         // If parent is replicated, can write the ID directly
         unsigned parentID = parent_->GetID();
         unsigned parentID = parent_->GetID();
         if (parentID < FIRST_LOCAL_ID)
         if (parentID < FIRST_LOCAL_ID)
-            attrBuffer_.WriteNetID(parentID);
+            impl_->attrBuffer_.WriteNetID(parentID);
         else
         else
         {
         {
             // Parent is local: traverse hierarchy to find a non-local base node
             // Parent is local: traverse hierarchy to find a non-local base node
@@ -1480,12 +1483,12 @@ const PODVector<unsigned char>& Node::GetNetParentAttr() const
                 current = current->GetParent();
                 current = current->GetParent();
 
 
             // Then write the base node ID and the parent's name hash
             // Then write the base node ID and the parent's name hash
-            attrBuffer_.WriteNetID(current->GetID());
-            attrBuffer_.WriteStringHash(parent_->GetNameHash());
+            impl_->attrBuffer_.WriteNetID(current->GetID());
+            impl_->attrBuffer_.WriteStringHash(parent_->GetNameHash());
         }
         }
     }
     }
 
 
-    return attrBuffer_.GetBuffer();
+    return impl_->attrBuffer_.GetBuffer();
 }
 }
 
 
 bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren, bool rewriteIDs, CreateMode mode)
 bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren, bool rewriteIDs, CreateMode mode)
@@ -1625,7 +1628,7 @@ bool Node::LoadJSON(const JSONValue& source, SceneResolver& resolver, bool readC
 void Node::PrepareNetworkUpdate()
 void Node::PrepareNetworkUpdate()
 {
 {
     // Update dependency nodes list first
     // Update dependency nodes list first
-    dependencyNodes_.Clear();
+    impl_->dependencyNodes_.Clear();
 
 
     // Add the parent node, but if it is local, traverse to the first non-local node
     // Add the parent node, but if it is local, traverse to the first non-local node
     if (parent_ && parent_ != scene_)
     if (parent_ && parent_ != scene_)
@@ -1634,7 +1637,7 @@ void Node::PrepareNetworkUpdate()
         while (current->id_ >= FIRST_LOCAL_ID)
         while (current->id_ >= FIRST_LOCAL_ID)
             current = current->parent_;
             current = current->parent_;
         if (current && current != scene_)
         if (current && current != scene_)
-            dependencyNodes_.Push(current);
+            impl_->dependencyNodes_.Push(current);
     }
     }
 
 
     // Let the components add their dependencies
     // Let the components add their dependencies
@@ -1642,7 +1645,7 @@ void Node::PrepareNetworkUpdate()
     {
     {
         Component* component = *i;
         Component* component = *i;
         if (component->GetID() < FIRST_LOCAL_ID)
         if (component->GetID() < FIRST_LOCAL_ID)
-            component->GetDependencyNodes(dependencyNodes_);
+            component->GetDependencyNodes(impl_->dependencyNodes_);
     }
     }
 
 
     // Then check for node attribute changes
     // Then check for node attribute changes
@@ -1712,8 +1715,8 @@ void Node::PrepareNetworkUpdate()
 
 
 void Node::CleanupConnection(Connection* connection)
 void Node::CleanupConnection(Connection* connection)
 {
 {
-    if (owner_ == connection)
-        owner_ = 0;
+    if (impl_->owner_ == connection)
+        impl_->owner_ = 0;
 
 
     if (networkState_)
     if (networkState_)
     {
     {

+ 35 - 22
Source/Urho3D/Scene/Node.h

@@ -31,6 +31,7 @@ namespace Urho3D
 
 
 class Component;
 class Component;
 class Connection;
 class Connection;
+class Node;
 class Scene;
 class Scene;
 class SceneResolver;
 class SceneResolver;
 
 
@@ -51,6 +52,23 @@ enum TransformSpace
     TS_WORLD
     TS_WORLD
 };
 };
 
 
+/// Internal implementation structure for less performance-critical Node variables.
+struct URHO3D_API NodeImpl
+{
+    /// Nodes this node depends on for network updates.
+    PODVector<Node*> dependencyNodes_;
+    /// Network owner connection.
+    Connection* owner_;
+    /// Name.
+    String name_;
+    /// Tag strings.
+    StringVector tags_;
+    /// Name hash.
+    StringHash nameHash_;
+    /// Attribute buffer for network updates.
+    mutable VectorBuffer attrBuffer_;
+};
+
 /// %Scene node that may contain components and child nodes.
 /// %Scene node that may contain components and child nodes.
 class URHO3D_API Node : public Animatable
 class URHO3D_API Node : public Animatable
 {
 {
@@ -316,13 +334,13 @@ public:
     unsigned GetID() const { return id_; }
     unsigned GetID() const { return id_; }
 
 
     /// Return name.
     /// Return name.
-    const String& GetName() const { return name_; }
+    const String& GetName() const { return impl_->name_; }
 
 
     /// Return name hash.
     /// Return name hash.
-    StringHash GetNameHash() const { return nameHash_; }
+    StringHash GetNameHash() const { return impl_->nameHash_; }
 
 
     /// Return all tags.
     /// Return all tags.
-    const StringVector& GetTags() const { return tags_; }
+    const StringVector& GetTags() const { return impl_->tags_; }
 
 
     /// Return whether has a specific tag.
     /// Return whether has a specific tag.
     bool HasTag(const String& tag) const;
     bool HasTag(const String& tag) const;
@@ -340,7 +358,7 @@ public:
     bool IsEnabledSelf() const { return enabledPrev_; }
     bool IsEnabledSelf() const { return enabledPrev_; }
 
 
     /// Return owner connection in networking.
     /// Return owner connection in networking.
-    Connection* GetOwner() const { return owner_; }
+    Connection* GetOwner() const { return impl_->owner_; }
 
 
     /// Return position in parent space.
     /// Return position in parent space.
     const Vector3& GetPosition() const { return position_; }
     const Vector3& GetPosition() const { return position_; }
@@ -564,7 +582,7 @@ public:
     bool LoadJSON(const JSONValue& source, SceneResolver& resolver, bool loadChildren = true, bool rewriteIDs = false,
     bool LoadJSON(const JSONValue& source, SceneResolver& resolver, bool loadChildren = true, bool rewriteIDs = false,
         CreateMode mode = REPLICATED);
         CreateMode mode = REPLICATED);
     /// Return the depended on nodes to order network updates.
     /// Return the depended on nodes to order network updates.
-    const PODVector<Node*>& GetDependencyNodes() const { return dependencyNodes_; }
+    const PODVector<Node*>& GetDependencyNodes() const { return impl_->dependencyNodes_; }
 
 
     /// Prepare network update by comparing attributes and marking replication states dirty as necessary.
     /// Prepare network update by comparing attributes and marking replication states dirty as necessary.
     void PrepareNetworkUpdate();
     void PrepareNetworkUpdate();
@@ -601,11 +619,6 @@ protected:
     /// Find target of an attribute animation from object hierarchy by name.
     /// Find target of an attribute animation from object hierarchy by name.
     virtual Animatable* FindAttributeAnimationTarget(const String& name, String& outName);
     virtual Animatable* FindAttributeAnimationTarget(const String& name, String& outName);
 
 
-    /// Network update queued flag.
-    bool networkUpdate_;
-    /// User variables.
-    VariantMap vars_;
-
 private:
 private:
     /// Set enabled/disabled state with optional recursion. Optionally affect the remembered enable state.
     /// Set enabled/disabled state with optional recursion. Optionally affect the remembered enable state.
     void SetEnabled(bool enable, bool recursive, bool storeSelf);
     void SetEnabled(bool enable, bool recursive, bool storeSelf);
@@ -638,6 +651,12 @@ private:
     bool enabled_;
     bool enabled_;
     /// Last SetEnabled flag before any SetDeepEnabled.
     /// Last SetEnabled flag before any SetDeepEnabled.
     bool enabledPrev_;
     bool enabledPrev_;
+
+protected:
+    /// Network update queued flag.
+    bool networkUpdate_;
+
+private:
     /// Parent scene node.
     /// Parent scene node.
     Node* parent_;
     Node* parent_;
     /// Scene (root node.)
     /// Scene (root node.)
@@ -658,18 +677,12 @@ private:
     Vector<SharedPtr<Node> > children_;
     Vector<SharedPtr<Node> > children_;
     /// Node listeners.
     /// Node listeners.
     Vector<WeakPtr<Component> > listeners_;
     Vector<WeakPtr<Component> > listeners_;
-    /// Nodes this node depends on for network updates.
-    PODVector<Node*> dependencyNodes_;
-    /// Network owner connection.
-    Connection* owner_;
-    /// Name.
-    String name_;
-    /// Tag strings.
-    StringVector tags_;
-    /// Name hash.
-    StringHash nameHash_;
-    /// Attribute buffer for network updates.
-    mutable VectorBuffer attrBuffer_;
+    /// Pointer to implementation.
+    NodeImpl* impl_;
+
+protected:
+    /// User variables.
+    VariantMap vars_;
 };
 };
 
 
 template <class T> T* Node::CreateComponent(CreateMode mode, unsigned id)
 template <class T> T* Node::CreateComponent(CreateMode mode, unsigned id)