summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/common/container_hash.cpp44
-rw-r--r--src/tests/common/range_map.cpp20
-rw-r--r--src/tests/common/ring_buffer.cpp2
-rw-r--r--src/tests/common/scratch_buffer.cpp2
-rw-r--r--src/tests/video_core/buffer_base.cpp549
-rw-r--r--src/tests/video_core/memory_tracker.cpp549
7 files changed, 607 insertions, 562 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index ae84408bc..1e158f375 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -4,6 +4,7 @@
add_executable(tests
common/bit_field.cpp
common/cityhash.cpp
+ common/container_hash.cpp
common/fibers.cpp
common/host_memory.cpp
common/param_package.cpp
@@ -14,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/common/container_hash.cpp b/src/tests/common/container_hash.cpp
new file mode 100644
index 000000000..dc45565ef
--- /dev/null
+++ b/src/tests/common/container_hash.cpp
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <catch2/catch_test_macros.hpp>
+
+#include "common/common_types.h"
+#include "common/container_hash.h"
+
+TEST_CASE("ContainerHash", "[common]") {
+ constexpr std::array<u8, 32> U8Values{
+ 114, 10, 238, 189, 199, 242, 86, 96, 53, 193, 195, 247, 249, 56, 253, 61,
+ 205, 3, 172, 4, 210, 197, 43, 72, 103, 8, 99, 89, 5, 97, 68, 196,
+ };
+ constexpr std::array<u16, 32> U16Values{
+ 61586, 49151, 3313, 11641, 31695, 54795, 46764, 20965, 23287, 14039, 19265,
+ 49093, 58932, 22518, 27139, 42825, 57417, 54237, 48057, 14586, 42813, 32994,
+ 33970, 45501, 5619, 15895, 33227, 27509, 25391, 37275, 60218, 17599,
+ };
+ constexpr std::array<u32, 32> U32Values{
+ 3838402410U, 2029146863U, 1730869921U, 985528872U, 186773874U, 2094639868U, 3324775932U,
+ 1795512424U, 2571165571U, 3256934519U, 2358691590U, 2752682538U, 1484336451U, 378124520U,
+ 3463015699U, 3395942161U, 1263211979U, 3473632889U, 3039822212U, 2068707357U, 2223837919U,
+ 1823232191U, 1583884041U, 1264393380U, 4087566993U, 3188607101U, 3933680362U, 1464520765U,
+ 1786838406U, 1311734848U, 2773642241U, 3993641692U,
+ };
+ constexpr std::array<u64, 32> U64Values{
+ 5908025796157537817ULL, 10947547850358315100ULL, 844798943576724669ULL,
+ 7999662937458523703ULL, 4006550374705895164ULL, 1832550525423503632ULL,
+ 9323088254855830976ULL, 12028890075598379412ULL, 6021511300787826236ULL,
+ 7864675007938747948ULL, 18099387408859708806ULL, 6438638299316820708ULL,
+ 9029399285648501543ULL, 18195459433089960253ULL, 17214335092761966083ULL,
+ 5549347964591337833ULL, 14899526073304962015ULL, 5058883181561464475ULL,
+ 7436311795731206973ULL, 7535129567768649864ULL, 1287169596809258072ULL,
+ 8237671246353565927ULL, 1715230541978016153ULL, 8443157615068813300ULL,
+ 6098675262328527839ULL, 704652094100376853ULL, 1303411723202926503ULL,
+ 7808312933946424854ULL, 6863726670433556594ULL, 9870361541383217495ULL,
+ 9273671094091079488ULL, 17541434976160119010ULL,
+ };
+
+ REQUIRE(Common::HashValue(U8Values) == 5867183267093890552ULL);
+ REQUIRE(Common::HashValue(U16Values) == 9594135570564347135ULL);
+ REQUIRE(Common::HashValue(U32Values) == 13123757214696618460ULL);
+ REQUIRE(Common::HashValue(U64Values) == 7296500016546938380ULL);
+}
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp
index d301ac5f6..faaefd49f 100644
--- a/src/tests/common/range_map.cpp
+++ b/src/tests/common/range_map.cpp
@@ -21,9 +21,9 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
my_map.Map(4000, 4500, MappedEnum::Valid_2);
my_map.Map(4200, 4400, MappedEnum::Valid_2);
my_map.Map(4200, 4400, MappedEnum::Valid_1);
- REQUIRE(my_map.GetContinousSizeFrom(4200) == 200);
- REQUIRE(my_map.GetContinousSizeFrom(3000) == 200);
- REQUIRE(my_map.GetContinousSizeFrom(2900) == 0);
+ REQUIRE(my_map.GetContinuousSizeFrom(4200) == 200);
+ REQUIRE(my_map.GetContinuousSizeFrom(3000) == 200);
+ REQUIRE(my_map.GetContinuousSizeFrom(2900) == 0);
REQUIRE(my_map.GetValueAt(2900) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(3100) == MappedEnum::Valid_1);
@@ -38,20 +38,20 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
my_map.Unmap(0, 6000);
for (u64 address = 0; address < 10000; address += 1000) {
- REQUIRE(my_map.GetContinousSizeFrom(address) == 0);
+ REQUIRE(my_map.GetContinuousSizeFrom(address) == 0);
}
my_map.Map(1000, 3000, MappedEnum::Valid_1);
my_map.Map(4000, 5000, MappedEnum::Valid_1);
my_map.Map(2500, 4100, MappedEnum::Valid_1);
- REQUIRE(my_map.GetContinousSizeFrom(1000) == 4000);
+ REQUIRE(my_map.GetContinuousSizeFrom(1000) == 4000);
my_map.Map(1000, 3000, MappedEnum::Valid_1);
my_map.Map(4000, 5000, MappedEnum::Valid_2);
my_map.Map(2500, 4100, MappedEnum::Valid_3);
- REQUIRE(my_map.GetContinousSizeFrom(1000) == 1500);
- REQUIRE(my_map.GetContinousSizeFrom(2500) == 1600);
- REQUIRE(my_map.GetContinousSizeFrom(4100) == 900);
+ REQUIRE(my_map.GetContinuousSizeFrom(1000) == 1500);
+ REQUIRE(my_map.GetContinuousSizeFrom(2500) == 1600);
+ REQUIRE(my_map.GetContinuousSizeFrom(4100) == 900);
REQUIRE(my_map.GetValueAt(900) == MappedEnum::Invalid);
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(2500) == MappedEnum::Valid_3);
@@ -59,8 +59,8 @@ TEST_CASE("Range Map: Setup", "[video_core]") {
REQUIRE(my_map.GetValueAt(5000) == MappedEnum::Invalid);
my_map.Map(2000, 6000, MappedEnum::Valid_3);
- REQUIRE(my_map.GetContinousSizeFrom(1000) == 1000);
- REQUIRE(my_map.GetContinousSizeFrom(3000) == 3000);
+ REQUIRE(my_map.GetContinuousSizeFrom(1000) == 1000);
+ REQUIRE(my_map.GetContinuousSizeFrom(3000) == 3000);
REQUIRE(my_map.GetValueAt(1000) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(1999) == MappedEnum::Valid_1);
REQUIRE(my_map.GetValueAt(1500) == MappedEnum::Valid_1);
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index 7dee988c8..e85f9977b 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -52,7 +52,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") {
REQUIRE(buf.Size() == 1U);
- // Pushing more values than space available should partially suceed.
+ // Pushing more values than space available should partially succeed.
{
std::vector<char> to_push(6);
std::iota(to_push.begin(), to_push.end(), 88);
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
index 132f139fa..26e401760 100644
--- a/src/tests/common/scratch_buffer.cpp
+++ b/src/tests/common/scratch_buffer.cpp
@@ -191,7 +191,7 @@ TEST_CASE("ScratchBuffer: Span Writes", "[common]") {
for (size_t i = 0; i < buf_span.size(); ++i) {
const auto new_value = static_cast<u8>(i + 1U);
- // Writes to a span of the scratch buffer will propogate to the buffer itself
+ // Writes to a span of the scratch buffer will propagate to the buffer itself
buf_span[i] = new_value;
REQUIRE(buf[i] == new_value);
}
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
deleted file mode 100644
index 1275cca24..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 <stdexcept>
-#include <unordered_map>
-
-#include <catch2/catch_test_macros.hpp>
-
-#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<u64, u64>;
-
-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<u64, int> 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<u64, 2> offsets{PAGE, PAGE * 3};
- static constexpr std::array<u64, 2> 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<u64, 2> offsets{PAGE * 0x1B, PAGE * 0x21};
- static constexpr std::array<u64, 2> 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 == 1);
- 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..618793668
--- /dev/null
+++ b/src/tests/video_core/memory_tracker.cpp
@@ -0,0 +1,549 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+#include <stdexcept>
+#include <unordered_map>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include "common/alignment.h"
+#include "common/common_types.h"
+#include "video_core/buffer_cache/memory_tracker_base.h"
+
+namespace {
+using Range = std::pair<u64, u64>;
+
+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<u64, int> page_table;
+};
+} // Anonymous namespace
+
+using MemoryTracker = VideoCommon::MemoryTrackerBase<RasterizerInterface>;
+
+TEST_CASE("MemoryTracker: Small region", "[video_core]") {
+ RasterizerInterface rasterizer;
+ std::unique_ptr<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<u64, 2> offsets{c + PAGE, c + PAGE * 3};
+ static constexpr std::array<u64, 2> 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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<u64, 3> offsets{c + PAGE * 0x1B, c + PAGE * 0x21};
+ static constexpr std::array<u64, 3> 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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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<MemoryTracker> memory_track(std::make_unique<MemoryTracker>(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; });
+ REQUIRE(num == 0);
+ num = 0;
+ 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