diff --git a/Ryujinx.Common/Collections/TreeDictionary.cs b/Ryujinx.Common/Collections/TreeDictionary.cs
new file mode 100644
index 0000000000..a44f650c1b
--- /dev/null
+++ b/Ryujinx.Common/Collections/TreeDictionary.cs
@@ -0,0 +1,987 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Ryujinx.Common.Collections
+{
+    /// <summary>
+    /// Dictionary that provides the ability for O(logN) Lookups for keys that exist in the Dictionary, and O(logN) lookups for keys immediately greater than or less than a specified key.
+    /// </summary>
+    /// <typeparam name="K">Key</typeparam>
+    /// <typeparam name="V">Value</typeparam>
+    public class TreeDictionary<K, V> : IDictionary<K, V> where K : IComparable<K>
+    {
+        private const bool Black = true;
+        private const bool Red = false;
+        private Node<K, V> _root = null;
+        private int _count = 0;
+        public TreeDictionary() { }
+
+        #region Public Methods
+
+        /// <summary>
+        /// Returns the value of the node whose key is <paramref name="key"/>, or the default value if no such node exists.
+        /// </summary>
+        /// <param name="key">Key of the node value to get</param>
+        /// <returns>Value associated w/ <paramref name="key"/></returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        public V Get(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Node<K, V> node = GetNode(key);
+
+            if (node == null)
+            {
+                return default;
+            }
+
+            return node.Value;
+        }
+
+        /// <summary>
+        /// Adds a new node into the tree whose key is <paramref name="key"/> key and value is <paramref name="value"/>.
+        /// <br></br>
+        /// <b>Note:</b> Adding the same key multiple times will cause the value for that key to be overwritten.
+        /// </summary>
+        /// <param name="key">Key of the node to add</param>
+        /// <param name="value">Value of the node to add</param>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> are null</exception>
+        public void Add(K key, V value)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            if (null == value)
+            {
+                throw new ArgumentNullException(nameof(value));
+            }
+
+            Insert(key, value);
+        }
+
+        /// <summary>
+        /// Removes the node whose key is <paramref name="key"/> from the tree.
+        /// </summary>
+        /// <param name="key">Key of the node to remove</param>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        public void Remove(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            if (Delete(key) != null)
+            {
+                _count--;
+            }
+        }
+
+        /// <summary>
+        /// Returns the value whose key is equal to or immediately less than <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key for which to find the floor value of</param>
+        /// <returns>Key of node immediately less than <paramref name="key"/></returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        public K Floor(K key)
+        {
+            Node<K, V> node = FloorNode(key);
+            if (node != null)
+            {
+                return node.Key;
+            }
+            return default;
+        }
+
+        /// <summary>
+        /// Returns the node whose key is equal to or immediately greater than <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key for which to find the ceiling node of</param>
+        /// <returns>Key of node immediately greater than <paramref name="key"/></returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        public K Ceiling(K key)
+        {
+            Node<K, V> node = CeilingNode(key);
+            if (node != null)
+            {
+                return node.Key;
+            }
+            return default;
+        }
+
+        /// <summary>
+        /// Finds the value whose key is immediately greater than <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key to find the successor of</param>
+        /// <returns>Value</returns>
+        public K SuccessorOf(K key)
+        {
+            Node<K, V> node = GetNode(key);
+            if (node != null)
+            {
+                Node<K, V> successor =  SuccessorOf(node);
+
+                return successor != null ? successor.Key : default;
+            }
+            return default;
+        }
+
+        /// <summary>
+        /// Finds the value whose key is immediately less than <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key to find the predecessor of</param>
+        /// <returns>Value</returns>
+        public K PredecessorOf(K key)
+        {
+            Node<K, V> node = GetNode(key);
+            if (node != null)
+            {
+                Node<K, V> predecessor = PredecessorOf(node);
+
+                return predecessor != null ? predecessor.Key : default;
+            }
+            return default;
+        }
+
+        /// <summary>
+        /// Adds all the nodes in the dictionary as key/value pairs into <paramref name="list"/>.
+        /// <br></br>
+        /// The key/value pairs will be added in Level Order.
+        /// </summary>
+        /// <param name="list">List to add the tree pairs into</param>
+        public List<KeyValuePair<K, V>> AsLevelOrderList()
+        {
+            List<KeyValuePair<K, V>> list = new List<KeyValuePair<K, V>>();
+
+            Queue<Node<K, V>> nodes = new Queue<Node<K, V>>();
+
+            if (this._root != null)
+            {
+                nodes.Enqueue(this._root);
+            }
+            while (nodes.Count > 0)
+            {
+                Node<K, V> node = nodes.Dequeue();
+                list.Add(new KeyValuePair<K, V>(node.Key, node.Value));
+                if (node.Left != null)
+                {
+                    nodes.Enqueue(node.Left);
+                }
+                if (node.Right != null)
+                {
+                    nodes.Enqueue(node.Right);
+                }
+            }
+            return list;
+        }
+
+        /// <summary>
+        /// Adds all the nodes in the dictionary into <paramref name="list"/>.
+        /// <br></br>
+        /// The nodes will be added in Sorted by Key Order.
+        /// </summary>
+        public List<KeyValuePair<K, V>> AsList()
+        {
+            List<KeyValuePair<K, V>> list = new List<KeyValuePair<K, V>>();
+
+            Queue<Node<K, V>> nodes = new Queue<Node<K, V>>();
+
+            if (this._root != null)
+            {
+                nodes.Enqueue(this._root);
+            }
+            while (nodes.Count > 0)
+            {
+                Node<K, V> node = nodes.Dequeue();
+                list.Add(new KeyValuePair<K, V>(node.Key, node.Value));
+                if (node.Left != null)
+                {
+                    nodes.Enqueue(node.Left);
+                }
+                if (node.Right != null)
+                {
+                    nodes.Enqueue(node.Right);
+                }
+            }
+
+            return list;
+        }
+        #endregion
+        #region Private Methods (BST)
+
+        /// <summary>
+        /// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
+        /// </summary>
+        /// <param name="key">Key of the node to get</param>
+        /// <returns>Node reference in the tree</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        private Node<K, V> GetNode(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Node<K, V> node = _root;
+            while (node != null)
+            {
+                int cmp = key.CompareTo(node.Key);
+                if (cmp < 0)
+                {
+                    node = node.Left;
+                }
+                else if (cmp > 0)
+                {
+                    node = node.Right;
+                }
+                else
+                {
+                    return node;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Inserts a new node into the tree whose key is <paramref name="key"/> and value is <paramref name="value"/>.
+        /// <br></br>
+        /// Adding the same key multiple times will overwrite the previous value.
+        /// </summary>
+        /// <param name="key">Key of the node to insert</param>
+        /// <param name="value">Value of the node to insert</param>
+        private void Insert(K key, V value)
+        {
+            Node<K, V> newNode = BSTInsert(key, value);
+            RestoreBalanceAfterInsertion(newNode);
+        }
+
+        /// <summary>
+        /// Insertion Mechanism for a Binary Search Tree (BST).
+        /// <br></br>
+        /// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="key"/>, and all children in the right subtree are greater than <paramref name="key"/>.
+        /// <br></br>
+        /// <b>Note: </b> If a node whose key is <paramref name="key"/> already exists, it's value will be overwritten.
+        /// </summary>
+        /// <param name="key">Key of the node to insert</param>
+        /// <param name="value">Value of the node to insert</param>
+        /// <returns>The inserted Node</returns>
+        private Node<K, V> BSTInsert(K key, V value)
+        {
+            Node<K, V> parent = null;
+            Node<K, V> node = _root;
+
+            while (node != null)
+            {
+                parent = node;
+                int cmp = key.CompareTo(node.Key);
+                if (cmp < 0)
+                {
+                    node = node.Left;
+                }
+                else if (cmp > 0)
+                {
+                    node = node.Right;
+                }
+                else
+                {
+                    node.Value = value;
+                    return node;
+                }
+            }
+            Node<K, V> newNode = new Node<K, V>(key, value, parent);
+            if (newNode.Parent == null)
+            {
+                _root = newNode;
+            }
+            else if (key.CompareTo(parent.Key) < 0)
+            {
+                parent.Left = newNode;
+            }
+            else
+            {
+                parent.Right = newNode;
+            }
+            _count++;
+            return newNode;
+        }
+
+        /// <summary>
+        /// Removes <paramref name="key"/> from the dictionary, if it exists.
+        /// </summary>
+        /// <param name="key">Key of the node to delete</param>
+        /// <returns>The deleted Node</returns>
+        private Node<K, V> Delete(K key)
+        {
+            // O(1) Retrieval
+            Node<K, V> nodeToDelete = GetNode(key);
+
+            if (nodeToDelete == null) return null;
+
+            Node<K, V> replacementNode;
+
+            if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null)
+            {
+                replacementNode = nodeToDelete;
+            }
+            else
+            {
+                replacementNode = PredecessorOf(nodeToDelete);
+            }
+
+            Node<K, V> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
+
+            if (tmp != null)
+            {
+                tmp.Parent = ParentOf(replacementNode);
+            }
+
+            if (ParentOf(replacementNode) == null)
+            {
+                _root = tmp;
+            }
+
+            else if (replacementNode == LeftOf(ParentOf(replacementNode)))
+            {
+                ParentOf(replacementNode).Left = tmp;
+            }
+            else
+            {
+                ParentOf(replacementNode).Right = tmp;
+            }
+
+            if (replacementNode != nodeToDelete)
+            {
+                nodeToDelete.Key = replacementNode.Key;
+                nodeToDelete.Value = replacementNode.Value;
+            }
+
+            if (tmp != null && ColorOf(replacementNode) == Black)
+            {
+                RestoreBalanceAfterRemoval(tmp);
+            }
+
+            return replacementNode;
+        }
+
+        /// <summary>
+        /// Returns the node with the largest key where <paramref name="node"/> is considered the root node.
+        /// </summary>
+        /// <param name="node">Root Node</param>
+        /// <returns>Node with the maximum key in the tree of <paramref name="node"/></returns>
+        /// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
+        private static Node<K, V> Maximum(Node<K, V> node)
+        {
+            if (node == null)
+            {
+                throw new ArgumentNullException(nameof(node));
+            }
+            Node<K, V> tmp = node;
+            while (tmp.Right != null)
+            {
+                tmp = tmp.Right;
+            }
+
+            return tmp;
+        }
+
+        /// <summary>
+        /// Returns the node with the smallest key where <paramref name="node"/> is considered the root node.
+        /// </summary>
+        /// <param name="node">Root Node</param>
+        /// <returns>Node with the minimum key in the tree of <paramref name="node"/></returns>
+        ///<exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
+        private static Node<K, V> Minimum(Node<K, V> node)
+        {
+            if (node == null)
+            {
+                throw new ArgumentNullException(nameof(node));
+            }
+            Node<K, V> tmp = node;
+            while (tmp.Left != null)
+            {
+                tmp = tmp.Left;
+            }
+
+            return tmp;
+        }
+
+        /// <summary>
+        /// Returns the node whose key immediately less than or equal to <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key for which to find the floor node of</param>
+        /// <returns>Node whose key is immediately less than or equal to <paramref name="key"/>, or null if no such node is found.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        private Node<K, V> FloorNode(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            Node<K, V> tmp = _root;
+
+            while (tmp != null)
+            {
+                int cmp = key.CompareTo(tmp.Key);
+                if (cmp > 0)
+                {
+                    if (tmp.Right != null)
+                    {
+                        tmp = tmp.Right;
+                    }
+                    else
+                    {
+                        return tmp;
+                    }
+                }
+                else if (cmp < 0)
+                {
+                    if (tmp.Left != null)
+                    {
+                        tmp = tmp.Left;
+                    }
+                    else
+                    {
+                        Node<K, V> parent = tmp.Parent;
+                        Node<K, V> ptr = tmp;
+                        while (parent != null && ptr == parent.Left)
+                        {
+                            ptr = parent;
+                            parent = parent.Parent;
+                        }
+                        return parent;
+                    }
+                }
+                else
+                {
+                    return tmp;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Returns the node whose key is immediately greater than or equal to than <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">Key for which to find the ceiling node of</param>
+        /// <returns>Node whose key is immediately greater than or equal to <paramref name="key"/>, or null if no such node is found.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
+        private Node<K, V> CeilingNode(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            Node<K, V> tmp = _root;
+
+            while (tmp != null)
+            {
+                int cmp = key.CompareTo(tmp.Key);
+                if (cmp < 0)
+                {
+                    if (tmp.Left != null)
+                    {
+                        tmp = tmp.Left;
+                    }
+                    else
+                    {
+                        return tmp;
+                    }
+                }
+                else if (cmp > 0)
+                {
+                    if (tmp.Right != null)
+                    {
+                        tmp = tmp.Right;
+                    }
+                    else
+                    {
+                        Node<K, V> parent = tmp.Parent;
+                        Node<K, V> ptr = tmp;
+                        while (parent != null && ptr == parent.Right)
+                        {
+                            ptr = parent;
+                            parent = parent.Parent;
+                        }
+                        return parent;
+                    }
+                }
+                else
+                {
+                    return tmp;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Finds the node with the key immediately greater than <paramref name="node"/>.Key.
+        /// </summary>
+        /// <param name="node">Node to find the successor of</param>
+        /// <returns>Successor of <paramref name="node"/></returns>
+        private static Node<K, V> SuccessorOf(Node<K, V> node)
+        {
+            if (node.Right != null)
+            {
+                return Minimum(node.Right);
+            }
+            Node<K, V> parent = node.Parent;
+            while (parent != null && node == parent.Right)
+            {
+                node = parent;
+                parent = parent.Parent;
+            }
+            return parent;
+        }
+
+        /// <summary>
+        /// Finds the node whose key immediately less than <paramref name="node"/>.Key.
+        /// </summary>
+        /// <param name="node">Node to find the predecessor of</param>
+        /// <returns>Predecessor of <paramref name="node"/></returns>
+        private static Node<K, V> PredecessorOf(Node<K, V> node)
+        {
+            if (node.Left != null)
+            {
+                return Maximum(node.Left);
+            }
+            Node<K, V> parent = node.Parent;
+            while (parent != null && node == parent.Left)
+            {
+                node = parent;
+                parent = parent.Parent;
+            }
+            return parent;
+        }
+        #endregion
+        #region Private Methods (RBL)
+
+        private void RestoreBalanceAfterRemoval(Node<K, V> balanceNode)
+        {
+            Node<K, V> ptr = balanceNode;
+
+            while (ptr != _root && ColorOf(ptr) == Black)
+            {
+                if (ptr == LeftOf(ParentOf(ptr)))
+                {
+                    Node<K, V> sibling = RightOf(ParentOf(ptr));
+
+                    if (ColorOf(sibling) == Red)
+                    {
+                        SetColor(sibling, Black);
+                        SetColor(ParentOf(ptr), Red);
+                        RotateLeft(ParentOf(ptr));
+                        sibling = RightOf(ParentOf(ptr));
+                    }
+                    if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black)
+                    {
+                        SetColor(sibling, Red);
+                        ptr = ParentOf(ptr);
+                    }
+                    else
+                    {
+                        if (ColorOf(RightOf(sibling)) == Black)
+                        {
+                            SetColor(LeftOf(sibling), Black);
+                            SetColor(sibling, Red);
+                            RotateRight(sibling);
+                            sibling = RightOf(ParentOf(ptr));
+                        }
+                        SetColor(sibling, ColorOf(ParentOf(ptr)));
+                        SetColor(ParentOf(ptr), Black);
+                        SetColor(RightOf(sibling), Black);
+                        RotateLeft(ParentOf(ptr));
+                        ptr = _root;
+                    }
+                }
+                else
+                {
+                    Node<K, V> sibling = LeftOf(ParentOf(ptr));
+
+                    if (ColorOf(sibling) == Red)
+                    {
+                        SetColor(sibling, Black);
+                        SetColor(ParentOf(ptr), Red);
+                        RotateRight(ParentOf(ptr));
+                        sibling = LeftOf(ParentOf(ptr));
+                    }
+                    if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black)
+                    {
+                        SetColor(sibling, Red);
+                        ptr = ParentOf(ptr);
+                    }
+                    else
+                    {
+                        if (ColorOf(LeftOf(sibling)) == Black)
+                        {
+                            SetColor(RightOf(sibling), Black);
+                            SetColor(sibling, Red);
+                            RotateLeft(sibling);
+                            sibling = LeftOf(ParentOf(ptr));
+                        }
+                        SetColor(sibling, ColorOf(ParentOf(ptr)));
+                        SetColor(ParentOf(ptr), Black);
+                        SetColor(LeftOf(sibling), Black);
+                        RotateRight(ParentOf(ptr));
+                        ptr = _root;
+                    }
+                }
+            }
+            SetColor(ptr, Black);
+        }
+
+        private void RestoreBalanceAfterInsertion(Node<K, V> balanceNode)
+        {
+            SetColor(balanceNode, Red);
+            while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red)
+            {
+                if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode))))
+                {
+                    Node<K, V> sibling = RightOf(ParentOf(ParentOf(balanceNode)));
+
+                    if (ColorOf(sibling) == Red)
+                    {
+                        SetColor(ParentOf(balanceNode), Black);
+                        SetColor(sibling, Black);
+                        SetColor(ParentOf(ParentOf(balanceNode)), Red);
+                        balanceNode = ParentOf(ParentOf(balanceNode));
+                    }
+                    else
+                    {
+                        if (balanceNode == RightOf(ParentOf(balanceNode)))
+                        {
+                            balanceNode = ParentOf(balanceNode);
+                            RotateLeft(balanceNode);
+                        }
+                        SetColor(ParentOf(balanceNode), Black);
+                        SetColor(ParentOf(ParentOf(balanceNode)), Red);
+                        RotateRight(ParentOf(ParentOf(balanceNode)));
+                    }
+                }
+                else
+                {
+                    Node<K, V> sibling = LeftOf(ParentOf(ParentOf(balanceNode)));
+
+                    if (ColorOf(sibling) == Red)
+                    {
+                        SetColor(ParentOf(balanceNode), Black);
+                        SetColor(sibling, Black);
+                        SetColor(ParentOf(ParentOf(balanceNode)), Red);
+                        balanceNode = ParentOf(ParentOf(balanceNode));
+                    }
+                    else
+                    {
+                        if (balanceNode == LeftOf(ParentOf(balanceNode)))
+                        {
+                            balanceNode = ParentOf(balanceNode);
+                            RotateRight(balanceNode);
+                        }
+                        SetColor(ParentOf(balanceNode), Black);
+                        SetColor(ParentOf(ParentOf(balanceNode)), Red);
+                        RotateLeft(ParentOf(ParentOf(balanceNode)));
+                    }
+                }
+            }
+            SetColor(_root, Black);
+        }
+
+        private void RotateLeft(Node<K, V> node)
+        {
+            if (node != null)
+            {
+                Node<K, V> right = RightOf(node);
+                node.Right = LeftOf(right);
+                if (LeftOf(right) != null)
+                {
+                    LeftOf(right).Parent = node;
+                }
+                right.Parent = ParentOf(node);
+                if (ParentOf(node) == null)
+                {
+                    _root = right;
+                }
+                else if (node == LeftOf(ParentOf(node)))
+                {
+                    ParentOf(node).Left = right;
+                }
+                else
+                {
+                    ParentOf(node).Right = right;
+                }
+                right.Left = node;
+                node.Parent = right;
+            }
+        }
+
+        private void RotateRight(Node<K, V> node)
+        {
+            if (node != null)
+            {
+                Node<K, V> left = LeftOf(node);
+                node.Left = RightOf(left);
+                if (RightOf(left) != null)
+                {
+                    RightOf(left).Parent = node;
+                }
+                left.Parent = node.Parent;
+                if (ParentOf(node) == null)
+                {
+                    _root = left;
+                }
+                else if (node == RightOf(ParentOf(node)))
+                {
+                    ParentOf(node).Right = left;
+                }
+                else
+                {
+                    ParentOf(node).Left = left;
+                }
+                left.Right = node;
+                node.Parent = left;
+            }
+        }
+        #endregion
+
+        #region Safety-Methods
+
+        // These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against nullpointerexceptions.
+
+        /// <summary>
+        /// Returns the color of <paramref name="node"/>, or Black if it is null.
+        /// </summary>
+        /// <param name="node">Node</param>
+        /// <returns>The boolean color of <paramref name="node"/>, or black if null</returns>
+        private static bool ColorOf(Node<K, V> node)
+        {
+            return node == null || node.Color;
+        }
+
+        /// <summary>
+        /// Sets the color of <paramref name="node"/> node to <paramref name="color"/>.
+        /// <br></br>
+        /// This method does nothing if <paramref name="node"/> is null.
+        /// </summary>
+        /// <param name="node">Node to set the color of</param>
+        /// <param name="color">Color (Boolean)</param>
+        private static void SetColor(Node<K, V> node, bool color)
+        {
+            if (node != null)
+            {
+                node.Color = color;
+            }
+        }
+
+        /// <summary>
+        /// This method returns the left node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
+        /// </summary>
+        /// <param name="node">Node to retrieve the left child from</param>
+        /// <returns>Left child of <paramref name="node"/></returns>
+        private static Node<K, V> LeftOf(Node<K, V> node)
+        {
+            return node?.Left;
+        }
+
+        /// <summary>
+        /// This method returns the right node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
+        /// </summary>
+        /// <param name="node">Node to retrieve the right child from</param>
+        /// <returns>Right child of <paramref name="node"/></returns>
+        private static Node<K, V> RightOf(Node<K, V> node)
+        {
+            return node?.Right;
+        }
+
+        /// <summary>
+        /// Returns the parent node of <paramref name="node"/>, or null if <paramref name="node"/> is null.
+        /// </summary>
+        /// <param name="node">Node to retrieve the parent from</param>
+        /// <returns>Parent of <paramref name="node"/></returns>
+        private static Node<K, V> ParentOf(Node<K, V> node)
+        {
+            return node?.Parent;
+        }
+        #endregion
+
+        #region Interface Implementations
+
+        // Method descriptions are not provided as they are already included as part of the interface.
+        public bool ContainsKey(K key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            return GetNode(key) != null;
+        }
+
+        bool IDictionary<K, V>.Remove(K key)
+        {
+            int count = _count;
+            Remove(key);
+            return count > _count;
+        }
+
+        public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value)
+        {
+            if (null == key)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+            Node<K, V> node = GetNode(key);
+            value = node != null ? node.Value : default;
+            return node != null;
+        }
+
+        public void Add(KeyValuePair<K, V> item)
+        {
+            if (item.Key == null)
+            {
+                throw new ArgumentNullException(nameof(item.Key));
+            }
+
+            Add(item.Key, item.Value);
+        }
+
+        public void Clear()
+        {
+            _root = null;
+            _count = 0;
+        }
+
+        public bool Contains(KeyValuePair<K, V> item)
+        {
+            if (item.Key == null)
+            {
+                return false;
+            }
+
+            Node<K, V> node = GetNode(item.Key);
+            if (node != null)
+            {
+                return node.Key.Equals(item.Key) && node.Value.Equals(item.Value);
+            }
+            return false;
+        }
+
+        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
+        {
+            if (arrayIndex < 0 || array.Length - arrayIndex < this.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+            }
+
+            SortedList<K, V> list = GetKeyValues();
+
+            int offset = 0;
+
+            for (int i = arrayIndex; i < array.Length && offset < list.Count; i++)
+            {
+                array[i] = new KeyValuePair<K, V>(list.Keys[i], list.Values[i]);
+                offset++;
+            }
+        }
+
+        public bool Remove(KeyValuePair<K, V> item)
+        {
+            Node<K, V> node = GetNode(item.Key);
+
+            if (node == null)
+            {
+                return false;
+            }
+
+            if (node.Value.Equals(item.Value))
+            {
+                int count = _count;
+                Remove(item.Key);
+                return count > _count;
+            }
+
+            return false;
+        }
+
+        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
+        {
+            return GetKeyValues().GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetKeyValues().GetEnumerator();
+        }
+
+        public int Count => _count;
+
+        public ICollection<K> Keys => GetKeyValues().Keys;
+
+        public ICollection<V> Values => GetKeyValues().Values;
+
+        public bool IsReadOnly => false;
+
+        public V this[K key] 
+        { 
+            get => Get(key);
+            set => Add(key, value); 
+        }
+
+        #endregion
+        #region Private Interface Helper Methods
+
+        /// <summary>
+        /// Returns a sorted list of all the node keys / values in the tree.
+        /// </summary>
+        /// <returns>List of node keys</returns>
+        private SortedList<K, V> GetKeyValues()
+        {
+            SortedList<K, V> set = new SortedList<K, V>();
+            Queue<Node<K, V>> queue = new Queue<Node<K, V>>();
+            if (_root != null)
+            {
+                queue.Enqueue(_root);
+            }
+
+            while (queue.Count > 0)
+            {
+                Node<K, V> node = queue.Dequeue();
+                set.Add(node.Key, node.Value);
+                if (null != node.Left)
+                {
+                    queue.Enqueue(node.Left);
+                }
+                if (null != node.Right)
+                {
+                    queue.Enqueue(node.Right);
+                }
+            }
+
+            return set;
+        }
+        #endregion
+    }
+
+    /// <summary>
+    /// Represents a node in the TreeDictionary which contains a key and value of generic type K and V, respectively.
+    /// </summary>
+    /// <typeparam name="K">Key of the node</typeparam>
+    /// <typeparam name="V">Value of the node</typeparam>
+    internal class Node<K, V>
+    {
+        internal bool Color = true;
+        internal Node<K, V> Left = null;
+        internal Node<K, V> Right = null;
+        internal Node<K, V> Parent = null;
+        internal K Key;
+        internal V Value;
+
+        public Node(K key, V value, Node<K, V> parent)
+        {
+            this.Key = key;
+            this.Value = value;
+            this.Parent = parent;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 6b11a6715f..993218ce09 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -472,7 +472,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack());
 
-            if (address == MemoryManager.BadAddress)
+            if (address == MemoryManager.PteUnmapped)
             {
                 return null;
             }
@@ -533,7 +533,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());
 
-            if (address == MemoryManager.BadAddress)
+            if (address == MemoryManager.PteUnmapped)
             {
                 return null;
             }
@@ -618,7 +618,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             ulong address = _context.MemoryManager.Translate(dsState.Address.Pack());
 
-            if (address == MemoryManager.BadAddress)
+            if (address == MemoryManager.PteUnmapped)
             {
                 return null;
             }
@@ -983,7 +983,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             ulong address = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
 
-            if (address == MemoryManager.BadAddress)
+            if (address == MemoryManager.PteUnmapped)
             {
                 return null;
             }
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 53d810b960..9c7e849b82 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                 // Bad address. We can't add a texture with a invalid address
                 // to the cache.
-                if (info.Address == MemoryManager.BadAddress)
+                if (info.Address == MemoryManager.PteUnmapped)
                 {
                     return null;
                 }
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 568133ca54..1d48b38ca3 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -401,7 +401,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             ulong address = _context.MemoryManager.Translate(gpuVa);
 
-            if (address == MemoryManager.BadAddress)
+            if (address == MemoryManager.PteUnmapped)
             {
                 return 0;
             }
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index 91575e205a..2990fb52a5 100644
--- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -10,10 +10,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     public class MemoryManager
     {
-        private const ulong AddressSpaceSize = 1UL << 40;
-
-        public const ulong BadAddress = ulong.MaxValue;
-
         private const int PtLvl0Bits = 14;
         private const int PtLvl1Bits = 14;
         public  const int PtPageBits = 12;
@@ -29,8 +25,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
         private const int PtLvl1Bit = PtPageBits;
 
-        private const ulong PteUnmapped = 0xffffffff_ffffffff;
-        private const ulong PteReserved = 0xffffffff_fffffffe;
+        public const ulong PteUnmapped = 0xffffffff_ffffffff;
 
         private readonly ulong[][] _pageTable;
 
@@ -136,116 +131,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return va;
         }
 
-        /// <summary>
-        /// Maps a given range of pages to an allocated GPU virtual address.
-        /// The memory is automatically allocated by the memory manager.
-        /// </summary>
-        /// <param name="pa">CPU virtual address to map into</param>
-        /// <param name="size">Size in bytes of the mapping</param>
-        /// <param name="alignment">Required alignment of the GPU virtual address in bytes</param>
-        /// <returns>GPU virtual address where the range was mapped, or an all ones mask in case of failure</returns>
-        public ulong MapAllocate(ulong pa, ulong size, ulong alignment)
-        {
-            lock (_pageTable)
-            {
-                ulong va = GetFreePosition(size, alignment);
-
-                if (va != PteUnmapped)
-                {
-                    for (ulong offset = 0; offset < size; offset += PageSize)
-                    {
-                        SetPte(va + offset, pa + offset);
-                    }
-                }
-
-                return va;
-            }
-        }
-
-        /// <summary>
-        /// Maps a given range of pages to an allocated GPU virtual address.
-        /// The memory is automatically allocated by the memory manager.
-        /// This also ensures that the mapping is always done in the first 4GB of GPU address space.
-        /// </summary>
-        /// <param name="pa">CPU virtual address to map into</param>
-        /// <param name="size">Size in bytes of the mapping</param>
-        /// <returns>GPU virtual address where the range was mapped, or an all ones mask in case of failure</returns>
-        public ulong MapLow(ulong pa, ulong size)
-        {
-            lock (_pageTable)
-            {
-                ulong va = GetFreePosition(size, 1, PageSize);
-
-                if (va != PteUnmapped && va <= uint.MaxValue && (va + size) <= uint.MaxValue)
-                {
-                    for (ulong offset = 0; offset < size; offset += PageSize)
-                    {
-                        SetPte(va + offset, pa + offset);
-                    }
-                }
-                else
-                {
-                    va = PteUnmapped;
-                }
-
-                return va;
-            }
-        }
-
-        /// <summary>
-        /// Reserves memory at a fixed GPU memory location.
-        /// This prevents the reserved region from being used for memory allocation for map.
-        /// </summary>
-        /// <param name="va">GPU virtual address to reserve</param>
-        /// <param name="size">Size in bytes of the reservation</param>
-        /// <returns>GPU virtual address of the reservation, or an all ones mask in case of failure</returns>
-        public ulong ReserveFixed(ulong va, ulong size)
-        {
-            lock (_pageTable)
-            {
-                MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
-
-                for (ulong offset = 0; offset < size; offset += PageSize)
-                {
-                    if (IsPageInUse(va + offset))
-                    {
-                        return PteUnmapped;
-                    }
-                }
-
-                for (ulong offset = 0; offset < size; offset += PageSize)
-                {
-                    SetPte(va + offset, PteReserved);
-                }
-            }
-
-            return va;
-        }
-
-        /// <summary>
-        /// Reserves memory at any GPU memory location.
-        /// </summary>
-        /// <param name="size">Size in bytes of the reservation</param>
-        /// <param name="alignment">Reservation address alignment in bytes</param>
-        /// <returns>GPU virtual address of the reservation, or an all ones mask in case of failure</returns>
-        public ulong Reserve(ulong size, ulong alignment)
-        {
-            lock (_pageTable)
-            {
-                ulong address = GetFreePosition(size, alignment);
-
-                if (address != PteUnmapped)
-                {
-                    for (ulong offset = 0; offset < size; offset += PageSize)
-                    {
-                        SetPte(address + offset, PteReserved);
-                    }
-                }
-
-                return address;
-            }
-        }
-
         /// <summary>
         /// Frees memory that was previously allocated by a map or reserved.
         /// </summary>
@@ -265,55 +150,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             }
         }
 
-        /// <summary>
-        /// Gets the address of an unused (free) region of the specified size.
-        /// </summary>
-        /// <param name="size">Size of the region in bytes</param>
-        /// <param name="alignment">Required alignment of the region address in bytes</param>
-        /// <param name="start">Start address of the search on the address space</param>
-        /// <returns>GPU virtual address of the allocation, or an all ones mask in case of failure</returns>
-        private ulong GetFreePosition(ulong size, ulong alignment = 1, ulong start = 1UL << 32)
-        {
-            // Note: Address 0 is not considered valid by the driver,
-            // when 0 is returned it's considered a mapping error.
-            ulong address  = start;
-            ulong freeSize = 0;
-
-            if (alignment == 0)
-            {
-                alignment = 1;
-            }
-
-            alignment = (alignment + PageMask) & ~PageMask;
-
-            while (address + freeSize < AddressSpaceSize)
-            {
-                if (!IsPageInUse(address + freeSize))
-                {
-                    freeSize += PageSize;
-
-                    if (freeSize >= size)
-                    {
-                        return address;
-                    }
-                }
-                else
-                {
-                    address += freeSize + PageSize;
-                    freeSize = 0;
-
-                    ulong remainder = address % alignment;
-
-                    if (remainder != 0)
-                    {
-                        address = (address - remainder) + alignment;
-                    }
-                }
-            }
-
-            return PteUnmapped;
-        }
-
         /// <summary>
         /// Checks if a given page is mapped.
         /// </summary>
@@ -333,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             ulong baseAddress = GetPte(gpuVa);
 
-            if (baseAddress == PteUnmapped || baseAddress == PteReserved)
+            if (baseAddress == PteUnmapped)
             {
                 return PteUnmapped;
             }
@@ -341,29 +177,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return baseAddress + (gpuVa & PageMask);
         }
 
-        /// <summary>
-        /// Checks if a given memory page is mapped or reserved.
-        /// </summary>
-        /// <param name="gpuVa">GPU virtual address of the page</param>
-        /// <returns>True if the page is mapped or reserved, false otherwise</returns>
-        private bool IsPageInUse(ulong gpuVa)
-        {
-            if (gpuVa >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0)
-            {
-                return false;
-            }
-
-            ulong l0 = (gpuVa >> PtLvl0Bit) & PtLvl0Mask;
-            ulong l1 = (gpuVa >> PtLvl1Bit) & PtLvl1Mask;
-
-            if (_pageTable[l0] == null)
-            {
-                return false;
-            }
-
-            return _pageTable[l0][l1] != PteUnmapped;
-        }
-
         /// <summary>
         /// Gets the Page Table entry for a given GPU virtual address.
         /// </summary>
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
index 6c49fd5ca1..fb97322930 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
@@ -1,4 +1,5 @@
-using Ryujinx.Common.Logging;
+using Ryujinx.Common.Collections;
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
@@ -12,8 +13,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
     class NvHostAsGpuDeviceFile : NvDeviceFile
     {
         private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
+        private NvMemoryAllocator _memoryAllocator;
 
-        public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { }
+        public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
+        { 
+            _memoryAllocator = context.Device.MemoryAllocator; 
+        }
 
         public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
         {
@@ -92,11 +97,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
                 // the Offset field holds the alignment size instead.
                 if ((arguments.Flags & AddressSpaceFlags.FixedOffset) != 0)
                 {
-                    arguments.Offset = (long)addressSpaceContext.Gmm.ReserveFixed((ulong)arguments.Offset, size);
+                    bool regionInUse = _memoryAllocator.IsRegionInUse((ulong)arguments.Offset, size, out ulong freeAddressStartPosition);
+                    ulong address;
+
+                    if (!regionInUse)
+                    {
+                        _memoryAllocator.AllocateRange((ulong)arguments.Offset, size, freeAddressStartPosition);
+                        address = freeAddressStartPosition;
+                    }
+                    else
+                    {
+                        address = NvMemoryAllocator.PteUnmapped;
+                    }
+
+                    arguments.Offset = (long)address;
                 }
                 else
                 {
-                    arguments.Offset = (long)addressSpaceContext.Gmm.Reserve((ulong)size, (ulong)arguments.Offset);
+                    ulong address = _memoryAllocator.GetFreeAddress((ulong)size, out ulong freeAddressStartPosition, (ulong)arguments.Offset);
+                    if (address != NvMemoryAllocator.PteUnmapped)
+                    {
+                        _memoryAllocator.AllocateRange(address, (ulong)size, freeAddressStartPosition);
+                    }
+
+                    arguments.Offset = unchecked((long)address);
                 }
 
                 if (arguments.Offset < 0)
@@ -128,6 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
 
                 if (addressSpaceContext.RemoveReservation(arguments.Offset))
                 {
+                    _memoryAllocator.DeallocateRange((ulong)arguments.Offset, size);
                     addressSpaceContext.Gmm.Free((ulong)arguments.Offset, size);
                 }
                 else
@@ -152,6 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
                 {
                     if (size != 0)
                     {
+                        _memoryAllocator.DeallocateRange((ulong)arguments.Offset, (ulong)size);
                         addressSpaceContext.Gmm.Free((ulong)arguments.Offset, (ulong)size);
                     }
                 }
@@ -252,7 +278,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
                 }
                 else
                 {
-                    arguments.Offset = (long)addressSpaceContext.Gmm.MapAllocate((ulong)physicalAddress, (ulong)size, pageSize);
+                    ulong va = _memoryAllocator.GetFreeAddress((ulong)size, out ulong freeAddressStartPosition, (ulong) pageSize);
+                    if (va != NvMemoryAllocator.PteUnmapped)
+                    {
+                        _memoryAllocator.AllocateRange(va, (ulong)size, freeAddressStartPosition);
+                    }
+                    arguments.Offset = (long)addressSpaceContext.Gmm.Map((ulong)physicalAddress, va, (ulong)size);
                 }
 
                 if (arguments.Offset < 0)
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
index d675ffc701..ca20aab505 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
@@ -1,4 +1,5 @@
-using Ryujinx.Common.Logging;
+using Ryujinx.Common.Collections;
+using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
@@ -23,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
         private readonly Switch _device;
 
         private readonly IVirtualMemoryManager _memory;
+        private NvMemoryAllocator _memoryAllocator;
 
         public enum ResourcePolicy
         {
@@ -45,6 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
             _timeout       = 3000;
             _submitTimeout = 0;
             _timeslice     = 0;
+            _memoryAllocator = _device.MemoryAllocator;
 
             ChannelSyncpoints = new uint[MaxModuleSyncpoint];
 
@@ -245,7 +248,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
                 {
                     if (map.DmaMapAddress == 0)
                     {
-                        map.DmaMapAddress = (long)gmm.MapLow((ulong)map.Address, (uint)map.Size);
+                        ulong va = _memoryAllocator.GetFreeAddress((ulong) map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
+
+                        if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
+                        {
+                            _memoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
+                            map.DmaMapAddress = (long)gmm.Map((ulong)map.Address, va, (uint)map.Size);
+                        }
+                        else
+                        {
+                            map.DmaMapAddress = unchecked((long)NvMemoryAllocator.PteUnmapped);
+                        }
                     }
 
                     commandBufferEntry.MapAddress = (int)map.DmaMapAddress;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
new file mode 100644
index 0000000000..4662660818
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
@@ -0,0 +1,282 @@
+using Ryujinx.Common.Collections;
+using System.Collections.Generic;
+using Ryujinx.Common;
+using System;
+using Ryujinx.Graphics.Gpu.Memory;
+
+namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
+{
+    class NvMemoryAllocator
+    {
+        private const ulong AddressSpaceSize = 1UL << 40;
+
+        private const ulong DefaultStart = 1UL << 32;
+        private const ulong InvalidAddress = 0;
+
+        private const ulong PageSize = MemoryManager.PageSize;
+        private const ulong PageMask = MemoryManager.PageMask;
+
+        public const ulong PteUnmapped = MemoryManager.PteUnmapped;
+
+        // Key   --> Start Address of Region
+        // Value --> End Address of Region
+        private readonly TreeDictionary<ulong, ulong> _tree = new TreeDictionary<ulong, ulong>();
+
+        private readonly Dictionary<ulong, LinkedListNode<ulong>> _dictionary = new Dictionary<ulong, LinkedListNode<ulong>>();
+        private readonly LinkedList<ulong> _list = new LinkedList<ulong>();
+
+        public NvMemoryAllocator()
+        {
+            _tree.Add(PageSize, PageSize + AddressSpaceSize);
+            LinkedListNode<ulong> node = _list.AddFirst(PageSize);
+            _dictionary[PageSize] = node;
+        }
+
+        /// <summary>
+        /// Marks a range of memory as consumed by removing it from the tree.
+        /// This function will split memory regions if there is available space.
+        /// </summary>
+        /// <param name="va">Virtual address at which to allocate</param>
+        /// <param name="size">Size of the allocation in bytes</param>
+        /// <param name="referenceAddress">Reference to the address of memory where the allocation can take place</param>
+        #region Memory Allocation
+        public void AllocateRange(ulong va, ulong size, ulong referenceAddress = InvalidAddress)
+        {
+            lock (_tree)
+            {
+                if (referenceAddress != InvalidAddress)
+                {
+                    ulong endAddress = va + size;
+                    ulong referenceEndAddress = _tree.Get(referenceAddress);
+                    if (va >= referenceAddress)
+                    {
+                        // Need Left Node
+                        if (va > referenceAddress)
+                        {
+                            ulong leftEndAddress = va;
+
+                            // Overwrite existing block with its new smaller range.
+                            _tree.Add(referenceAddress, leftEndAddress);
+                        }
+                        else
+                        {
+                            // We need to get rid of the large chunk.
+                            _tree.Remove(referenceAddress);
+                        }
+
+                        ulong rightSize = referenceEndAddress - endAddress;
+                        // If leftover space, create a right node.
+                        if (rightSize > 0)
+                        {
+                            _tree.Add(endAddress, referenceEndAddress);
+
+                            LinkedListNode<ulong> node = _list.AddAfter(_dictionary[referenceAddress], endAddress);
+                            _dictionary[endAddress] = node;
+                        }
+
+                        if (va == referenceAddress)
+                        {
+                            _list.Remove(_dictionary[referenceAddress]);
+                            _dictionary.Remove(referenceAddress);
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Marks a range of memory as free by adding it to the tree.
+        /// This function will automatically compact the tree when it determines there are multiple ranges of free memory adjacent to each other.
+        /// </summary>
+        /// <param name="va">Virtual address at which to deallocate</param>
+        /// <param name="size">Size of the allocation in bytes</param>
+        public void DeallocateRange(ulong va, ulong size)
+        {
+            lock (_tree)
+            {
+                ulong freeAddressStartPosition = _tree.Floor(va);
+                if (freeAddressStartPosition != InvalidAddress)
+                {
+                    LinkedListNode<ulong> node = _dictionary[freeAddressStartPosition];
+                    ulong targetPrevAddress = _dictionary[freeAddressStartPosition].Previous != null ? _dictionary[_dictionary[freeAddressStartPosition].Previous.Value].Value : InvalidAddress;
+                    ulong targetNextAddress = _dictionary[freeAddressStartPosition].Next != null ? _dictionary[_dictionary[freeAddressStartPosition].Next.Value].Value : InvalidAddress;
+                    ulong expandedStart = va;
+                    ulong expandedEnd = va + size;
+
+                    while (targetPrevAddress != InvalidAddress)
+                    {
+                        ulong prevAddress = targetPrevAddress;
+                        ulong prevEndAddress = _tree.Get(targetPrevAddress);
+                        if (prevEndAddress >= expandedStart)
+                        {
+                            expandedStart = targetPrevAddress;
+                            LinkedListNode<ulong> prevPtr = _dictionary[prevAddress];
+                            if (prevPtr.Previous != null)
+                            {
+                                targetPrevAddress = prevPtr.Previous.Value;
+                            }
+                            else
+                            {
+                                targetPrevAddress = InvalidAddress;
+                            }
+                            node = node.Previous;
+                            _tree.Remove(prevAddress);
+                            _list.Remove(_dictionary[prevAddress]);
+                            _dictionary.Remove(prevAddress);
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+
+                    while (targetNextAddress != InvalidAddress)
+                    {
+                        ulong nextAddress = targetNextAddress;
+                        ulong nextEndAddress = _tree.Get(targetNextAddress);
+                        if (nextAddress <= expandedEnd)
+                        {
+                            expandedEnd = Math.Max(expandedEnd, nextEndAddress);
+                            LinkedListNode<ulong> nextPtr = _dictionary[nextAddress];
+                            if (nextPtr.Next != null)
+                            {
+                                targetNextAddress = nextPtr.Next.Value;
+                            }
+                            else
+                            {
+                                targetNextAddress = InvalidAddress;
+                            }
+                            _tree.Remove(nextAddress);
+                            _list.Remove(_dictionary[nextAddress]);
+                            _dictionary.Remove(nextAddress);
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+                    _tree.Add(expandedStart, expandedEnd);
+                    LinkedListNode<ulong> nodePtr = _list.AddAfter(node, expandedStart);
+                    _dictionary[expandedStart] = nodePtr;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the address of an unused (free) region of the specified size.
+        /// </summary>
+        /// <param name="size">Size of the region in bytes</param>
+        /// <param name="freeAddressStartPosition">Position at which memory can be allocated</param>
+        /// <param name="alignment">Required alignment of the region address in bytes</param>
+        /// <param name="start">Start address of the search on the address space</param>
+        /// <returns>GPU virtual address of the allocation, or an all ones mask in case of failure</returns>
+        public ulong GetFreeAddress(ulong size, out ulong freeAddressStartPosition, ulong alignment = 1, ulong start = DefaultStart)
+        {
+            // Note: Address 0 is not considered valid by the driver,
+            // when 0 is returned it's considered a mapping error.
+            lock (_tree)
+            {
+                ulong address = start;
+
+                if (alignment == 0)
+                {
+                    alignment = 1;
+                }
+
+                alignment = (alignment + PageMask) & ~PageMask;
+                if (address < AddressSpaceSize)
+                {
+                    bool completedFirstPass = false;
+                    ulong targetAddress;
+                    if(start == DefaultStart)
+                    {
+                        targetAddress = _list.Last.Value;
+                    }
+                    else
+                    {
+                        targetAddress = _tree.Floor(address);
+                        if(targetAddress == InvalidAddress)
+                        {
+                            targetAddress = _tree.Ceiling(address);
+                        }
+                    }
+                    while (address < AddressSpaceSize)
+                    {
+                        if (targetAddress != InvalidAddress)
+                        {
+                            if (address >= targetAddress)
+                            {
+                                if (address + size <= _tree.Get(targetAddress))
+                                {
+                                    freeAddressStartPosition = targetAddress;
+                                    return address;
+                                }
+                                else
+                                {
+                                    LinkedListNode<ulong> nextPtr = _dictionary[targetAddress];
+                                    if (nextPtr.Next != null)
+                                    {
+                                        targetAddress = nextPtr.Next.Value;
+                                    }
+                                    else
+                                    {
+                                        if (completedFirstPass)
+                                        {
+                                            break;
+                                        }
+                                        else
+                                        {
+                                            completedFirstPass = true;
+                                            address = start;
+                                            targetAddress = _tree.Floor(address);
+                                        }
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                address += PageSize * (targetAddress / PageSize - (address / PageSize));
+
+                                ulong remainder = address % alignment;
+
+                                if (remainder != 0)
+                                {
+                                    address = (address - remainder) + alignment;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+                }
+                freeAddressStartPosition = InvalidAddress;
+            }
+
+            return PteUnmapped;
+        }
+
+        /// <summary>
+        /// Checks if a given memory region is mapped or reserved.
+        /// </summary>
+        /// <param name="gpuVa">GPU virtual address of the page</param>
+        /// <param name="size">Size of the allocation in bytes</param>
+        /// <param name="freeAddressStartPosition">Nearest lower address that memory can be allocated</param>
+        /// <returns>True if the page is mapped or reserved, false otherwise</returns>
+        public bool IsRegionInUse(ulong gpuVa, ulong size, out ulong freeAddressStartPosition)
+        {
+            lock (_tree)
+            {
+                ulong floorAddress = _tree.Floor(gpuVa);
+                freeAddressStartPosition = floorAddress;
+                if (floorAddress != InvalidAddress)
+                {
+                    return !(gpuVa >= floorAddress && ((gpuVa + size) < _tree.Get(floorAddress)));
+                }
+            }
+            return true;
+        }
+        #endregion
+    }
+}
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index d54c64e109..6014ccff92 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -12,6 +12,7 @@ using Ryujinx.HLE.HOS;
 using Ryujinx.HLE.HOS.Services;
 using Ryujinx.HLE.HOS.Services.Apm;
 using Ryujinx.HLE.HOS.Services.Hid;
+using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.Memory;
 using System;
@@ -26,6 +27,8 @@ namespace Ryujinx.HLE
 
         public GpuContext Gpu { get; private set; }
 
+        internal NvMemoryAllocator MemoryAllocator { get; private set; }
+
         internal Host1xDevice Host1x { get; }
 
         public VirtualFileSystem FileSystem { get; private set; }
@@ -69,6 +72,8 @@ namespace Ryujinx.HLE
 
             Gpu = new GpuContext(renderer);
 
+            MemoryAllocator = new NvMemoryAllocator();
+
             Host1x = new Host1xDevice(Gpu.Synchronization);
             var nvdec = new NvdecDevice(Gpu.MemoryManager);
             var vic = new VicDevice(Gpu.MemoryManager);
diff --git a/Ryujinx.Tests/TreeDictionaryTests.cs b/Ryujinx.Tests/TreeDictionaryTests.cs
new file mode 100644
index 0000000000..610c2f6e02
--- /dev/null
+++ b/Ryujinx.Tests/TreeDictionaryTests.cs
@@ -0,0 +1,244 @@
+using NUnit.Framework;
+using Ryujinx.Common.Collections;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Tests.Collections
+{
+    class TreeDictionaryTests
+    {
+        [Test]
+        public void EnsureAddIntegrity()
+        {
+            TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>();
+
+            Assert.AreEqual(dictionary.Count, 0);
+
+            dictionary.Add(2, 7);
+            dictionary.Add(1, 4);
+            dictionary.Add(10, 2);
+            dictionary.Add(4, 1);
+            dictionary.Add(3, 2);
+            dictionary.Add(11, 2);
+            dictionary.Add(5, 2);
+
+            Assert.AreEqual(dictionary.Count, 7);
+
+            List<KeyValuePair<int, int>> list = dictionary.AsLevelOrderList();
+
+            /*
+             *  Tree Should Look as Follows After Rotations
+             *  
+             *        2
+             *    1        4
+             *           3    10
+             *              5    11
+             *  
+             */
+            
+            Assert.AreEqual(list.Count, dictionary.Count);
+            Assert.AreEqual(list[0].Key, 2);
+            Assert.AreEqual(list[1].Key, 1);
+            Assert.AreEqual(list[2].Key, 4);
+            Assert.AreEqual(list[3].Key, 3);
+            Assert.AreEqual(list[4].Key, 10);
+            Assert.AreEqual(list[5].Key, 5);
+            Assert.AreEqual(list[6].Key, 11);
+        }
+
+        [Test]
+        public void EnsureRemoveIntegrity()
+        {
+            TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>();
+
+            Assert.AreEqual(dictionary.Count, 0);
+
+            dictionary.Add(2, 7);
+            dictionary.Add(1, 4);
+            dictionary.Add(10, 2);
+            dictionary.Add(4, 1);
+            dictionary.Add(3, 2);
+            dictionary.Add(11, 2);
+            dictionary.Add(5, 2);
+            dictionary.Add(7, 2);
+            dictionary.Add(9, 2);
+            dictionary.Add(8, 2);
+            dictionary.Add(13, 2);
+            dictionary.Add(24, 2);
+            dictionary.Add(6, 2);
+            Assert.AreEqual(dictionary.Count, 13);
+
+            List<KeyValuePair<int, int>> list = dictionary.AsLevelOrderList();
+
+            /*
+             *  Tree Should Look as Follows After Rotations
+             *  
+             *              4
+             *      2               10
+             *  1      3       7         13
+             *              5      9  11    24
+             *                6  8 
+             */
+
+            foreach (KeyValuePair<int, int> node in list)
+            {
+                Console.WriteLine($"{node.Key} -> {node.Value}");
+            }
+            Assert.AreEqual(list.Count, dictionary.Count);
+            Assert.AreEqual(list[0].Key, 4);
+            Assert.AreEqual(list[1].Key, 2);
+            Assert.AreEqual(list[2].Key, 10);
+            Assert.AreEqual(list[3].Key, 1);
+            Assert.AreEqual(list[4].Key, 3);
+            Assert.AreEqual(list[5].Key, 7);
+            Assert.AreEqual(list[6].Key, 13);
+            Assert.AreEqual(list[7].Key, 5);
+            Assert.AreEqual(list[8].Key, 9);
+            Assert.AreEqual(list[9].Key, 11);
+            Assert.AreEqual(list[10].Key, 24);
+            Assert.AreEqual(list[11].Key, 6);
+            Assert.AreEqual(list[12].Key, 8);
+
+            list.Clear();
+
+            dictionary.Remove(7);
+
+            /*
+             *  Tree Should Look as Follows After Removal
+             *  
+             *              4
+             *      2               10
+             *  1      3       6         13
+             *              5      9  11    24
+             *                  8 
+             */
+
+            list = dictionary.AsLevelOrderList();
+            foreach (KeyValuePair<int, int> node in list)
+            {
+                Console.WriteLine($"{node.Key} -> {node.Value}");
+            }
+            Assert.AreEqual(list[0].Key, 4);
+            Assert.AreEqual(list[1].Key, 2);
+            Assert.AreEqual(list[2].Key, 10);
+            Assert.AreEqual(list[3].Key, 1);
+            Assert.AreEqual(list[4].Key, 3);
+            Assert.AreEqual(list[5].Key, 6);
+            Assert.AreEqual(list[6].Key, 13);
+            Assert.AreEqual(list[7].Key, 5);
+            Assert.AreEqual(list[8].Key, 9);
+            Assert.AreEqual(list[9].Key, 11);
+            Assert.AreEqual(list[10].Key, 24);
+            Assert.AreEqual(list[11].Key, 8);
+
+            list.Clear();
+
+            dictionary.Remove(10);
+
+            list = dictionary.AsLevelOrderList();
+            /*
+             *  Tree Should Look as Follows After Removal
+             *  
+             *              4
+             *      2               9
+             *  1      3       6         13
+             *              5      8  11    24
+             *                   
+             */
+            foreach (KeyValuePair<int, int> node in list)
+            {
+                Console.WriteLine($"{node.Key} -> {node.Value}");
+            }
+            Assert.AreEqual(list[0].Key, 4);
+            Assert.AreEqual(list[1].Key, 2);
+            Assert.AreEqual(list[2].Key, 9);
+            Assert.AreEqual(list[3].Key, 1);
+            Assert.AreEqual(list[4].Key, 3);
+            Assert.AreEqual(list[5].Key, 6);
+            Assert.AreEqual(list[6].Key, 13);
+            Assert.AreEqual(list[7].Key, 5);
+            Assert.AreEqual(list[8].Key, 8);
+            Assert.AreEqual(list[9].Key, 11);
+            Assert.AreEqual(list[10].Key, 24);
+        }
+
+        [Test]
+        public void EnsureOverwriteIntegrity()
+        {
+            TreeDictionary<int, int> dictionary = new TreeDictionary<int, int>();
+
+            Assert.AreEqual(dictionary.Count, 0);
+
+            dictionary.Add(2, 7);
+            dictionary.Add(1, 4);
+            dictionary.Add(10, 2);
+            dictionary.Add(4, 1);
+            dictionary.Add(3, 2);
+            dictionary.Add(11, 2);
+            dictionary.Add(5, 2);
+            dictionary.Add(7, 2);
+            dictionary.Add(9, 2);
+            dictionary.Add(8, 2);
+            dictionary.Add(13, 2);
+            dictionary.Add(24, 2);
+            dictionary.Add(6, 2);
+            Assert.AreEqual(dictionary.Count, 13);
+
+            List<KeyValuePair<int, int>> list = dictionary.AsLevelOrderList();
+
+            foreach (KeyValuePair<int, int> node in list)
+            {
+                Console.WriteLine($"{node.Key} -> {node.Value}");
+            }
+
+            /*
+             *  Tree Should Look as Follows After Rotations
+             *  
+             *              4
+             *      2               10
+             *  1      3       7         13
+             *              5      9  11    24
+             *                6  8 
+             */
+
+            Assert.AreEqual(list.Count, dictionary.Count);
+            Assert.AreEqual(list[0].Key, 4);
+            Assert.AreEqual(list[1].Key, 2);
+            Assert.AreEqual(list[2].Key, 10);
+            Assert.AreEqual(list[3].Key, 1);
+            Assert.AreEqual(list[4].Key, 3);
+            Assert.AreEqual(list[5].Key, 7);
+            Assert.AreEqual(list[6].Key, 13);
+            Assert.AreEqual(list[7].Key, 5);
+            Assert.AreEqual(list[8].Key, 9);
+            Assert.AreEqual(list[9].Key, 11);
+            Assert.AreEqual(list[10].Key, 24);
+            Assert.AreEqual(list[11].Key, 6);
+            Assert.AreEqual(list[12].Key, 8);
+
+            Assert.AreEqual(list[4].Value, 2);
+
+            dictionary.Add(3, 4);
+
+            list = dictionary.AsLevelOrderList();
+
+            Assert.AreEqual(list[4].Value, 4);
+
+
+            // Assure that none of the nodes locations have been modified.
+            Assert.AreEqual(list[0].Key, 4);
+            Assert.AreEqual(list[1].Key, 2);
+            Assert.AreEqual(list[2].Key, 10);
+            Assert.AreEqual(list[3].Key, 1);
+            Assert.AreEqual(list[4].Key, 3);
+            Assert.AreEqual(list[5].Key, 7);
+            Assert.AreEqual(list[6].Key, 13);
+            Assert.AreEqual(list[7].Key, 5);
+            Assert.AreEqual(list[8].Key, 9);
+            Assert.AreEqual(list[9].Key, 11);
+            Assert.AreEqual(list[10].Key, 24);
+            Assert.AreEqual(list[11].Key, 6);
+            Assert.AreEqual(list[12].Key, 8);
+        }
+    }
+}