mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 01:00:14 +02:00
160 lines
5.1 KiB
C++
160 lines
5.1 KiB
C++
// The Dawn blob cache writes off Dawn's device lock: store_to_cache copies and queues, a writer
|
|
// thread compresses and commits. These tests pin what callers rely on: a stored blob reads back
|
|
// at once, reaches the database, is not rewritten when unchanged, and survives shutdown.
|
|
|
|
#include "webgpu/gpu.hpp"
|
|
|
|
#include "internal.hpp"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace aurora {
|
|
AuroraConfig g_config{};
|
|
void log_internal(AuroraLogLevel, const char*, const char* message, unsigned int len) noexcept {
|
|
std::fprintf(stderr, "%.*s\n", static_cast<int>(len), message);
|
|
}
|
|
void Module::show_fatal_dialog(const char*, std::string_view) noexcept {}
|
|
} // namespace aurora
|
|
|
|
auto fmt::formatter<AuroraLogLevel>::format(AuroraLogLevel level, format_context& ctx) const
|
|
-> format_context::iterator {
|
|
return fmt::format_to(ctx.out(), "{}", static_cast<int>(level));
|
|
}
|
|
|
|
namespace {
|
|
|
|
using aurora::webgpu::blob_cache_stats;
|
|
using aurora::webgpu::cache_shutdown;
|
|
using aurora::webgpu::flush_cache_writes;
|
|
using aurora::webgpu::load_from_cache;
|
|
using aurora::webgpu::store_to_cache;
|
|
|
|
class GpuCacheTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
m_directory = std::filesystem::temp_directory_path() /
|
|
("aurora_gpu_cache_test_" + std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()));
|
|
std::filesystem::remove_all(m_directory);
|
|
std::filesystem::create_directories(m_directory);
|
|
m_path = m_directory.string();
|
|
aurora::g_config.cachePath = m_path.c_str();
|
|
}
|
|
|
|
void TearDown() override {
|
|
cache_shutdown();
|
|
std::filesystem::remove_all(m_directory);
|
|
}
|
|
|
|
static void Store(const std::string& key, const std::vector<uint8_t>& value) {
|
|
store_to_cache(key.data(), key.size(), value.data(), value.size(), nullptr);
|
|
}
|
|
|
|
// Dawn's protocol: probe for the size, then fetch into a buffer of that size.
|
|
static std::vector<uint8_t> Load(const std::string& key) {
|
|
const size_t size = load_from_cache(key.data(), key.size(), nullptr, 0, nullptr);
|
|
std::vector<uint8_t> value(size);
|
|
if (size != 0) {
|
|
EXPECT_EQ(load_from_cache(key.data(), key.size(), value.data(), value.size(), nullptr), size);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
// Pseudo-random bytes, so compression cannot hide a mix-up between two blobs.
|
|
static std::vector<uint8_t> Bytes(size_t size, uint32_t seed) {
|
|
std::vector<uint8_t> value(size);
|
|
uint32_t state = seed * 2654435761u + 1u;
|
|
for (auto& byte : value) {
|
|
state = state * 1664525u + 1013904223u;
|
|
byte = static_cast<uint8_t>(state >> 24);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
std::filesystem::path m_directory;
|
|
std::string m_path;
|
|
};
|
|
|
|
TEST_F(GpuCacheTest, StoredBlobReadsBackBeforeAndAfterItIsCommitted) {
|
|
const auto value = Bytes(4096, 1);
|
|
Store("pipeline", value);
|
|
EXPECT_EQ(Load("pipeline"), value);
|
|
flush_cache_writes();
|
|
EXPECT_EQ(Load("pipeline"), value);
|
|
EXPECT_TRUE(Load("missing").empty());
|
|
}
|
|
|
|
TEST_F(GpuCacheTest, ShutdownWritesWhatIsStillQueued) {
|
|
const auto value = Bytes(3 << 20, 2);
|
|
Store("monolithic", value);
|
|
cache_shutdown();
|
|
// The next call reopens the database: only a committed row can answer now.
|
|
EXPECT_EQ(Load("monolithic"), value);
|
|
}
|
|
|
|
TEST_F(GpuCacheTest, NewestStoreOfAKeyWins) {
|
|
const auto first = Bytes(2 << 20, 3);
|
|
const auto second = Bytes(2 << 20, 4);
|
|
Store("monolithic", first);
|
|
Store("monolithic", second);
|
|
EXPECT_EQ(Load("monolithic"), second);
|
|
flush_cache_writes();
|
|
cache_shutdown();
|
|
EXPECT_EQ(Load("monolithic"), second);
|
|
}
|
|
|
|
TEST_F(GpuCacheTest, UnchangedLargeBlobIsNotRewritten) {
|
|
const auto value = Bytes(2 << 20, 5);
|
|
Store("monolithic", value);
|
|
flush_cache_writes();
|
|
const auto before = blob_cache_stats();
|
|
Store("monolithic", value);
|
|
auto after = blob_cache_stats();
|
|
EXPECT_EQ(after.stores, before.stores);
|
|
EXPECT_EQ(after.unchanged, before.unchanged + 1);
|
|
|
|
auto changed = value;
|
|
changed[changed.size() / 2] ^= 0xFF;
|
|
Store("monolithic", changed);
|
|
after = blob_cache_stats();
|
|
EXPECT_EQ(after.stores, before.stores + 1);
|
|
EXPECT_EQ(Load("monolithic"), changed);
|
|
|
|
// Changing back is a change too.
|
|
Store("monolithic", value);
|
|
EXPECT_EQ(blob_cache_stats().stores, before.stores + 2);
|
|
EXPECT_EQ(Load("monolithic"), value);
|
|
}
|
|
|
|
TEST_F(GpuCacheTest, BlobLoadedAtStartupIsNotRewrittenUnchanged) {
|
|
const auto value = Bytes(2 << 20, 6);
|
|
Store("monolithic", value);
|
|
cache_shutdown();
|
|
|
|
// A new session loads the blob Dawn saved last time, then serializes the same bytes again.
|
|
ASSERT_EQ(Load("monolithic"), value);
|
|
const auto before = blob_cache_stats();
|
|
Store("monolithic", value);
|
|
const auto after = blob_cache_stats();
|
|
EXPECT_EQ(after.stores, before.stores);
|
|
EXPECT_EQ(after.unchanged, before.unchanged + 1);
|
|
}
|
|
|
|
TEST_F(GpuCacheTest, SmallBlobsAreAlwaysWritten) {
|
|
const auto value = Bytes(512, 7);
|
|
Store("shader", value);
|
|
flush_cache_writes();
|
|
const auto before = blob_cache_stats();
|
|
Store("shader", value);
|
|
EXPECT_EQ(blob_cache_stats().stores, before.stores + 1);
|
|
flush_cache_writes();
|
|
EXPECT_EQ(Load("shader"), value);
|
|
}
|
|
|
|
} // namespace
|