From 891236124caaed34cdefac61cf90896a5b66b267 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 17 May 2020 16:56:08 -0300
Subject: [PATCH] buffer_cache: Use boost::intrusive::set for caching

Instead of using boost::icl::interval_map for caching, use
boost::intrusive::set. interval_map is intended as a container where the
keys can overlap with one another; we don't need this for caching
buffers and a std::set-like data structure that allows us to search with
lower_bound is enough.
---
 src/video_core/buffer_cache/buffer_cache.h    | 42 ++++++++++++-------
 src/video_core/buffer_cache/map_interval.h    | 32 +++++++-------
 .../renderer_opengl/gl_buffer_cache.cpp       |  1 +
 .../renderer_opengl/gl_fence_manager.cpp      |  1 +
 .../renderer_vulkan/vk_buffer_cache.cpp       |  1 +
 .../renderer_vulkan/vk_fence_manager.h        |  1 +
 6 files changed, 48 insertions(+), 30 deletions(-)

diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index eb03879c4b..fb12af9d85 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -14,9 +14,11 @@
 
 #include <boost/icl/interval_map.hpp>
 #include <boost/icl/interval_set.hpp>
+#include <boost/intrusive/set.hpp>
 #include <boost/range/iterator_range.hpp>
 
 #include "common/alignment.h"
+#include "common/assert.h"
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "core/core.h"
@@ -73,7 +75,7 @@ public:
             }
         }
 
-        auto block = GetBlock(cpu_addr, size);
+        OwnerBuffer block = GetBlock(cpu_addr, size);
         MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size);
         if (!map) {
             return {GetEmptyBuffer(size), 0};
@@ -272,16 +274,16 @@ protected:
         }
         const std::size_t size = new_map.end - new_map.start;
         new_map.is_registered = true;
-        const IntervalType interval{new_map.start, new_map.end};
         rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
         new_map.is_memory_marked = true;
         if (inherit_written) {
             MarkRegionAsWritten(new_map.start, new_map.end - 1);
             new_map.is_written = true;
         }
-        mapped_addresses.insert({interval, new_map});
-        // Temporary hack until this is replaced with boost::intrusive::rbtree
-        return const_cast<MapInterval*>(&mapped_addresses.find(interval)->second);
+        // Temporary hack, leaks memory and it's not cache local
+        MapInterval* const storage = &mapped_addresses_storage.emplace_back(new_map);
+        mapped_addresses.insert(*storage);
+        return storage;
     }
 
     void UnmarkMemory(MapInterval* map) {
@@ -304,8 +306,9 @@ protected:
         if (map->is_written) {
             UnmarkRegionAsWritten(map->start, map->end - 1);
         }
-        const IntervalType delete_interval{map->start, map->end};
-        mapped_addresses.erase(delete_interval);
+        const auto it = mapped_addresses.find(*map);
+        ASSERT(it != mapped_addresses.end());
+        mapped_addresses.erase(it);
     }
 
 private:
@@ -389,13 +392,20 @@ private:
             return {};
         }
 
-        std::vector<MapInterval*> objects;
-        const IntervalType interval{addr, addr + size};
-        for (auto& pair : boost::make_iterator_range(mapped_addresses.equal_range(interval))) {
-            objects.push_back(&pair.second);
-        }
+        std::vector<MapInterval*> result;
+        const VAddr addr_end = addr + size;
 
-        return objects;
+        auto it = mapped_addresses.lower_bound(addr);
+        if (it != mapped_addresses.begin()) {
+            --it;
+        }
+        while (it != mapped_addresses.end() && it->start < addr_end) {
+            if (it->Overlaps(addr, addr_end)) {
+                result.push_back(&*it);
+            }
+            ++it;
+        }
+        return result;
     }
 
     /// Returns a ticks counter used for tracking when cached objects were last modified
@@ -565,9 +575,9 @@ private:
     u64 buffer_offset_base = 0;
 
     using IntervalSet = boost::icl::interval_set<VAddr>;
-    using IntervalCache = boost::icl::interval_map<VAddr, MapInterval>;
-    using IntervalType = typename IntervalCache::interval_type;
-    IntervalCache mapped_addresses;
+    using IntervalType = typename IntervalSet::interval_type;
+    std::list<MapInterval> mapped_addresses_storage; // Temporary hack
+    boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>> mapped_addresses;
 
     static constexpr u64 write_page_bit = 11;
     std::unordered_map<u64, u32> written_pages;
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h
index ad4db01359..45705cccf9 100644
--- a/src/video_core/buffer_cache/map_interval.h
+++ b/src/video_core/buffer_cache/map_interval.h
@@ -4,38 +4,36 @@
 
 #pragma once
 
+#include <boost/intrusive/set_hook.hpp>
+
 #include "common/common_types.h"
 #include "video_core/gpu.h"
 
 namespace VideoCommon {
 
-struct MapInterval {
-    constexpr explicit MapInterval() noexcept = default;
+struct MapInterval : public boost::intrusive::set_base_hook<boost::intrusive::optimize_size<true>> {
+    /*implicit*/ MapInterval(VAddr start_) noexcept : start{start_} {}
 
-    constexpr explicit MapInterval(VAddr start, VAddr end, GPUVAddr gpu_addr) noexcept
-        : start{start}, end{end}, gpu_addr{gpu_addr} {}
+    explicit MapInterval(VAddr start_, VAddr end_, GPUVAddr gpu_addr_) noexcept
+        : start{start_}, end{end_}, gpu_addr{gpu_addr_} {}
 
-    constexpr bool IsInside(VAddr other_start, VAddr other_end) const noexcept {
-        return (start <= other_start && other_end <= end);
+    bool IsInside(VAddr other_start, VAddr other_end) const noexcept {
+        return start <= other_start && other_end <= end;
     }
 
-    constexpr bool operator==(const MapInterval& rhs) const noexcept {
-        return start == rhs.start && end == rhs.end;
+    bool Overlaps(VAddr other_start, VAddr other_end) const noexcept {
+        return start < other_end && other_start < end;
     }
 
-    constexpr bool operator!=(const MapInterval& rhs) const noexcept {
-        return !operator==(rhs);
-    }
-
-    constexpr void MarkAsModified(bool is_modified_, u64 ticks_) noexcept {
+    void MarkAsModified(bool is_modified_, u64 ticks_) noexcept {
         is_modified = is_modified_;
         ticks = ticks_;
     }
 
+    boost::intrusive::set_member_hook<> member_hook_;
     VAddr start = 0;
     VAddr end = 0;
     GPUVAddr gpu_addr = 0;
-    VAddr cpu_addr = 0;
     u64 ticks = 0;
     bool is_written = false;
     bool is_modified = false;
@@ -44,4 +42,10 @@ struct MapInterval {
     bool is_sync_pending = false;
 };
 
+struct MapIntervalCompare {
+    constexpr bool operator()(const MapInterval& lhs, const MapInterval& rhs) const noexcept {
+        return lhs.start < rhs.start;
+    }
+};
+
 } // namespace VideoCommon
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index d2cab50bd9..9964ea8941 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -8,6 +8,7 @@
 
 #include "common/assert.h"
 #include "common/microprofile.h"
+#include "video_core/buffer_cache/buffer_cache.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/rasterizer_interface.h"
 #include "video_core/renderer_opengl/gl_buffer_cache.h"
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 99ddcb3f85..ec5421afae 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -4,6 +4,7 @@
 
 #include "common/assert.h"
 
+#include "video_core/renderer_opengl/gl_buffer_cache.h"
 #include "video_core/renderer_opengl/gl_fence_manager.h"
 
 namespace OpenGL {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5b494da8cb..5f33d9e40e 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "core/core.h"
+#include "video_core/buffer_cache/buffer_cache.h"
 #include "video_core/renderer_vulkan/vk_buffer_cache.h"
 #include "video_core/renderer_vulkan/vk_device.h"
 #include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 04d07fe6af..043fe79471 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "video_core/fence_manager.h"
+#include "video_core/renderer_vulkan/vk_buffer_cache.h"
 #include "video_core/renderer_vulkan/wrapper.h"
 
 namespace Core {