summaryrefslogtreecommitdiffstats
path: root/src/core/memory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/memory.cpp')
-rw-r--r--src/core/memory.cpp140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 7de5bd15d..ee9b69f81 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) {
@@ -107,6 +128,28 @@ void UnmapRegion(VAddr base, u32 size) {
}
/**
+ * 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`.
*/
static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
@@ -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);
}