summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2020-12-30 18:42:27 -0300
committerReinUsesLisp <reinuseslisp@airmail.cc>2021-01-13 04:31:40 -0300
commit7bd603061c81e1088448d51853b6f721a932d31c (patch)
treeca842acc9bb883cb2b9538f5bddd150da294a70d /src/tests
parenta4bfae1b551651e50f1484d6d488d3f598d274cf (diff)
tests: Add unit tests for the GPU range tracking buffer container
Due to how error prone the container design is, this commit adds unit tests for it. Some tests taken from here are based on bugs from using this buffer container in games, so if we ever break it in the future in a way that might harm games, the tests should fail.
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/video_core/buffer_base.cpp473
2 files changed, 474 insertions, 0 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 8a606b448..5b637f3c5 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -6,6 +6,7 @@ add_executable(tests
common/ring_buffer.cpp
core/core_timing.cpp
tests.cpp
+ video_core/buffer_base.cpp
)
create_target_directory_groups(tests)
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
new file mode 100644
index 000000000..651633e9e
--- /dev/null
+++ b/src/tests/video_core/buffer_base.cpp
@@ -0,0 +1,473 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <stdexcept>
+#include <unordered_map>
+
+#include <catch2/catch.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::PAGE_BITS};
+ const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS};
+ 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::PAGE_BITS);
+ 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));
+}