From 8390c7dff7038b55f31388f79d67e24d4328fbfd Mon Sep 17 00:00:00 2001
From: JPikachu <jpikachu@noreply.localhost>
Date: Sun, 4 May 2025 18:31:32 +0000
Subject: [PATCH] Add rasterizer_accelerated (#79)

Credit: Antique - [Sudachi] Dev (https://sudachi.emuplace.app/)
Co-authored-by: JPikachu <james.da.rogers@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/79
Co-authored-by: JPikachu <jpikachu@noreply.localhost>
Co-committed-by: JPikachu <jpikachu@noreply.localhost>
---
 src/video_core/rasterizer_accelerated.cpp | 72 +++++++++++++++++++++++
 src/video_core/rasterizer_accelerated.h   | 49 +++++++++++++++
 2 files changed, 121 insertions(+)
 create mode 100644 src/video_core/rasterizer_accelerated.cpp
 create mode 100644 src/video_core/rasterizer_accelerated.h

diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
new file mode 100644
index 0000000000..9289cae64c
--- /dev/null
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <atomic>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/div_ceil.h"
+#include "core/memory.h"
+#include "video_core/rasterizer_accelerated.h"
+
+namespace VideoCore {
+
+using namespace Core::Memory;
+
+RasterizerAccelerated::RasterizerAccelerated(Memory& cpu_memory_)
+    : cached_pages(std::make_unique<CachedPages>()), cpu_memory{cpu_memory_} {}
+
+RasterizerAccelerated::~RasterizerAccelerated() = default;
+
+void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
+    u64 uncache_begin = 0;
+    u64 cache_begin = 0;
+    u64 uncache_bytes = 0;
+    u64 cache_bytes = 0;
+
+    std::atomic_thread_fence(std::memory_order_acquire);
+    const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
+    for (u64 page = addr >> YUZU_PAGEBITS; page != page_end; ++page) {
+        std::atomic_uint16_t& count = cached_pages->at(page >> 2).Count(page);
+
+        if (delta > 0) {
+            ASSERT_MSG(count.load(std::memory_order::relaxed) < UINT16_MAX, "Count may overflow!");
+        } else if (delta < 0) {
+            ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!");
+        } else {
+            ASSERT_MSG(false, "Delta must be non-zero!");
+        }
+
+        // Adds or subtracts 1, as count is a unsigned 8-bit value
+        count.fetch_add(static_cast<u16>(delta), std::memory_order_release);
+
+        // Assume delta is either -1 or 1
+        if (count.load(std::memory_order::relaxed) == 0) {
+            if (uncache_bytes == 0) {
+                uncache_begin = page;
+            }
+            uncache_bytes += YUZU_PAGESIZE;
+        } else if (uncache_bytes > 0) {
+            cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes,
+                                                  false);
+            uncache_bytes = 0;
+        }
+        if (count.load(std::memory_order::relaxed) == 1 && delta > 0) {
+            if (cache_bytes == 0) {
+                cache_begin = page;
+            }
+            cache_bytes += YUZU_PAGESIZE;
+        } else if (cache_bytes > 0) {
+            cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
+            cache_bytes = 0;
+        }
+    }
+    if (uncache_bytes > 0) {
+        cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, false);
+    }
+    if (cache_bytes > 0) {
+        cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
+    }
+}
+
+} // namespace VideoCore
diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h
new file mode 100644
index 0000000000..0edca5a2c2
--- /dev/null
+++ b/src/video_core/rasterizer_accelerated.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <atomic>
+
+#include "common/common_types.h"
+#include "video_core/rasterizer_interface.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace VideoCore {
+
+/// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
+class RasterizerAccelerated : public RasterizerInterface {
+public:
+    explicit RasterizerAccelerated(Core::Memory::Memory& cpu_memory_);
+    ~RasterizerAccelerated() override;
+
+    void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
+
+private:
+    class CacheEntry final {
+    public:
+        CacheEntry() = default;
+
+        std::atomic_uint16_t& Count(std::size_t page) {
+            return values[page & 3];
+        }
+
+        const std::atomic_uint16_t& Count(std::size_t page) const {
+            return values[page & 3];
+        }
+
+    private:
+        std::array<std::atomic_uint16_t, 4> values{};
+    };
+    static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
+
+    using CachedPages = std::array<CacheEntry, 0x2000000>;
+    std::unique_ptr<CachedPages> cached_pages;
+    Core::Memory::Memory& cpu_memory;
+};
+
+} // namespace VideoCore