From fff6155bc3a24016496d1290944b8f111a367c9a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 23 Apr 2023 11:58:43 +0200 Subject: [PATCH] Tests: Add memory tracker tests. --- src/tests/CMakeLists.txt | 2 +- src/tests/video_core/buffer_base.cpp | 549 ------------------------ src/tests/video_core/memory_tracker.cpp | 547 +++++++++++++++++++++++ 3 files changed, 548 insertions(+), 550 deletions(-) delete mode 100644 src/tests/video_core/buffer_base.cpp create mode 100644 src/tests/video_core/memory_tracker.cpp diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 39b774c98..1e158f375 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -15,7 +15,7 @@ add_executable(tests core/core_timing.cpp core/internal_network/network.cpp precompiled_headers.h - video_core/buffer_base.cpp + video_core/memory_tracker.cpp input_common/calibration_configuration_job.cpp ) diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp deleted file mode 100644 index 734dbf4b6..000000000 --- a/src/tests/video_core/buffer_base.cpp +++ /dev/null @@ -1,549 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include - -#include "common/alignment.h" -#include "common/common_types.h" -#include "video_core/buffer_cache/buffer_base.h" - -namespace { -using VideoCommon::BufferBase; -using Range = std::pair; - -constexpr u64 PAGE = 4096; -constexpr u64 WORD = 4096 * 64; - -constexpr VAddr c = 0x1328914000; - -class RasterizerInterface { -public: - void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { - const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS}; - const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >> - Core::Memory::YUZU_PAGEBITS}; - for (u64 page = page_start; page < page_end; ++page) { - int& value = page_table[page]; - value += delta; - if (value < 0) { - throw std::logic_error{"negative page"}; - } - if (value == 0) { - page_table.erase(page); - } - } - } - - [[nodiscard]] int Count(VAddr addr) const noexcept { - const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS); - return it == page_table.end() ? 0 : it->second; - } - - [[nodiscard]] unsigned Count() const noexcept { - unsigned count = 0; - for (const auto& [index, value] : page_table) { - count += value; - } - return count; - } - -private: - std::unordered_map page_table; -}; -} // Anonymous namespace - -TEST_CASE("BufferBase: Small buffer", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - REQUIRE(rasterizer.Count() == 0); - buffer.UnmarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == WORD / PAGE); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{0, 0}); - - buffer.MarkRegionAsCpuModified(c + PAGE, 1); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{PAGE * 1, PAGE * 2}); -} - -TEST_CASE("BufferBase: Large buffer", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 32); - buffer.UnmarkRegionAsCpuModified(c, WORD * 32); - buffer.MarkRegionAsCpuModified(c + 4096, WORD * 4); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD + PAGE * 2) == Range{PAGE, WORD + PAGE * 2}); - REQUIRE(buffer.ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) == Range{PAGE * 2, PAGE * 8}); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 4 + PAGE}); - REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 4, PAGE) == Range{WORD * 4, WORD * 4 + PAGE}); - REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) == - Range{WORD * 3 + PAGE * 63, WORD * 4}); - - buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE); - buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); - REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) == - Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 9}); - - buffer.UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); - REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) == - Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 7}); - - buffer.MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 32}); - - buffer.UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE); - buffer.UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE); - - buffer.UnmarkRegionAsCpuModified(c, WORD * 32); - REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{0, 0}); -} - -TEST_CASE("BufferBase: Rasterizer counting", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, PAGE * 2); - REQUIRE(rasterizer.Count() == 0); - buffer.UnmarkRegionAsCpuModified(c, PAGE); - REQUIRE(rasterizer.Count() == 1); - buffer.MarkRegionAsCpuModified(c, PAGE * 2); - REQUIRE(rasterizer.Count() == 0); - buffer.UnmarkRegionAsCpuModified(c, PAGE); - buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE); - REQUIRE(rasterizer.Count() == 2); - buffer.MarkRegionAsCpuModified(c, PAGE * 2); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Basic range", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.MarkRegionAsCpuModified(c, PAGE); - int num = 0; - buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == 0U); - REQUIRE(size == PAGE); - ++num; - }); - REQUIRE(num == 1U); -} - -TEST_CASE("BufferBase: Border upload", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 2); - buffer.UnmarkRegionAsCpuModified(c, WORD * 2); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE * 2); - }); -} - -TEST_CASE("BufferBase: Border upload range", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 2); - buffer.UnmarkRegionAsCpuModified(c, WORD * 2); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE * 2); - }); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE); - }); - buffer.ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) { - REQUIRE(offset == WORD); - REQUIRE(size == PAGE); - }); -} - -TEST_CASE("BufferBase: Border upload partial range", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 2); - buffer.UnmarkRegionAsCpuModified(c, WORD * 2); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE * 2); - }); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE); - }); - buffer.ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) { - REQUIRE(offset == WORD); - REQUIRE(size == PAGE); - }); -} - -TEST_CASE("BufferBase: Partial word uploads", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, 0x9d000); - int num = 0; - buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == 0U); - REQUIRE(size == WORD); - ++num; - }); - REQUIRE(num == 1); - buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == WORD); - REQUIRE(size == WORD); - ++num; - }); - REQUIRE(num == 2); - buffer.ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) { - REQUIRE(offset == WORD * 2); - REQUIRE(size == PAGE * 0x1d); - ++num; - }); - REQUIRE(num == 3); -} - -TEST_CASE("BufferBase: Partial page upload", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - int num = 0; - buffer.MarkRegionAsCpuModified(c + PAGE * 2, PAGE); - buffer.MarkRegionAsCpuModified(c + PAGE * 9, PAGE); - buffer.ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 2); - REQUIRE(size == PAGE); - ++num; - }); - REQUIRE(num == 1); - buffer.ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 9); - REQUIRE(size == PAGE); - ++num; - }); - REQUIRE(num == 2); -} - -TEST_CASE("BufferBase: Partial page upload with multiple words on the right") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 8); - buffer.UnmarkRegionAsCpuModified(c, WORD * 8); - buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); - int num = 0; - buffer.ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 13); - REQUIRE(size == WORD * 7 - PAGE * 3); - ++num; - }); - REQUIRE(num == 1); - buffer.ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) { - REQUIRE(offset == WORD * 7 + PAGE * 10); - REQUIRE(size == PAGE * 3); - ++num; - }); - REQUIRE(num == 2); -} - -TEST_CASE("BufferBase: Partial page upload with multiple words on the left", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 8); - buffer.UnmarkRegionAsCpuModified(c, WORD * 8); - buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); - int num = 0; - buffer.ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 16); - REQUIRE(size == WORD * 7 - PAGE * 3); - ++num; - }); - REQUIRE(num == 1); - buffer.ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 13); - REQUIRE(size == PAGE * 3); - ++num; - }); - REQUIRE(num == 2); -} - -TEST_CASE("BufferBase: Partial page upload with multiple words in the middle", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 8); - buffer.UnmarkRegionAsCpuModified(c, WORD * 8); - buffer.MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140); - int num = 0; - buffer.ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 16); - REQUIRE(size == WORD); - ++num; - }); - REQUIRE(num == 1); - buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { - REQUIRE(offset == PAGE * 13); - REQUIRE(size == PAGE * 3); - ++num; - }); - REQUIRE(num == 2); - buffer.ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) { - REQUIRE(offset == WORD + PAGE * 16); - REQUIRE(size == PAGE * 73); - ++num; - }); - REQUIRE(num == 3); -} - -TEST_CASE("BufferBase: Empty right bits", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 2048); - buffer.UnmarkRegionAsCpuModified(c, WORD * 2048); - buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); - buffer.ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) { - REQUIRE(offset == WORD - PAGE); - REQUIRE(size == PAGE * 2); - }); -} - -TEST_CASE("BufferBase: Out of bound ranges 1", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.MarkRegionAsCpuModified(c, PAGE); - int num = 0; - buffer.ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; }); - buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; }); - buffer.ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; }); - REQUIRE(num == 0); - buffer.ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; }); - REQUIRE(num == 1); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Out of bound ranges 2", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, 0x22000); - REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x22000, PAGE)); - REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x28000, PAGE)); - REQUIRE(rasterizer.Count() == 0); - REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100)); - REQUIRE(rasterizer.Count() == 1); - REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c - 0x1000, PAGE * 2)); - buffer.UnmarkRegionAsCpuModified(c - 0x3000, PAGE * 2); - buffer.UnmarkRegionAsCpuModified(c - 0x2000, PAGE * 2); - REQUIRE(rasterizer.Count() == 2); -} - -TEST_CASE("BufferBase: Out of bound ranges 3", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, 0x310720); - buffer.UnmarkRegionAsCpuModified(c, 0x310720); - REQUIRE(rasterizer.Count(c) == 1); - REQUIRE(rasterizer.Count(c + PAGE) == 1); - REQUIRE(rasterizer.Count(c + WORD) == 1); - REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1); -} - -TEST_CASE("BufferBase: Sparse regions 1", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.MarkRegionAsCpuModified(c + PAGE * 1, PAGE); - buffer.MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4); - buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable { - static constexpr std::array offsets{PAGE, PAGE * 3}; - static constexpr std::array sizes{PAGE, PAGE * 4}; - REQUIRE(offset == offsets.at(i)); - REQUIRE(size == sizes.at(i)); - ++i; - }); -} - -TEST_CASE("BufferBase: Sparse regions 2", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, 0x22000); - buffer.UnmarkRegionAsCpuModified(c, 0x22000); - REQUIRE(rasterizer.Count() == 0x22); - buffer.MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE); - buffer.MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE); - buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable { - static constexpr std::array offsets{PAGE * 0x1B, PAGE * 0x21}; - static constexpr std::array sizes{PAGE, PAGE}; - REQUIRE(offset == offsets.at(i)); - REQUIRE(size == sizes.at(i)); - ++i; - }); -} - -TEST_CASE("BufferBase: Single page modified range", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, PAGE); - REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); - buffer.UnmarkRegionAsCpuModified(c, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(c, PAGE)); -} - -TEST_CASE("BufferBase: Two page modified range", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, PAGE * 2); - REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c, PAGE * 2)); - buffer.UnmarkRegionAsCpuModified(c, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(c, PAGE)); -} - -TEST_CASE("BufferBase: Multi word modified ranges", "[video_core]") { - for (int offset = 0; offset < 4; ++offset) { - const VAddr address = c + WORD * offset; - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, address, WORD * 4); - REQUIRE(buffer.IsRegionCpuModified(address, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 48, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 56, PAGE)); - - buffer.UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE, WORD)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE)); - REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 33, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE * 2)); - REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); - - buffer.UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); - } -} - -TEST_CASE("BufferBase: Single page in large buffer", "[video_core]") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 16); - buffer.UnmarkRegionAsCpuModified(c, WORD * 16); - REQUIRE(!buffer.IsRegionCpuModified(c, WORD * 16)); - - buffer.MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE); - REQUIRE(buffer.IsRegionCpuModified(c, WORD * 16)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 10, WORD * 2)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 11, WORD * 2)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12, WORD * 2)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8)); - REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2)); -} - -TEST_CASE("BufferBase: Out of bounds region query") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 16); - REQUIRE(!buffer.IsRegionCpuModified(c - PAGE, PAGE)); - REQUIRE(!buffer.IsRegionCpuModified(c - PAGE * 2, PAGE)); - REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + WORD * 16 - PAGE, WORD * 64)); - REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, WORD * 64)); -} - -TEST_CASE("BufferBase: Wrap word regions") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD * 2); - buffer.UnmarkRegionAsCpuModified(c, WORD * 2); - buffer.MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2); - REQUIRE(buffer.IsRegionCpuModified(c, WORD * 2)); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 62, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 64, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 2)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 8)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 60, PAGE * 8)); - - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16)); - buffer.MarkRegionAsCpuModified(c + PAGE * 127, PAGE); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, PAGE)); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 126, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 126, PAGE * 2)); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 128, WORD * 16)); -} - -TEST_CASE("BufferBase: Unaligned page region query") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.MarkRegionAsCpuModified(c + 4000, 1000); - REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1000)); - REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1)); -} - -TEST_CASE("BufferBase: Cached write") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.CachedCpuWrite(c + PAGE, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.FlushCachedWrites(); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Multiple cached write") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.CachedCpuWrite(c + PAGE, PAGE); - buffer.CachedCpuWrite(c + PAGE * 3, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 3, PAGE)); - buffer.FlushCachedWrites(); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 3, PAGE)); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Cached write unmarked") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.CachedCpuWrite(c + PAGE, PAGE); - buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.FlushCachedWrites(); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Cached write iterated") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - buffer.CachedCpuWrite(c + PAGE, PAGE); - int num = 0; - buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); - REQUIRE(num == 0); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.FlushCachedWrites(); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} - -TEST_CASE("BufferBase: Cached write downloads") { - RasterizerInterface rasterizer; - BufferBase buffer(rasterizer, c, WORD); - buffer.UnmarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 64); - buffer.CachedCpuWrite(c + PAGE, PAGE); - REQUIRE(rasterizer.Count() == 63); - buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); - int num = 0; - buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); - buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); - REQUIRE(num == 0); - REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); - buffer.FlushCachedWrites(); - REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); - REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); - buffer.MarkRegionAsCpuModified(c, WORD); - REQUIRE(rasterizer.Count() == 0); -} diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp new file mode 100644 index 000000000..77d391f15 --- /dev/null +++ b/src/tests/video_core/memory_tracker.cpp @@ -0,0 +1,547 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#include + +#include "common/alignment.h" +#include "common/common_types.h" +#include "video_core/buffer_cache/memory_tracker_base.h" + +namespace { +using Range = std::pair; + +constexpr u64 PAGE = 4096; +constexpr u64 WORD = 4096 * 64; +constexpr u64 HIGH_PAGE_BITS = 22; +constexpr u64 HIGH_PAGE_SIZE = 1ULL << HIGH_PAGE_BITS; + +constexpr VAddr c = 16 * HIGH_PAGE_SIZE; + +class RasterizerInterface { +public: + void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { + const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS}; + const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >> + Core::Memory::YUZU_PAGEBITS}; + for (u64 page = page_start; page < page_end; ++page) { + int& value = page_table[page]; + value += delta; + if (value < 0) { + throw std::logic_error{"negative page"}; + } + if (value == 0) { + page_table.erase(page); + } + } + } + + [[nodiscard]] int Count(VAddr addr) const noexcept { + const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS); + return it == page_table.end() ? 0 : it->second; + } + + [[nodiscard]] unsigned Count() const noexcept { + unsigned count = 0; + for (const auto& [index, value] : page_table) { + count += value; + } + return count; + } + +private: + std::unordered_map page_table; +}; +} // Anonymous namespace + +using MemoryTracker = VideoCommon::MemoryTrackerBase; + +TEST_CASE("MemoryTracker: Small region", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE(rasterizer.Count() == 0); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == WORD / PAGE); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD) == Range{0, 0}); + + memory_track->MarkRegionAsCpuModified(c + PAGE, 1); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD) == Range{c + PAGE * 1, c + PAGE * 2}); +} + +TEST_CASE("MemoryTracker: Large region", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 32); + memory_track->MarkRegionAsCpuModified(c + 4096, WORD * 4); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD + PAGE * 2) == + Range{c + PAGE, c + WORD + PAGE * 2}); + REQUIRE(memory_track->ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) == + Range{c + PAGE * 2, c + PAGE * 8}); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{c + PAGE, c + WORD * 4 + PAGE}); + REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 4, PAGE) == + Range{c + WORD * 4, c + WORD * 4 + PAGE}); + REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) == + Range{c + WORD * 3 + PAGE * 63, c + WORD * 4}); + + memory_track->MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE); + memory_track->MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); + REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 5, WORD) == + Range{c + WORD * 5 + PAGE * 6, c + WORD * 5 + PAGE * 9}); + + memory_track->UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); + REQUIRE(memory_track->ModifiedCpuRegion(c + WORD * 5, WORD) == + Range{c + WORD * 5 + PAGE * 6, c + WORD * 5 + PAGE * 7}); + + memory_track->MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{c + PAGE, c + WORD * 32}); + + memory_track->UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE); + memory_track->UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE); + + memory_track->UnmarkRegionAsCpuModified(c, WORD * 32); + REQUIRE(memory_track->ModifiedCpuRegion(c, WORD * 32) == Range{0, 0}); +} + +TEST_CASE("MemoryTracker: Rasterizer counting", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE(rasterizer.Count() == 0); + memory_track->UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(rasterizer.Count() == 1); + memory_track->MarkRegionAsCpuModified(c, PAGE * 2); + REQUIRE(rasterizer.Count() == 0); + memory_track->UnmarkRegionAsCpuModified(c, PAGE); + memory_track->UnmarkRegionAsCpuModified(c + PAGE, PAGE); + REQUIRE(rasterizer.Count() == 2); + memory_track->MarkRegionAsCpuModified(c, PAGE * 2); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("MemoryTracker: Basic range", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->MarkRegionAsCpuModified(c, PAGE); + int num = 0; + memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 1U); +} + +TEST_CASE("MemoryTracker: Border upload", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 2); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); +} + +TEST_CASE("MemoryTracker: Border upload range", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 2); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE); + }); + memory_track->ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD); + REQUIRE(size == PAGE); + }); +} + +TEST_CASE("MemoryTracker: Border upload partial range", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 2); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE); + }); + memory_track->ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD); + REQUIRE(size == PAGE); + }); +} + +TEST_CASE("MemoryTracker: Partial word uploads", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + int num = 0; + memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 1); + memory_track->ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c + WORD); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 2); + memory_track->ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) { + REQUIRE(offset == c + WORD * 2); + REQUIRE(size == PAGE * 0x1d); + ++num; + }); + REQUIRE(num == 3); +} + +TEST_CASE("MemoryTracker: Partial page upload", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + int num = 0; + memory_track->MarkRegionAsCpuModified(c + PAGE * 2, PAGE); + memory_track->MarkRegionAsCpuModified(c + PAGE * 9, PAGE); + memory_track->ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 2); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 1); + memory_track->ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 9); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("MemoryTracker: Partial page upload with multiple words on the right") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 9); + memory_track->MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); + int num = 0; + memory_track->ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 13); + REQUIRE(size == WORD * 7 - PAGE * 3); + ++num; + }); + REQUIRE(num == 1); + memory_track->ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) { + REQUIRE(offset == c + WORD * 7 + PAGE * 10); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("MemoryTracker: Partial page upload with multiple words on the left", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 8); + memory_track->MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); + int num = 0; + memory_track->ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 16); + REQUIRE(size == WORD * 7 - PAGE * 3); + ++num; + }); + REQUIRE(num == 1); + memory_track->ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 13); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("MemoryTracker: Partial page upload with multiple words in the middle", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 8); + memory_track->MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140); + int num = 0; + memory_track->ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 16); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 1); + memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == c + PAGE * 13); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); + memory_track->ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) { + REQUIRE(offset == c + WORD + PAGE * 16); + REQUIRE(size == PAGE * 73); + ++num; + }); + REQUIRE(num == 3); +} + +TEST_CASE("MemoryTracker: Empty right bits", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 2048); + memory_track->MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + memory_track->ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) { + REQUIRE(offset == c + WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); +} + +TEST_CASE("MemoryTracker: Out of bound ranges 1", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c - WORD, 3 * WORD); + memory_track->MarkRegionAsCpuModified(c, PAGE); + REQUIRE(rasterizer.Count() == (3 * WORD - PAGE) / PAGE); + int num = 0; + memory_track->ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; }); + memory_track->ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; }); + memory_track->ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 0); + memory_track->ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 1); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 2 * WORD / PAGE); +} + +TEST_CASE("MemoryTracker: Out of bound ranges 2", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x22000, PAGE)); + REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x28000, PAGE)); + REQUIRE(rasterizer.Count() == 2); + REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100)); + REQUIRE(rasterizer.Count() == 3); + REQUIRE_NOTHROW(memory_track->UnmarkRegionAsCpuModified(c - PAGE, PAGE * 2)); + memory_track->UnmarkRegionAsCpuModified(c - PAGE * 3, PAGE * 2); + memory_track->UnmarkRegionAsCpuModified(c - PAGE * 2, PAGE * 2); + REQUIRE(rasterizer.Count() == 7); +} + +TEST_CASE("MemoryTracker: Out of bound ranges 3", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, 0x310720); + REQUIRE(rasterizer.Count(c) == 1); + REQUIRE(rasterizer.Count(c + PAGE) == 1); + REQUIRE(rasterizer.Count(c + WORD) == 1); + REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1); +} + +TEST_CASE("MemoryTracker: Sparse regions 1", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->MarkRegionAsCpuModified(c + PAGE * 1, PAGE); + memory_track->MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4); + memory_track->ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable { + static constexpr std::array offsets{c + PAGE, c + PAGE * 3}; + static constexpr std::array sizes{PAGE, PAGE * 4}; + REQUIRE(offset == offsets.at(i)); + REQUIRE(size == sizes.at(i)); + ++i; + }); +} + +TEST_CASE("MemoryTracker: Sparse regions 2", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, PAGE * 0x23); + REQUIRE(rasterizer.Count() == 0x23); + memory_track->MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE); + memory_track->MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE); + memory_track->ForEachUploadRange(c, PAGE * 0x23, [i = 0](u64 offset, u64 size) mutable { + static constexpr std::array offsets{c + PAGE * 0x1B, c + PAGE * 0x21}; + static constexpr std::array sizes{PAGE, PAGE}; + REQUIRE(offset == offsets.at(i)); + REQUIRE(size == sizes.at(i)); + ++i; + }); +} + +TEST_CASE("MemoryTracker: Single page modified range", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE(memory_track->IsRegionCpuModified(c, PAGE)); + memory_track->UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(c, PAGE)); +} + +TEST_CASE("MemoryTracker: Two page modified range", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE(memory_track->IsRegionCpuModified(c, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c, PAGE * 2)); + memory_track->UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(c, PAGE)); +} + +TEST_CASE("MemoryTracker: Multi word modified ranges", "[video_core]") { + for (int offset = 0; offset < 4; ++offset) { + const VAddr address = c + WORD * offset; + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + REQUIRE(memory_track->IsRegionCpuModified(address, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 48, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 56, PAGE)); + + memory_track->UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE, WORD)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 31, PAGE)); + REQUIRE(!memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 33, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 31, PAGE * 2)); + REQUIRE(memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); + + memory_track->UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); + } +} + +TEST_CASE("MemoryTracker: Single page in large region", "[video_core]") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 16); + REQUIRE(!memory_track->IsRegionCpuModified(c, WORD * 16)); + + memory_track->MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE); + REQUIRE(memory_track->IsRegionCpuModified(c, WORD * 16)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 10, WORD * 2)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 11, WORD * 2)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12, WORD * 2)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8)); + REQUIRE(!memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2)); + REQUIRE(memory_track->IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2)); +} + +TEST_CASE("MemoryTracker: Wrap word regions") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD * 32); + memory_track->MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2); + REQUIRE(memory_track->IsRegionCpuModified(c, WORD * 2)); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 62, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 64, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE * 2)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 63, PAGE * 8)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 60, PAGE * 8)); + + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 127, WORD * 16)); + memory_track->MarkRegionAsCpuModified(c + PAGE * 127, PAGE); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 127, WORD * 16)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 127, PAGE)); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 126, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 126, PAGE * 2)); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 128, WORD * 16)); +} + +TEST_CASE("MemoryTracker: Unaligned page region query") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->MarkRegionAsCpuModified(c + 4000, 1000); + REQUIRE(memory_track->IsRegionCpuModified(c, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + 4000, 1000)); + REQUIRE(memory_track->IsRegionCpuModified(c + 4000, 1)); +} + +TEST_CASE("MemoryTracker: Cached write") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->CachedCpuWrite(c + PAGE, c + PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->FlushCachedWrites(); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("MemoryTracker: Multiple cached write") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->CachedCpuWrite(c + PAGE, PAGE); + memory_track->CachedCpuWrite(c + PAGE * 3, PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE * 3, PAGE)); + memory_track->FlushCachedWrites(); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE * 3, PAGE)); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("MemoryTracker: Cached write unmarked") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->CachedCpuWrite(c + PAGE, PAGE); + memory_track->UnmarkRegionAsCpuModified(c + PAGE, PAGE); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->FlushCachedWrites(); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("MemoryTracker: Cached write iterated") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + memory_track->CachedCpuWrite(c + PAGE, PAGE); + int num = 0; + memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 0); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->FlushCachedWrites(); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("MemoryTracker: Cached write downloads") { + RasterizerInterface rasterizer; + std::unique_ptr memory_track(std::make_unique(rasterizer)); + memory_track->UnmarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 64); + memory_track->CachedCpuWrite(c + PAGE, PAGE); + REQUIRE(rasterizer.Count() == 63); + memory_track->MarkRegionAsGpuModified(c + PAGE, PAGE); + int num = 0; + memory_track->ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); + memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 0); + REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE)); + memory_track->FlushCachedWrites(); + REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE)); + memory_track->MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} \ No newline at end of file