Add gaussian splat asset caching groundwork

This commit is contained in:
2026-04-12 11:15:59 +08:00
parent b7ce8618d2
commit 7ee28a7969
16 changed files with 1652 additions and 88 deletions

View File

@@ -15,6 +15,7 @@
#include <fstream>
#include <functional>
#include <mutex>
#include <sstream>
#include <thread>
#include <vector>
@@ -109,6 +110,19 @@ bool PumpAsyncLoadsUntil(ResourceManager& manager,
return condition();
}
std::filesystem::path GetRepositoryRoot() {
std::filesystem::path current = std::filesystem::path(__FILE__).parent_path();
while (!current.empty()) {
if (std::filesystem::exists(current / "project") &&
std::filesystem::exists(current / "engine")) {
return current;
}
current = current.parent_path();
}
return std::filesystem::path(__FILE__).parent_path();
}
bool DirectoryHasEntries(const std::filesystem::path& directoryPath) {
std::error_code ec;
if (!std::filesystem::exists(directoryPath, ec) || !std::filesystem::is_directory(directoryPath, ec)) {
@@ -118,6 +132,38 @@ bool DirectoryHasEntries(const std::filesystem::path& directoryPath) {
return std::filesystem::directory_iterator(directoryPath) != std::filesystem::directory_iterator();
}
std::vector<std::string> SplitTabSeparatedLine(const std::string& line) {
std::vector<std::string> fields;
std::stringstream stream(line);
std::string field;
while (std::getline(stream, field, '\t')) {
fields.push_back(field);
}
return fields;
}
std::string ReadSourceHashFromAssetsDb(const std::filesystem::path& assetsDbPath,
const std::string& relativePath) {
std::ifstream input(assetsDbPath, std::ios::binary);
if (!input.is_open()) {
return std::string();
}
std::string line;
while (std::getline(input, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
const std::vector<std::string> fields = SplitTabSeparatedLine(line);
if (fields.size() > 7 && fields[1] == relativePath) {
return fields[7];
}
}
return std::string();
}
std::vector<std::filesystem::path> ListArtifactEntries(const std::filesystem::path& artifactsRoot) {
namespace fs = std::filesystem;
@@ -424,6 +470,47 @@ TEST(AssetImportService_Test, EnsureArtifactExposesContainerEntryRuntimeLoadPath
fs::remove_all(projectRoot);
}
TEST(AssetImportService_Test, BootstrapProjectDefersSourceHashUntilArtifactIsNeeded) {
namespace fs = std::filesystem;
AssetImportService importService;
importService.Initialize();
const fs::path projectRoot = fs::temp_directory_path() / "xc_asset_import_service_deferred_source_hash_test";
const fs::path assetsDir = projectRoot / "Assets";
const fs::path materialPath = assetsDir / "runtime.material";
fs::remove_all(projectRoot);
fs::create_directories(assetsDir);
{
std::ofstream materialFile(materialPath);
ASSERT_TRUE(materialFile.is_open());
materialFile << "{\n";
materialFile << " \"renderQueue\": \"geometry\"\n";
materialFile << "}\n";
}
importService.SetProjectRoot(projectRoot.string().c_str());
ASSERT_TRUE(importService.BootstrapProject());
const fs::path libraryRoot(importService.GetLibraryRoot().CStr());
const fs::path assetsDbPath = libraryRoot / "assets.db";
ASSERT_TRUE(fs::exists(assetsDbPath));
EXPECT_FALSE(DirectoryHasEntries(libraryRoot / "Artifacts"));
EXPECT_TRUE(ReadSourceHashFromAssetsDb(assetsDbPath, "Assets/runtime.material").empty());
AssetImportService::ImportedAsset importedAsset;
ASSERT_TRUE(importService.EnsureArtifact("Assets/runtime.material", ResourceType::Material, importedAsset));
EXPECT_TRUE(importedAsset.exists);
EXPECT_TRUE(importedAsset.artifactReady);
EXPECT_TRUE(importedAsset.imported);
EXPECT_FALSE(ReadSourceHashFromAssetsDb(assetsDbPath, "Assets/runtime.material").empty());
EXPECT_TRUE(DirectoryHasEntries(libraryRoot / "Artifacts"));
importService.Shutdown();
fs::remove_all(projectRoot);
}
TEST(ResourceManager_Test, RebuildProjectAssetCacheRefreshesLookupState) {
namespace fs = std::filesystem;
@@ -545,6 +632,58 @@ TEST(ResourceManager_Test, SetResourceRootBootstrapsProjectAssetCache) {
fs::remove_all(projectRoot);
}
TEST(ResourceManager_ProjectSample, BootstrapProjectKeepsCloudSourceHashDeferred) {
namespace fs = std::filesystem;
const fs::path repositoryRoot = GetRepositoryRoot();
const fs::path projectRoot = repositoryRoot / "project";
const fs::path volumePath = projectRoot / "Assets" / "cloud.nvdb";
if (!fs::exists(volumePath)) {
GTEST_SKIP() << "Project cloud volume fixture is not available.";
}
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
struct ResourceManagerGuard {
ResourceManager* manager = nullptr;
~ResourceManagerGuard() {
if (manager != nullptr) {
manager->Shutdown();
}
}
} resourceManagerGuard{ &manager };
struct CurrentPathGuard {
fs::path previousPath;
~CurrentPathGuard() {
if (!previousPath.empty()) {
fs::current_path(previousPath);
}
}
} currentPathGuard{ fs::current_path() };
fs::current_path(projectRoot);
manager.SetResourceRoot(projectRoot.string().c_str());
AssetRef volumeRef;
EXPECT_TRUE(manager.TryGetAssetRef("Assets/cloud.nvdb", ResourceType::VolumeField, volumeRef));
EXPECT_TRUE(volumeRef.IsValid());
const AssetImportService::ImportStatusSnapshot status = manager.GetProjectAssetImportStatus();
EXPECT_TRUE(status.HasValue());
EXPECT_FALSE(status.inProgress);
EXPECT_TRUE(status.success);
EXPECT_EQ(std::string(status.operation.CStr()), "Bootstrap Project");
const fs::path assetsDbPath = projectRoot / "Library" / "assets.db";
ASSERT_TRUE(fs::exists(assetsDbPath));
EXPECT_TRUE(ReadSourceHashFromAssetsDb(assetsDbPath, "Assets/cloud.nvdb").empty());
manager.SetResourceRoot("");
}
TEST(AssetImportService_Test, ClearLibraryAndReimportAllAssetsManageArtifactsExplicitly) {
namespace fs = std::filesystem;