From 22f3a7e94ce0e325a8c3dfeb0814c4b82a42649d Mon Sep 17 00:00:00 2001
From: tfarley <tfarleygithub@gmail.com>
Date: Sat, 16 Apr 2016 18:57:57 -0400
Subject: HWRasterizer: Texture forwarding

---
 src/core/memory.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

(limited to 'src/core/memory.cpp')

diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 7de5bd15db..ee9b69f814 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -15,6 +15,9 @@
 #include "core/memory_setup.h"
 #include "core/mmio.h"
 
+#include "video_core/renderer_base.h"
+#include "video_core/video_core.h"
+
 namespace Memory {
 
 enum class PageType {
@@ -22,8 +25,12 @@ enum class PageType {
     Unmapped,
     /// Page is mapped to regular memory. This is the only type you can get pointers to.
     Memory,
+    /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and invalidation
+    RasterizerCachedMemory,
     /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
     Special,
+    /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and invalidation
+    RasterizerCachedSpecial,
 };
 
 struct SpecialRegion {
@@ -57,6 +64,12 @@ struct PageTable {
      * the corresponding entry in `pointers` MUST be set to null.
      */
     std::array<PageType, NUM_ENTRIES> attributes;
+
+    /**
+     * Indicates the number of externally cached resources touching a page that should be
+     * flushed before the memory is accessed
+     */
+    std::array<u8, NUM_ENTRIES> cached_res_count;
 };
 
 /// Singular page table used for the singleton process
@@ -72,8 +85,15 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
     while (base != end) {
         ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base);
 
+        // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be null here
+        if (current_page_table->attributes[base] == PageType::RasterizerCachedMemory ||
+            current_page_table->attributes[base] == PageType::RasterizerCachedSpecial) {
+            RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(base << PAGE_BITS), PAGE_SIZE);
+        }
+
         current_page_table->attributes[base] = type;
         current_page_table->pointers[base] = memory;
+        current_page_table->cached_res_count[base] = 0;
 
         base += 1;
         if (memory != nullptr)
@@ -84,6 +104,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
 void InitMemoryMap() {
     main_page_table.pointers.fill(nullptr);
     main_page_table.attributes.fill(PageType::Unmapped);
+    main_page_table.cached_res_count.fill(0);
 }
 
 void MapMemoryRegion(VAddr base, u32 size, u8* target) {
@@ -106,6 +127,28 @@ void UnmapRegion(VAddr base, u32 size) {
     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
 }
 
+/**
+ * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
+ * using a VMA from the current process
+ */
+static u8* GetPointerFromVMA(VAddr vaddr) {
+    u8* direct_pointer = nullptr;
+
+    auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second;
+    switch (vma.type) {
+    case Kernel::VMAType::AllocatedMemoryBlock:
+        direct_pointer = vma.backing_block->data() + vma.offset;
+        break;
+    case Kernel::VMAType::BackingMemory:
+        direct_pointer = vma.backing_memory;
+        break;
+    default:
+        UNREACHABLE();
+    }
+
+    return direct_pointer + (vaddr - vma.base);
+}
+
 /**
  * This function should only be called for virtual addreses with attribute `PageType::Special`.
  */
@@ -126,6 +169,7 @@ template <typename T>
 T Read(const VAddr vaddr) {
     const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
     if (page_pointer) {
+        // NOTE: Avoid adding any extra logic to this fast-path block
         T value;
         std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
         return value;
@@ -139,8 +183,22 @@ T Read(const VAddr vaddr) {
     case PageType::Memory:
         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
         break;
+    case PageType::RasterizerCachedMemory:
+    {
+        RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
+
+        T value;
+        std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
+        return value;
+    }
     case PageType::Special:
         return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
+    case PageType::RasterizerCachedSpecial:
+    {
+        RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
+
+        return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
+    }
     default:
         UNREACHABLE();
     }
@@ -153,6 +211,7 @@ template <typename T>
 void Write(const VAddr vaddr, const T data) {
     u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
     if (page_pointer) {
+        // NOTE: Avoid adding any extra logic to this fast-path block
         std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
         return;
     }
@@ -165,9 +224,23 @@ void Write(const VAddr vaddr, const T data) {
     case PageType::Memory:
         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
         break;
+    case PageType::RasterizerCachedMemory:
+    {
+        RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
+
+        std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
+        break;
+    }
     case PageType::Special:
         WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
         break;
+    case PageType::RasterizerCachedSpecial:
+    {
+        RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T));
+
+        WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
+        break;
+    }
     default:
         UNREACHABLE();
     }
@@ -179,6 +252,10 @@ u8* GetPointer(const VAddr vaddr) {
         return page_pointer + (vaddr & PAGE_MASK);
     }
 
+    if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
+        return GetPointerFromVMA(vaddr);
+    }
+
     LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
     return nullptr;
 }
@@ -187,6 +264,69 @@ u8* GetPhysicalPointer(PAddr address) {
     return GetPointer(PhysicalToVirtualAddress(address));
 }
 
+void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
+    if (start == 0) {
+        return;
+    }
+
+    u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1;
+    PAddr paddr = start;
+
+    for (unsigned i = 0; i < num_pages; ++i) {
+        VAddr vaddr = PhysicalToVirtualAddress(paddr);
+        u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS];
+        ASSERT_MSG(count_delta <= UINT8_MAX - res_count, "Rasterizer resource cache counter overflow!");
+        ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!");
+
+        // Switch page type to cached if now cached
+        if (res_count == 0) {
+            PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
+            switch (page_type) {
+            case PageType::Memory:
+                page_type = PageType::RasterizerCachedMemory;
+                current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
+                break;
+            case PageType::Special:
+                page_type = PageType::RasterizerCachedSpecial;
+                break;
+            default:
+                UNREACHABLE();
+            }
+        }
+
+        res_count += count_delta;
+
+        // Switch page type to uncached if now uncached
+        if (res_count == 0) {
+            PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
+            switch (page_type) {
+            case PageType::RasterizerCachedMemory:
+                page_type = PageType::Memory;
+                current_page_table->pointers[vaddr >> PAGE_BITS] = GetPointerFromVMA(vaddr & ~PAGE_MASK);
+                break;
+            case PageType::RasterizerCachedSpecial:
+                page_type = PageType::Special;
+                break;
+            default:
+                UNREACHABLE();
+            }
+        }
+        paddr += PAGE_SIZE;
+    }
+}
+
+void RasterizerFlushRegion(PAddr start, u32 size) {
+    if (VideoCore::g_renderer != nullptr) {
+        VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size);
+    }
+}
+
+void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) {
+    if (VideoCore::g_renderer != nullptr) {
+        VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size);
+    }
+}
+
 u8 Read8(const VAddr addr) {
     return Read<u8>(addr);
 }
-- 
cgit v1.2.3-70-g09d2