diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4e34d8334f..8a83de9048 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -42,6 +42,9 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
     LOG_DEBUG(HW_Memory, "Mapping %p onto %016" PRIX64 "-%016" PRIX64, memory, base * PAGE_SIZE,
               (base + size) * PAGE_SIZE);
 
+    RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
+                                 FlushMode::FlushAndInvalidate);
+
     VAddr end = base + size;
     while (base != end) {
         ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %016" PRIX64, base);
@@ -293,6 +296,42 @@ u8* GetPhysicalPointer(PAddr address) {
     return target_pointer;
 }
 
+void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
+    // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
+    // null here
+    if (VideoCore::g_renderer == nullptr) {
+        return;
+    }
+
+    VAddr end = start + size;
+
+    auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
+        if (start >= region_end || end <= region_start) {
+            // No overlap with region
+            return;
+        }
+
+        VAddr overlap_start = std::max(start, region_start);
+        VAddr overlap_end = std::min(end, region_end);
+        u32 overlap_size = overlap_end - overlap_start;
+
+        auto* rasterizer = VideoCore::g_renderer->Rasterizer();
+        switch (mode) {
+        case FlushMode::Flush:
+            rasterizer->FlushRegion(region_start, overlap_size);
+            break;
+        case FlushMode::Invalidate:
+            rasterizer->InvalidateRegion(region_start, overlap_size);
+            break;
+        case FlushMode::FlushAndInvalidate:
+            rasterizer->FlushAndInvalidateRegion(region_start, overlap_size);
+            break;
+        }
+    };
+
+    CheckRegion(HEAP_VADDR, HEAP_VADDR_END);
+}
+
 u8 Read8(const VAddr addr) {
     return Read<u8>(addr);
 }
diff --git a/src/core/memory.h b/src/core/memory.h
index f406cc848a..1c72321154 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -36,7 +36,10 @@ enum class PageType : u8 {
     Unmapped,
     /// Page is mapped to regular memory. This is the only type you can get pointers to.
     Memory,
-    /// Page is mapped to a memory hook, which intercepts read and write requests.
+    /// 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,
 };
 
@@ -253,4 +256,19 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
  */
 u8* GetPhysicalPointer(PAddr address);
 
+enum class FlushMode {
+    /// Write back modified surfaces to RAM
+    Flush,
+    /// Remove region from the cache
+    Invalidate,
+    /// Write back modified surfaces to RAM, and also remove them from the cache
+    FlushAndInvalidate,
+};
+
+/**
+ * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
+ * address region.
+ */
+void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
+
 } // namespace Memory