refactor: advance runtime and pipeline architecture split

This commit is contained in:
2026-05-01 19:02:25 +08:00
parent 054d419def
commit 290672159b
197 changed files with 7598 additions and 6030 deletions

View File

@@ -0,0 +1,211 @@
#pragma once
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ProjectAssetTypes.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Resources/UI/UIDocumentTypes.h>
#include <filesystem>
#include <memory>
#include <unordered_map>
#include <vector>
namespace XCEngine {
namespace Resources {
class Mesh;
class Material;
namespace AssetDatabaseInternal {
class AssetDatabaseImportRunner;
class AssetDatabaseMaintenanceRunner;
class SourceAssetDB;
class ArtifactDB;
}
class AssetDatabase {
public:
AssetDatabase();
~AssetDatabase();
struct MaintenanceStats {
Core::uint32 importedAssetCount = 0;
Core::uint32 removedArtifactCount = 0;
};
struct ArtifactDependencyRecord {
Containers::String path;
Containers::String hash;
Core::uint64 fileSize = 0;
Core::uint64 writeTime = 0;
};
struct SourceAssetRecord {
AssetGUID guid;
Containers::String relativePath;
Containers::String metaPath;
bool isFolder = false;
Containers::String importerName;
Core::uint32 importerVersion = 0;
Containers::String metaHash;
Containers::String sourceHash;
Core::uint64 sourceFileSize = 0;
Core::uint64 sourceWriteTime = 0;
Containers::String lastKnownArtifactKey;
};
struct ArtifactRecord {
Containers::String artifactKey;
AssetGUID assetGuid;
Containers::String importerName;
Core::uint32 importerVersion = 0;
ResourceType resourceType = ResourceType::Unknown;
ArtifactStorageKind storageKind = ArtifactStorageKind::Unknown;
Containers::String artifactDirectory;
Containers::String mainArtifactPath;
Containers::String mainEntryName;
Containers::String sourceHash;
Containers::String metaHash;
Core::uint64 sourceFileSize = 0;
Core::uint64 sourceWriteTime = 0;
LocalID mainLocalID = kMainAssetLocalID;
std::vector<ArtifactDependencyRecord> dependencies;
};
struct ResolvedAsset {
bool exists = false;
bool artifactReady = false;
bool imported = false;
Containers::String absolutePath;
Containers::String relativePath;
AssetGUID assetGuid;
ResourceType resourceType = ResourceType::Unknown;
Containers::String artifactMainPath;
Containers::String artifactMainEntryPath;
Containers::String artifactDirectory;
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
Containers::String mainEntryName;
LocalID mainLocalID = kMainAssetLocalID;
};
void Initialize(const Containers::String& projectRoot);
void Shutdown();
MaintenanceStats Refresh();
bool ResolvePath(const Containers::String& requestPath,
Containers::String& outAbsolutePath,
Containers::String& outRelativePath) const;
bool TryGetAssetGuid(const Containers::String& requestPath, AssetGUID& outGuid) const;
bool TryGetImportableResourceType(const Containers::String& requestPath, ResourceType& outType) const;
bool TryGetAssetRef(const Containers::String& requestPath, ResourceType resourceType, AssetRef& outRef) const;
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath);
bool ReimportAsset(const Containers::String& requestPath,
ResolvedAsset& outAsset,
MaintenanceStats* outStats = nullptr);
bool ReimportAllAssets(MaintenanceStats* outStats = nullptr);
bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ResolvedAsset& outAsset);
bool TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const;
void BuildLookupSnapshot(std::unordered_map<std::string, AssetGUID>& outPathToGuid,
std::unordered_map<AssetGUID, Containers::String>& outGuidToPath) const;
const Containers::String& GetProjectRoot() const { return m_projectRoot; }
const Containers::String& GetAssetsRoot() const { return m_assetsRoot; }
const Containers::String& GetLibraryRoot() const { return m_libraryRoot; }
const Containers::String& GetLastErrorMessage() const { return m_lastErrorMessage; }
private:
friend class AssetDatabaseInternal::AssetDatabaseImportRunner;
friend class AssetDatabaseInternal::AssetDatabaseMaintenanceRunner;
static constexpr Core::uint32 kBaseImporterVersion = 7;
void EnsureProjectLayout();
void LoadSourceAssetDB();
void SaveSourceAssetDB() const;
void LoadArtifactDB();
void SaveArtifactDB() const;
bool EnsureMetaForPath(const std::filesystem::path& sourcePath,
bool isFolder,
SourceAssetRecord& outRecord);
bool ReadMetaFile(const std::filesystem::path& metaPath,
SourceAssetRecord& inOutRecord) const;
void WriteMetaFile(const std::filesystem::path& metaPath,
const SourceAssetRecord& record) const;
Containers::String NormalizeRelativePath(const std::filesystem::path& sourcePath) const;
static Containers::String NormalizePathString(const std::filesystem::path& path);
static Containers::String NormalizePathString(const Containers::String& path);
static Containers::String MakeKey(const Containers::String& path);
static Containers::String GetImporterNameForPath(const Containers::String& relativePath, bool isFolder);
static Core::uint32 GetCurrentImporterVersion(const Containers::String& importerName);
static ResourceType GetPrimaryResourceTypeForImporter(const Containers::String& importerName);
bool ShouldReimport(const SourceAssetRecord& sourceRecord,
const ArtifactRecord* artifactRecord) const;
bool ImportAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportTextureAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportMaterialAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportModelAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportShaderAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportGaussianSplatAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportVolumeFieldAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
bool ImportUIDocumentAsset(const SourceAssetRecord& sourceRecord,
UIDocumentKind kind,
const char* artifactFileName,
ResourceType resourceType,
ArtifactRecord& outRecord);
Containers::String BuildArtifactKey(
const SourceAssetRecord& sourceRecord,
const std::vector<ArtifactDependencyRecord>& dependencies = {}) const;
Containers::String BuildArtifactDirectory(const Containers::String& artifactKey) const;
Containers::String BuildArtifactFilePath(const Containers::String& artifactKey,
const char* extension) const;
static Containers::String ReadWholeFileText(const std::filesystem::path& path);
static Containers::String ComputeFileHash(const std::filesystem::path& path);
static Core::uint64 GetFileSizeValue(const std::filesystem::path& path);
static Core::uint64 GetFileWriteTimeValue(const std::filesystem::path& path);
Containers::String NormalizeDependencyPath(const std::filesystem::path& path) const;
std::filesystem::path ResolveDependencyPath(const Containers::String& path) const;
bool CaptureDependencyRecord(const std::filesystem::path& path,
ArtifactDependencyRecord& outRecord) const;
bool AreDependenciesCurrent(const std::vector<ArtifactDependencyRecord>& dependencies) const;
bool CollectModelDependencies(const SourceAssetRecord& sourceRecord,
const std::vector<Containers::String>& importedTexturePaths,
std::vector<ArtifactDependencyRecord>& outDependencies) const;
bool CollectMaterialDependencies(const Material& material,
std::vector<ArtifactDependencyRecord>& outDependencies) const;
bool CollectShaderDependencies(const SourceAssetRecord& sourceRecord,
std::vector<ArtifactDependencyRecord>& outDependencies,
Containers::String* outError = nullptr) const;
void ClearLastErrorMessage();
void SetLastErrorMessage(const Containers::String& message);
AssetDatabaseInternal::SourceAssetDB& GetSourceAssetDB();
const AssetDatabaseInternal::SourceAssetDB& GetSourceAssetDB() const;
AssetDatabaseInternal::ArtifactDB& GetArtifactDB();
const AssetDatabaseInternal::ArtifactDB& GetArtifactDB() const;
Containers::String m_projectRoot;
Containers::String m_assetsRoot;
Containers::String m_libraryRoot;
Containers::String m_sourceDbPath;
Containers::String m_artifactDbPath;
Containers::String m_lastErrorMessage;
std::unique_ptr<AssetDatabaseInternal::SourceAssetDB> m_sourceAssetDB;
std::unique_ptr<AssetDatabaseInternal::ArtifactDB> m_artifactDB;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,393 @@
#include "Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h"
#include "Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h"
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <XCEngine/Debug/Logger.h>
#include <algorithm>
#include <filesystem>
#include <string>
#include <vector>
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
namespace fs = std::filesystem;
AssetDatabaseImportRunner::AssetDatabaseImportRunner(AssetDatabase& assetDatabase)
: m_assetDatabase(assetDatabase) {
}
bool AssetDatabaseImportRunner::ReimportAsset(const Containers::String& requestPath,
AssetDatabase::ResolvedAsset& outAsset,
AssetDatabase::MaintenanceStats* outStats) {
outAsset = AssetDatabase::ResolvedAsset();
m_assetDatabase.ClearLastErrorMessage();
if (outStats != nullptr) {
*outStats = AssetDatabase::MaintenanceStats();
}
Containers::String absolutePath;
Containers::String relativePath;
if (!m_assetDatabase.ResolvePath(requestPath, absolutePath, relativePath) || relativePath.Empty()) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Unable to resolve asset path: ") + requestPath);
return false;
}
const fs::path absoluteFsPath(absolutePath.CStr());
if (!fs::exists(absoluteFsPath) || fs::is_directory(absoluteFsPath)) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Asset source file does not exist: ") + absolutePath);
return false;
}
AssetDatabase::SourceAssetRecord sourceRecord;
if (!m_assetDatabase.EnsureMetaForPath(absoluteFsPath, false, sourceRecord)) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to prepare asset metadata: ") + absolutePath);
return false;
}
const ResourceType primaryType =
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord.importerName);
if (primaryType == ResourceType::Unknown) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Asset type is not importable: ") + requestPath);
return false;
}
AssetDatabase::ArtifactRecord rebuiltRecord;
if (!m_assetDatabase.ImportAsset(sourceRecord, rebuiltRecord)) {
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to import asset: ") + requestPath);
}
return false;
}
CommitArtifactRecord(sourceRecord, rebuiltRecord, true);
AssetDatabase::MaintenanceStats localStats;
localStats.importedAssetCount = 1;
localStats.removedArtifactCount =
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
if (outStats != nullptr) {
*outStats = localStats;
}
PopulateResolvedAssetResult(m_assetDatabase.m_projectRoot, sourceRecord, rebuiltRecord, true, outAsset);
m_assetDatabase.ClearLastErrorMessage();
return true;
}
std::vector<AssetDatabase::SourceAssetRecord> AssetDatabaseImportRunner::CollectImportableRecords() const {
std::vector<AssetDatabase::SourceAssetRecord> importableRecords;
const auto& sourceRecordsByGuid = m_assetDatabase.GetSourceAssetDB().GetRecordsByGuid();
importableRecords.reserve(sourceRecordsByGuid.size());
for (const auto& [guid, record] : sourceRecordsByGuid) {
(void)guid;
if (record.isFolder) {
continue;
}
const ResourceType primaryType =
AssetDatabase::GetPrimaryResourceTypeForImporter(record.importerName);
if (primaryType == ResourceType::Unknown) {
continue;
}
const fs::path sourcePath =
fs::path(m_assetDatabase.m_projectRoot.CStr()) / record.relativePath.CStr();
if (!fs::exists(sourcePath) || fs::is_directory(sourcePath)) {
continue;
}
importableRecords.push_back(record);
}
std::sort(importableRecords.begin(), importableRecords.end(),
[](const AssetDatabase::SourceAssetRecord& lhs,
const AssetDatabase::SourceAssetRecord& rhs) {
return ToStdString(lhs.relativePath) < ToStdString(rhs.relativePath);
});
return importableRecords;
}
bool AssetDatabaseImportRunner::ReimportAllAssets(AssetDatabase::MaintenanceStats* outStats) {
m_assetDatabase.ClearLastErrorMessage();
if (outStats != nullptr) {
*outStats = AssetDatabase::MaintenanceStats();
}
const std::vector<AssetDatabase::SourceAssetRecord> importableRecords = CollectImportableRecords();
bool allSucceeded = true;
AssetDatabase::MaintenanceStats localStats;
for (const AssetDatabase::SourceAssetRecord& record : importableRecords) {
AssetDatabase::ArtifactRecord rebuiltRecord;
if (!m_assetDatabase.ImportAsset(record, rebuiltRecord)) {
Debug::Logger::Get().Error(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] ReimportAllAssets failed path=") + record.relativePath);
allSucceeded = false;
continue;
}
CommitArtifactRecord(record, rebuiltRecord, false);
++localStats.importedAssetCount;
}
localStats.removedArtifactCount =
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
if (outStats != nullptr) {
*outStats = localStats;
}
return allSucceeded;
}
bool AssetDatabaseImportRunner::EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
AssetDatabase::ResolvedAsset& outAsset) {
outAsset = AssetDatabase::ResolvedAsset();
m_assetDatabase.ClearLastErrorMessage();
Containers::String absolutePath;
Containers::String relativePath;
if (!m_assetDatabase.ResolvePath(requestPath, absolutePath, relativePath) || relativePath.Empty()) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Unable to resolve asset path: ") + requestPath);
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact unresolved path=") + requestPath);
}
return false;
}
const fs::path absoluteFsPath(absolutePath.CStr());
if (!fs::exists(absoluteFsPath)) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Asset source file does not exist: ") + absolutePath);
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact missing source path=") +
requestPath +
" absolute=" +
absolutePath);
}
return false;
}
AssetDatabase::SourceAssetRecord sourceRecord;
if (!m_assetDatabase.EnsureMetaForPath(
absoluteFsPath,
fs::is_directory(absoluteFsPath),
sourceRecord)) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Failed to prepare asset metadata: ") + absolutePath);
return false;
}
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact source path=") +
requestPath +
" guid=" +
sourceRecord.guid.ToString() +
" importer=" +
sourceRecord.importerName +
" relative=" +
sourceRecord.relativePath);
}
const ResourceType primaryType =
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord.importerName);
if (primaryType == ResourceType::Unknown || primaryType != requestedType) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Asset type mismatch for ") +
requestPath +
": requested " +
GetResourceTypeName(requestedType) +
", importer produces " +
GetResourceTypeName(primaryType));
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact type-mismatch path=") +
requestPath +
" requested=" +
GetResourceTypeName(requestedType) +
" importerType=" +
GetResourceTypeName(primaryType));
}
return false;
}
const AssetDatabase::ArtifactRecord* artifactRecord =
m_assetDatabase.GetArtifactDB().FindByGuid(sourceRecord.guid);
if (m_assetDatabase.ShouldReimport(sourceRecord, artifactRecord)) {
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact reimport path=") + requestPath);
}
AssetDatabase::ArtifactRecord rebuiltRecord;
if (!m_assetDatabase.ImportAsset(sourceRecord, rebuiltRecord)) {
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to import asset: ") + requestPath);
}
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Error(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact reimport failed path=") + requestPath);
}
return false;
}
CommitArtifactRecord(sourceRecord, rebuiltRecord, true);
artifactRecord = m_assetDatabase.GetArtifactDB().FindByGuid(sourceRecord.guid);
outAsset.imported = true;
}
if (artifactRecord == nullptr) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Imported asset did not produce an artifact: ") + requestPath);
return false;
}
outAsset.exists = true;
PopulateResolvedAssetResult(
m_assetDatabase.m_projectRoot,
sourceRecord,
*artifactRecord,
outAsset.imported,
outAsset);
m_assetDatabase.ClearLastErrorMessage();
if (ShouldTraceAssetPath(requestPath)) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] EnsureArtifact ready path=") +
requestPath +
" artifactKey=" +
artifactRecord->artifactKey +
" artifact=" +
outAsset.artifactMainPath);
}
return true;
}
bool AssetDatabaseImportRunner::TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) {
outPath.Clear();
m_assetDatabase.ClearLastErrorMessage();
if (!assetRef.IsValid()) {
return false;
}
if (assetRef.localID == kMainAssetLocalID) {
return m_assetDatabase.TryGetPrimaryAssetPath(assetRef.assetGuid, outPath);
}
const AssetDatabase::SourceAssetRecord* sourceRecord =
m_assetDatabase.GetSourceAssetDB().FindByGuid(assetRef.assetGuid);
if (sourceRecord == nullptr) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Unknown asset GUID for sub-asset path resolution: ") +
assetRef.assetGuid.ToString());
return false;
}
const ResourceType primaryType =
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord->importerName);
if (primaryType == ResourceType::Unknown) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Asset does not have an importable primary type: ") +
sourceRecord->relativePath);
return false;
}
auto resolveFromArtifactRecord =
[&](const AssetDatabase::ArtifactRecord& artifactRecord) -> bool {
const Containers::String absoluteMainArtifactPath = NormalizeArtifactPathString(
fs::path(m_assetDatabase.m_projectRoot.CStr()) /
artifactRecord.mainArtifactPath.CStr());
ArtifactContainerReader reader;
Containers::String containerError;
if (reader.Open(absoluteMainArtifactPath, &containerError)) {
const ArtifactContainerEntryView* entry =
reader.FindEntry(assetRef.resourceType, assetRef.localID);
if (entry != nullptr) {
outPath = BuildArtifactContainerEntryPath(absoluteMainArtifactPath, entry->name);
return true;
}
}
const fs::path manifestPath =
fs::path(m_assetDatabase.m_projectRoot.CStr()) /
artifactRecord.artifactDirectory.CStr() /
kModelSubAssetManifestFileName;
Containers::String artifactPath;
if (!TryResolveModelSubAssetArtifactPath(manifestPath, assetRef, artifactPath)) {
return false;
}
outPath = NormalizeArtifactPathString(
fs::path(m_assetDatabase.m_projectRoot.CStr()) / artifactPath.CStr());
return true;
};
const AssetDatabase::ArtifactRecord* artifactRecord =
m_assetDatabase.GetArtifactDB().FindByGuid(assetRef.assetGuid);
if (artifactRecord != nullptr &&
!m_assetDatabase.ShouldReimport(*sourceRecord, artifactRecord) &&
resolveFromArtifactRecord(*artifactRecord)) {
return true;
}
AssetDatabase::ResolvedAsset resolvedAsset;
if (!m_assetDatabase.EnsureArtifact(sourceRecord->relativePath, primaryType, resolvedAsset)) {
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
m_assetDatabase.SetLastErrorMessage(
Containers::String("Failed to import asset while resolving sub-asset path: ") +
sourceRecord->relativePath);
}
return false;
}
artifactRecord = m_assetDatabase.GetArtifactDB().FindByGuid(assetRef.assetGuid);
if (artifactRecord != nullptr && resolveFromArtifactRecord(*artifactRecord)) {
return true;
}
m_assetDatabase.SetLastErrorMessage(
Containers::String("Sub-asset localID was not found in artifact manifest: ") +
sourceRecord->relativePath);
return false;
}
void AssetDatabaseImportRunner::CommitArtifactRecord(
const AssetDatabase::SourceAssetRecord& sourceRecord,
const AssetDatabase::ArtifactRecord& artifactRecord,
bool cleanupOrphans) {
m_assetDatabase.GetArtifactDB().Upsert(sourceRecord.guid, artifactRecord);
m_assetDatabase.GetSourceAssetDB().SetLastKnownArtifactKey(
ToStdString(AssetDatabase::MakeKey(sourceRecord.relativePath)),
sourceRecord.guid,
artifactRecord.artifactKey);
m_assetDatabase.SaveArtifactDB();
m_assetDatabase.SaveSourceAssetDB();
if (cleanupOrphans) {
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
}
}
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,33 @@
#pragma once
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
class AssetDatabaseImportRunner {
public:
explicit AssetDatabaseImportRunner(AssetDatabase& assetDatabase);
bool ReimportAsset(const Containers::String& requestPath,
AssetDatabase::ResolvedAsset& outAsset,
AssetDatabase::MaintenanceStats* outStats);
bool ReimportAllAssets(AssetDatabase::MaintenanceStats* outStats);
bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
AssetDatabase::ResolvedAsset& outAsset);
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath);
private:
void CommitArtifactRecord(const AssetDatabase::SourceAssetRecord& sourceRecord,
const AssetDatabase::ArtifactRecord& artifactRecord,
bool cleanupOrphans);
std::vector<AssetDatabase::SourceAssetRecord> CollectImportableRecords() const;
AssetDatabase& m_assetDatabase;
};
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,215 @@
#include "Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h"
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
namespace fs = std::filesystem;
namespace {
std::string ToStdString(const Containers::String& value) {
return std::string(value.CStr());
}
std::string ToLowerCopy(std::string text) {
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char ch) {
return static_cast<char>(std::tolower(ch));
});
return text;
}
std::vector<fs::path> CollectBuiltinShaderAssetPaths() {
std::vector<fs::path> paths;
paths.reserve(14u);
Containers::String resolvedPath;
const Containers::String builtinShaderPaths[] = {
GetBuiltinForwardLitShaderPath(),
GetBuiltinUnlitShaderPath(),
GetBuiltinDepthOnlyShaderPath(),
GetBuiltinShadowCasterShaderPath(),
#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT
GetBuiltinObjectIdShaderPath(),
#endif
GetBuiltinSkyboxShaderPath(),
GetBuiltinGaussianSplatShaderPath(),
GetBuiltinGaussianSplatUtilitiesShaderPath(),
GetBuiltinVolumetricShaderPath(),
GetBuiltinColorScalePostProcessShaderPath(),
GetBuiltinFinalColorShaderPath()
};
for (const Containers::String& builtinPath : builtinShaderPaths) {
if (TryResolveBuiltinShaderAssetPath(builtinPath, resolvedPath) && !resolvedPath.Empty()) {
paths.push_back(fs::path(resolvedPath.CStr()));
}
}
return paths;
}
} // namespace
AssetDatabaseMaintenanceRunner::AssetDatabaseMaintenanceRunner(AssetDatabase& assetDatabase)
: m_assetDatabase(assetDatabase) {
}
AssetDatabase::MaintenanceStats AssetDatabaseMaintenanceRunner::Refresh() {
AssetDatabase::MaintenanceStats stats;
std::unordered_map<std::string, bool> seenPaths;
const fs::path assetsRootPath(m_assetDatabase.m_assetsRoot.CStr());
if (fs::exists(assetsRootPath)) {
ScanAssetPath(assetsRootPath, seenPaths);
}
for (const fs::path& builtinShaderPath : CollectBuiltinShaderAssetPaths()) {
ScanAssetPath(builtinShaderPath, seenPaths);
}
RemoveMissingRecords(seenPaths);
stats.removedArtifactCount = CleanupOrphanedArtifacts();
m_assetDatabase.SaveSourceAssetDB();
m_assetDatabase.SaveArtifactDB();
return stats;
}
void AssetDatabaseMaintenanceRunner::ScanAssetPath(
const fs::path& path,
std::unordered_map<std::string, bool>& seenPaths) {
if (!fs::exists(path)) {
return;
}
if (path.has_extension() &&
ToLowerCopy(path.extension().string()) == ".meta") {
return;
}
const bool isFolder = fs::is_directory(path);
AssetDatabase::SourceAssetRecord record;
if (m_assetDatabase.EnsureMetaForPath(path, isFolder, record)) {
seenPaths[ToStdString(AssetDatabase::MakeKey(record.relativePath))] = true;
}
if (!isFolder) {
return;
}
for (const auto& entry : fs::directory_iterator(path)) {
ScanAssetPath(entry.path(), seenPaths);
}
}
void AssetDatabaseMaintenanceRunner::RemoveMissingRecords(
const std::unordered_map<std::string, bool>& seenPaths) {
std::vector<std::string> missingPathKeys;
for (const auto& [pathKey, record] : m_assetDatabase.GetSourceAssetDB().GetRecordsByPathKey()) {
(void)record;
if (seenPaths.find(pathKey) == seenPaths.end()) {
missingPathKeys.push_back(pathKey);
}
}
for (const std::string& pathKey : missingPathKeys) {
const AssetDatabase::SourceAssetRecord* record =
m_assetDatabase.GetSourceAssetDB().FindByPathKey(pathKey);
if (record == nullptr) {
continue;
}
m_assetDatabase.GetArtifactDB().EraseByGuid(record->guid);
m_assetDatabase.GetSourceAssetDB().EraseByPathKey(pathKey);
}
}
Core::uint32 AssetDatabaseMaintenanceRunner::CleanupOrphanedArtifacts() const {
std::error_code ec;
const fs::path artifactsRoot = fs::path(m_assetDatabase.m_libraryRoot.CStr()) / "Artifacts";
if (!fs::exists(artifactsRoot, ec) || !fs::is_directory(artifactsRoot, ec)) {
return 0;
}
std::unordered_set<std::string> retainedArtifactPathKeys;
const auto& artifactRecordsByGuid = m_assetDatabase.GetArtifactDB().GetRecordsByGuid();
retainedArtifactPathKeys.reserve(artifactRecordsByGuid.size() * 2u);
for (const auto& [guid, record] : artifactRecordsByGuid) {
(void)guid;
if (!record.artifactDirectory.Empty()) {
retainedArtifactPathKeys.insert(
ToStdString(AssetDatabase::MakeKey(record.artifactDirectory)));
}
if (!record.mainArtifactPath.Empty()) {
retainedArtifactPathKeys.insert(
ToStdString(AssetDatabase::MakeKey(record.mainArtifactPath)));
}
}
Core::uint32 removedArtifactCount = 0;
for (const auto& shardEntry : fs::directory_iterator(artifactsRoot, ec)) {
if (ec) {
ec.clear();
break;
}
const fs::path shardPath = shardEntry.path();
if (!shardEntry.is_directory()) {
fs::remove_all(shardPath, ec);
if (!ec) {
++removedArtifactCount;
}
ec.clear();
continue;
}
for (const auto& artifactEntry : fs::directory_iterator(shardPath, ec)) {
if (ec) {
ec.clear();
break;
}
const Containers::String relativeArtifactPath =
m_assetDatabase.NormalizeRelativePath(artifactEntry.path());
const std::string artifactPathKey =
ToStdString(AssetDatabase::MakeKey(relativeArtifactPath));
if (!relativeArtifactPath.Empty() &&
retainedArtifactPathKeys.find(artifactPathKey) != retainedArtifactPathKeys.end()) {
continue;
}
fs::remove_all(artifactEntry.path(), ec);
if (!ec) {
++removedArtifactCount;
}
ec.clear();
}
std::error_code shardEc;
if (fs::is_empty(shardPath, shardEc)) {
fs::remove(shardPath, shardEc);
}
}
if (removedArtifactCount > 0) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[AssetDatabase] Removed orphan artifact entries count=") +
Containers::String(std::to_string(removedArtifactCount).c_str()));
}
return removedArtifactCount;
}
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
class AssetDatabaseMaintenanceRunner {
public:
explicit AssetDatabaseMaintenanceRunner(AssetDatabase& assetDatabase);
AssetDatabase::MaintenanceStats Refresh();
Core::uint32 CleanupOrphanedArtifacts() const;
private:
void ScanAssetPath(const std::filesystem::path& path,
std::unordered_map<std::string, bool>& seenPaths);
void RemoveMissingRecords(const std::unordered_map<std::string, bool>& seenPaths);
AssetDatabase& m_assetDatabase;
};
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,287 @@
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
namespace fs = std::filesystem;
std::string ToStdString(const Containers::String& value) {
return std::string(value.CStr());
}
Containers::String ToContainersString(const std::string& value) {
return Containers::String(value.c_str());
}
bool HasVirtualPathScheme(const Containers::String& value) {
return ToStdString(value).find("://") != std::string::npos;
}
bool ShouldTraceAssetPath(const Containers::String& path) {
const std::string text = ToStdString(path);
return text.rfind("builtin://", 0) == 0 ||
text.find("backpack") != std::string::npos ||
text.find("New Material.mat") != std::string::npos;
}
Containers::String NormalizeArtifactPathString(const fs::path& path) {
if (path.empty()) {
return Containers::String();
}
return ToContainersString(path.lexically_normal().generic_string());
}
Containers::String NormalizeArtifactPathString(const Containers::String& path) {
if (path.Empty()) {
return Containers::String();
}
return NormalizeArtifactPathString(fs::path(path.CStr()));
}
ArtifactStorageKind InferArtifactStorageKind(
const Containers::String& projectRoot,
const AssetDatabase::ArtifactRecord& record) {
if (!record.mainArtifactPath.Empty()) {
const Containers::String absoluteMainArtifactPath =
NormalizeArtifactPathString(fs::path(projectRoot.CStr()) / record.mainArtifactPath.CStr());
if (IsArtifactContainerFile(absoluteMainArtifactPath)) {
return ArtifactStorageKind::SingleFileContainer;
}
}
return record.artifactDirectory.Empty()
? ArtifactStorageKind::Unknown
: ArtifactStorageKind::LegacyDirectory;
}
Containers::String BuildArtifactMainEntryLoadPath(
const Containers::String& artifactMainPath,
ArtifactStorageKind storageKind,
const Containers::String& mainEntryName) {
if (storageKind == ArtifactStorageKind::SingleFileContainer &&
!artifactMainPath.Empty() &&
!mainEntryName.Empty()) {
return BuildArtifactContainerEntryPath(artifactMainPath, mainEntryName);
}
return artifactMainPath;
}
namespace {
std::string EscapeField(const std::string& value) {
std::string escaped;
escaped.reserve(value.size());
for (const char ch : value) {
if (ch == '\\' || ch == '\t' || ch == '\n' || ch == '\r') {
escaped.push_back('\\');
switch (ch) {
case '\t':
escaped.push_back('t');
break;
case '\n':
escaped.push_back('n');
break;
case '\r':
escaped.push_back('r');
break;
default:
escaped.push_back(ch);
break;
}
} else {
escaped.push_back(ch);
}
}
return escaped;
}
std::string UnescapeField(const std::string& value) {
std::string result;
result.reserve(value.size());
for (size_t index = 0; index < value.size(); ++index) {
if (value[index] == '\\' && index + 1 < value.size()) {
++index;
switch (value[index]) {
case 't':
result.push_back('\t');
break;
case 'n':
result.push_back('\n');
break;
case 'r':
result.push_back('\r');
break;
default:
result.push_back(value[index]);
break;
}
} else {
result.push_back(value[index]);
}
}
return result;
}
std::vector<std::string> SplitFields(const std::string& line) {
std::vector<std::string> fields;
std::string current;
bool escaping = false;
for (const char ch : line) {
if (escaping) {
current.push_back('\\');
current.push_back(ch);
escaping = false;
continue;
}
if (ch == '\\') {
escaping = true;
continue;
}
if (ch == '\t') {
fields.push_back(UnescapeField(current));
current.clear();
continue;
}
current.push_back(ch);
}
if (escaping) {
current.push_back('\\');
}
fields.push_back(UnescapeField(current));
return fields;
}
} // namespace
bool WriteModelSubAssetManifest(
const std::filesystem::path& manifestPath,
const std::vector<ModelSubAssetManifestEntry>& entries) {
std::ofstream output(manifestPath, std::ios::out | std::ios::trunc);
if (!output.is_open()) {
return false;
}
output << "# localID\tresourceType\tartifactPath\n";
for (const ModelSubAssetManifestEntry& entry : entries) {
if (entry.localID == kInvalidLocalID ||
entry.resourceType == ResourceType::Unknown ||
entry.artifactPath.Empty()) {
continue;
}
output << entry.localID << '\t'
<< static_cast<Core::uint32>(entry.resourceType) << '\t'
<< EscapeField(ToStdString(entry.artifactPath)) << '\n';
}
return static_cast<bool>(output);
}
bool TryReadModelSubAssetManifest(
const std::filesystem::path& manifestPath,
std::vector<ModelSubAssetManifestEntry>& outEntries) {
outEntries.clear();
std::ifstream input(manifestPath);
if (!input.is_open()) {
return false;
}
std::string line;
while (std::getline(input, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
const std::vector<std::string> fields = SplitFields(line);
if (fields.size() < 3) {
continue;
}
ModelSubAssetManifestEntry entry;
entry.localID = static_cast<LocalID>(std::stoull(fields[0]));
entry.resourceType = static_cast<ResourceType>(std::stoul(fields[1]));
entry.artifactPath = ToContainersString(fields[2]);
if (entry.localID == kInvalidLocalID ||
entry.resourceType == ResourceType::Unknown ||
entry.artifactPath.Empty()) {
continue;
}
outEntries.push_back(entry);
}
return true;
}
bool TryResolveModelSubAssetArtifactPath(
const std::filesystem::path& manifestPath,
const AssetRef& assetRef,
Containers::String& outArtifactPath) {
std::vector<ModelSubAssetManifestEntry> entries;
if (!TryReadModelSubAssetManifest(manifestPath, entries)) {
return false;
}
for (const ModelSubAssetManifestEntry& entry : entries) {
if (entry.localID != assetRef.localID || entry.resourceType != assetRef.resourceType) {
continue;
}
outArtifactPath = entry.artifactPath;
return true;
}
return false;
}
void PopulateResolvedAssetResult(
const Containers::String& projectRoot,
const AssetDatabase::SourceAssetRecord& sourceRecord,
const AssetDatabase::ArtifactRecord& artifactRecord,
bool imported,
AssetDatabase::ResolvedAsset& outAsset) {
outAsset = AssetDatabase::ResolvedAsset();
outAsset.exists = true;
outAsset.artifactReady = true;
outAsset.imported = imported;
outAsset.absolutePath =
NormalizeArtifactPathString(fs::path(projectRoot.CStr()) / sourceRecord.relativePath.CStr());
outAsset.relativePath = sourceRecord.relativePath;
outAsset.assetGuid = sourceRecord.guid;
outAsset.resourceType = artifactRecord.resourceType;
outAsset.artifactStorageKind = artifactRecord.storageKind;
outAsset.mainEntryName = artifactRecord.mainEntryName;
if (!artifactRecord.artifactDirectory.Empty()) {
outAsset.artifactDirectory = NormalizeArtifactPathString(
fs::path(projectRoot.CStr()) / artifactRecord.artifactDirectory.CStr());
}
outAsset.artifactMainPath = NormalizeArtifactPathString(
fs::path(projectRoot.CStr()) / artifactRecord.mainArtifactPath.CStr());
outAsset.artifactMainEntryPath = BuildArtifactMainEntryLoadPath(
outAsset.artifactMainPath,
artifactRecord.storageKind,
artifactRecord.mainEntryName);
outAsset.mainLocalID = artifactRecord.mainLocalID;
}
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,56 @@
#pragma once
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
#include <filesystem>
#include <string>
#include <vector>
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
std::string ToStdString(const Containers::String& value);
Containers::String ToContainersString(const std::string& value);
bool HasVirtualPathScheme(const Containers::String& value);
bool ShouldTraceAssetPath(const Containers::String& path);
Containers::String NormalizeArtifactPathString(const std::filesystem::path& path);
Containers::String NormalizeArtifactPathString(const Containers::String& path);
ArtifactStorageKind InferArtifactStorageKind(
const Containers::String& projectRoot,
const AssetDatabase::ArtifactRecord& record);
Containers::String BuildArtifactMainEntryLoadPath(
const Containers::String& artifactMainPath,
ArtifactStorageKind storageKind,
const Containers::String& mainEntryName);
inline constexpr const char* kModelSubAssetManifestFileName = "subassets.tsv";
struct ModelSubAssetManifestEntry {
LocalID localID = kInvalidLocalID;
ResourceType resourceType = ResourceType::Unknown;
Containers::String artifactPath;
};
bool WriteModelSubAssetManifest(
const std::filesystem::path& manifestPath,
const std::vector<ModelSubAssetManifestEntry>& entries);
bool TryReadModelSubAssetManifest(
const std::filesystem::path& manifestPath,
std::vector<ModelSubAssetManifestEntry>& outEntries);
bool TryResolveModelSubAssetArtifactPath(
const std::filesystem::path& manifestPath,
const AssetRef& assetRef,
Containers::String& outArtifactPath);
void PopulateResolvedAssetResult(
const Containers::String& projectRoot,
const AssetDatabase::SourceAssetRecord& sourceRecord,
const AssetDatabase::ArtifactRecord& artifactRecord,
bool imported,
AssetDatabase::ResolvedAsset& outAsset);
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,435 @@
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
namespace XCEngine {
namespace Resources {
using AssetDatabaseInternal::NormalizeArtifactPathString;
using AssetDatabaseInternal::ToContainersString;
using AssetDatabaseInternal::ToStdString;
namespace {
using SourceAssetRecord = AssetDatabase::SourceAssetRecord;
using ArtifactRecord = AssetDatabase::ArtifactRecord;
using ArtifactDependencyRecord = AssetDatabase::ArtifactDependencyRecord;
namespace fs = std::filesystem;
Containers::String NormalizePathString(const fs::path& path) {
return ToContainersString(path.lexically_normal().generic_string());
}
Containers::String MakeKey(const Containers::String& path) {
std::string key = ToStdString(NormalizePathString(fs::path(path.CStr())));
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char ch) {
return static_cast<char>(std::tolower(ch));
});
return ToContainersString(key);
}
std::string EscapeField(const std::string& value) {
std::string escaped;
escaped.reserve(value.size());
for (const char ch : value) {
if (ch == '\\' || ch == '\t' || ch == '\n' || ch == '\r') {
escaped.push_back('\\');
switch (ch) {
case '\t':
escaped.push_back('t');
break;
case '\n':
escaped.push_back('n');
break;
case '\r':
escaped.push_back('r');
break;
default:
escaped.push_back(ch);
break;
}
} else {
escaped.push_back(ch);
}
}
return escaped;
}
std::string UnescapeField(const std::string& value) {
std::string result;
result.reserve(value.size());
for (size_t index = 0; index < value.size(); ++index) {
if (value[index] == '\\' && index + 1 < value.size()) {
++index;
switch (value[index]) {
case 't':
result.push_back('\t');
break;
case 'n':
result.push_back('\n');
break;
case 'r':
result.push_back('\r');
break;
default:
result.push_back(value[index]);
break;
}
} else {
result.push_back(value[index]);
}
}
return result;
}
std::vector<std::string> SplitFields(const std::string& line) {
std::vector<std::string> fields;
std::string current;
bool escaping = false;
for (const char ch : line) {
if (escaping) {
current.push_back('\\');
current.push_back(ch);
escaping = false;
continue;
}
if (ch == '\\') {
escaping = true;
continue;
}
if (ch == '\t') {
fields.push_back(UnescapeField(current));
current.clear();
continue;
}
current.push_back(ch);
}
if (escaping) {
current.push_back('\\');
}
fields.push_back(UnescapeField(current));
return fields;
}
constexpr Core::uint32 kArtifactDbSchemaVersion = 2;
} // namespace
namespace AssetDatabaseInternal {
void SourceAssetDB::Clear() {
m_recordsByPathKey.clear();
m_recordsByGuid.clear();
}
bool SourceAssetDB::Load(const Containers::String& sourceDbPath) {
Clear();
std::ifstream input(sourceDbPath.CStr());
if (!input.is_open()) {
return false;
}
std::string line;
while (std::getline(input, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
const std::vector<std::string> fields = SplitFields(line);
if (fields.size() < 10) {
continue;
}
SourceAssetRecord record;
record.guid = AssetGUID::ParseOrDefault(ToContainersString(fields[0]));
record.relativePath = ToContainersString(fields[1]);
record.metaPath = ToContainersString(fields[2]);
record.isFolder = (fields[3] == "1");
record.importerName = ToContainersString(fields[4]);
record.importerVersion = static_cast<Core::uint32>(std::stoul(fields[5]));
record.metaHash = ToContainersString(fields[6]);
record.sourceHash = ToContainersString(fields[7]);
record.sourceFileSize = static_cast<Core::uint64>(std::stoull(fields[8]));
record.sourceWriteTime = static_cast<Core::uint64>(std::stoull(fields[9]));
if (fields.size() > 10) {
record.lastKnownArtifactKey = ToContainersString(fields[10]);
}
if (!record.guid.IsValid() || record.relativePath.Empty()) {
continue;
}
const std::string pathKey = ToStdString(MakeKey(record.relativePath));
m_recordsByGuid[record.guid] = record;
m_recordsByPathKey[pathKey] = record;
}
return true;
}
bool SourceAssetDB::Save(const Containers::String& sourceDbPath) const {
std::ofstream output(sourceDbPath.CStr(), std::ios::out | std::ios::trunc);
if (!output.is_open()) {
return false;
}
output << "# guid\trelativePath\tmetaPath\tisFolder\timporter\timporterVersion\tmetaHash\tsourceHash\tsize\twriteTime\tartifactKey\n";
for (const auto& [guid, record] : m_recordsByGuid) {
(void)guid;
output << EscapeField(ToStdString(record.guid.ToString())) << '\t'
<< EscapeField(ToStdString(record.relativePath)) << '\t'
<< EscapeField(ToStdString(record.metaPath)) << '\t'
<< (record.isFolder ? "1" : "0") << '\t'
<< EscapeField(ToStdString(record.importerName)) << '\t'
<< record.importerVersion << '\t'
<< EscapeField(ToStdString(record.metaHash)) << '\t'
<< EscapeField(ToStdString(record.sourceHash)) << '\t'
<< record.sourceFileSize << '\t'
<< record.sourceWriteTime << '\t'
<< EscapeField(ToStdString(record.lastKnownArtifactKey)) << '\n';
}
return true;
}
SourceAssetRecord* SourceAssetDB::FindByPathKey(const std::string& pathKey) {
auto it = m_recordsByPathKey.find(pathKey);
return it != m_recordsByPathKey.end() ? &it->second : nullptr;
}
const SourceAssetRecord* SourceAssetDB::FindByPathKey(const std::string& pathKey) const {
auto it = m_recordsByPathKey.find(pathKey);
return it != m_recordsByPathKey.end() ? &it->second : nullptr;
}
SourceAssetRecord* SourceAssetDB::FindByGuid(const AssetGUID& guid) {
auto it = m_recordsByGuid.find(guid);
return it != m_recordsByGuid.end() ? &it->second : nullptr;
}
const SourceAssetRecord* SourceAssetDB::FindByGuid(const AssetGUID& guid) const {
auto it = m_recordsByGuid.find(guid);
return it != m_recordsByGuid.end() ? &it->second : nullptr;
}
void SourceAssetDB::Upsert(const std::string& pathKey, const SourceAssetRecord& record) {
auto existingByPath = m_recordsByPathKey.find(pathKey);
if (existingByPath != m_recordsByPathKey.end() && existingByPath->second.guid != record.guid) {
m_recordsByGuid.erase(existingByPath->second.guid);
}
auto existingByGuid = m_recordsByGuid.find(record.guid);
if (existingByGuid != m_recordsByGuid.end()) {
const std::string existingPathKey = ToStdString(MakeKey(existingByGuid->second.relativePath));
if (existingPathKey != pathKey) {
m_recordsByPathKey.erase(existingPathKey);
}
}
m_recordsByPathKey[pathKey] = record;
m_recordsByGuid[record.guid] = record;
}
void SourceAssetDB::EraseByPathKey(const std::string& pathKey) {
auto it = m_recordsByPathKey.find(pathKey);
if (it == m_recordsByPathKey.end()) {
return;
}
m_recordsByGuid.erase(it->second.guid);
m_recordsByPathKey.erase(it);
}
void SourceAssetDB::SetLastKnownArtifactKey(const std::string& pathKey,
const AssetGUID& guid,
const Containers::String& artifactKey) {
auto pathIt = m_recordsByPathKey.find(pathKey);
if (pathIt != m_recordsByPathKey.end()) {
pathIt->second.lastKnownArtifactKey = artifactKey;
}
auto guidIt = m_recordsByGuid.find(guid);
if (guidIt != m_recordsByGuid.end()) {
guidIt->second.lastKnownArtifactKey = artifactKey;
}
}
const std::unordered_map<std::string, SourceAssetRecord>& SourceAssetDB::GetRecordsByPathKey() const {
return m_recordsByPathKey;
}
const std::unordered_map<AssetGUID, SourceAssetRecord>& SourceAssetDB::GetRecordsByGuid() const {
return m_recordsByGuid;
}
void ArtifactDB::Clear() {
m_recordsByGuid.clear();
}
bool ArtifactDB::Load(const Containers::String& projectRoot, const Containers::String& artifactDbPath) {
Clear();
std::ifstream input(artifactDbPath.CStr());
if (!input.is_open()) {
return false;
}
std::string line;
Core::uint32 schemaVersion = 1;
while (std::getline(input, line)) {
if (line.empty()) {
continue;
}
if (line[0] == '#') {
const std::string schemaToken = "schema=";
const size_t schemaTokenPosition = line.find(schemaToken);
if (schemaTokenPosition != std::string::npos) {
const size_t valueStart = schemaTokenPosition + schemaToken.length();
const std::string valueText = line.substr(valueStart);
if (!valueText.empty()) {
schemaVersion = static_cast<Core::uint32>(std::stoul(valueText));
}
} else if (line.find("storageKind") != std::string::npos &&
line.find("mainEntryName") != std::string::npos) {
schemaVersion = std::max(schemaVersion, kArtifactDbSchemaVersion);
}
continue;
}
const std::vector<std::string> fields = SplitFields(line);
if (fields.size() < 10) {
continue;
}
ArtifactRecord record;
record.artifactKey = ToContainersString(fields[0]);
record.assetGuid = AssetGUID::ParseOrDefault(ToContainersString(fields[1]));
record.importerName = ToContainersString(fields[2]);
record.importerVersion = static_cast<Core::uint32>(std::stoul(fields[3]));
record.resourceType = static_cast<ResourceType>(std::stoul(fields[4]));
record.artifactDirectory = ToContainersString(fields[5]);
record.mainArtifactPath = ToContainersString(fields[6]);
record.sourceHash = ToContainersString(fields[7]);
record.metaHash = ToContainersString(fields[8]);
record.sourceFileSize = static_cast<Core::uint64>(std::stoull(fields[9]));
record.sourceWriteTime =
fields.size() > 10 ? static_cast<Core::uint64>(std::stoull(fields[10])) : 0;
record.mainLocalID =
fields.size() > 11 ? static_cast<LocalID>(std::stoull(fields[11])) : kMainAssetLocalID;
size_t dependencyFieldStart = 12;
if (schemaVersion >= kArtifactDbSchemaVersion && fields.size() > 13) {
record.storageKind = static_cast<ArtifactStorageKind>(std::stoul(fields[12]));
record.mainEntryName = ToContainersString(fields[13]);
dependencyFieldStart = 14;
}
if (record.storageKind == ArtifactStorageKind::Unknown) {
record.storageKind = InferArtifactStorageKind(projectRoot, record);
}
if (record.mainEntryName.Empty() &&
record.storageKind == ArtifactStorageKind::SingleFileContainer) {
record.mainEntryName = "main";
}
if (record.artifactDirectory.Empty() && !record.mainArtifactPath.Empty()) {
record.artifactDirectory = NormalizeArtifactPathString(
fs::path(record.mainArtifactPath.CStr()).parent_path());
}
for (size_t index = dependencyFieldStart; index + 3 < fields.size(); index += 4) {
ArtifactDependencyRecord dependency;
dependency.path = ToContainersString(fields[index + 0]);
dependency.hash = ToContainersString(fields[index + 1]);
dependency.fileSize = static_cast<Core::uint64>(std::stoull(fields[index + 2]));
dependency.writeTime = static_cast<Core::uint64>(std::stoull(fields[index + 3]));
if (!dependency.path.Empty()) {
record.dependencies.push_back(dependency);
}
}
if (!record.assetGuid.IsValid() || record.artifactKey.Empty()) {
continue;
}
m_recordsByGuid[record.assetGuid] = record;
}
return true;
}
bool ArtifactDB::Save(const Containers::String& artifactDbPath) const {
std::ofstream output(artifactDbPath.CStr(), std::ios::out | std::ios::trunc);
if (!output.is_open()) {
return false;
}
output << "# schema=" << kArtifactDbSchemaVersion << "\n";
output << "# artifactKey\tassetGuid\timporter\tversion\ttype\tartifactDir\tmainArtifact\tsourceHash\tmetaHash\tsize\twriteTime\tmainLocalID\tstorageKind\tmainEntryName\t(depPath\tdepHash\tdepSize\tdepWriteTime)...\n";
for (const auto& [guid, record] : m_recordsByGuid) {
(void)guid;
output << EscapeField(ToStdString(record.artifactKey)) << '\t'
<< EscapeField(ToStdString(record.assetGuid.ToString())) << '\t'
<< EscapeField(ToStdString(record.importerName)) << '\t'
<< record.importerVersion << '\t'
<< static_cast<Core::uint32>(record.resourceType) << '\t'
<< EscapeField(ToStdString(record.artifactDirectory)) << '\t'
<< EscapeField(ToStdString(record.mainArtifactPath)) << '\t'
<< EscapeField(ToStdString(record.sourceHash)) << '\t'
<< EscapeField(ToStdString(record.metaHash)) << '\t'
<< record.sourceFileSize << '\t'
<< record.sourceWriteTime << '\t'
<< record.mainLocalID << '\t'
<< static_cast<Core::uint32>(record.storageKind) << '\t'
<< EscapeField(ToStdString(record.mainEntryName));
for (const ArtifactDependencyRecord& dependency : record.dependencies) {
output << '\t' << EscapeField(ToStdString(dependency.path))
<< '\t' << EscapeField(ToStdString(dependency.hash))
<< '\t' << dependency.fileSize
<< '\t' << dependency.writeTime;
}
output << '\n';
}
return true;
}
ArtifactRecord* ArtifactDB::FindByGuid(const AssetGUID& guid) {
auto it = m_recordsByGuid.find(guid);
return it != m_recordsByGuid.end() ? &it->second : nullptr;
}
const ArtifactRecord* ArtifactDB::FindByGuid(const AssetGUID& guid) const {
auto it = m_recordsByGuid.find(guid);
return it != m_recordsByGuid.end() ? &it->second : nullptr;
}
void ArtifactDB::Upsert(const AssetGUID& guid, const ArtifactRecord& record) {
m_recordsByGuid[guid] = record;
}
void ArtifactDB::EraseByGuid(const AssetGUID& guid) {
m_recordsByGuid.erase(guid);
}
const std::unordered_map<AssetGUID, ArtifactRecord>& ArtifactDB::GetRecordsByGuid() const {
return m_recordsByGuid;
}
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,55 @@
#pragma once
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
#include <unordered_map>
namespace XCEngine {
namespace Resources {
namespace AssetDatabaseInternal {
class SourceAssetDB {
public:
void Clear();
bool Load(const Containers::String& sourceDbPath);
bool Save(const Containers::String& sourceDbPath) const;
AssetDatabase::SourceAssetRecord* FindByPathKey(const std::string& pathKey);
const AssetDatabase::SourceAssetRecord* FindByPathKey(const std::string& pathKey) const;
AssetDatabase::SourceAssetRecord* FindByGuid(const AssetGUID& guid);
const AssetDatabase::SourceAssetRecord* FindByGuid(const AssetGUID& guid) const;
void Upsert(const std::string& pathKey, const AssetDatabase::SourceAssetRecord& record);
void EraseByPathKey(const std::string& pathKey);
void SetLastKnownArtifactKey(const std::string& pathKey,
const AssetGUID& guid,
const Containers::String& artifactKey);
const std::unordered_map<std::string, AssetDatabase::SourceAssetRecord>& GetRecordsByPathKey() const;
const std::unordered_map<AssetGUID, AssetDatabase::SourceAssetRecord>& GetRecordsByGuid() const;
private:
std::unordered_map<std::string, AssetDatabase::SourceAssetRecord> m_recordsByPathKey;
std::unordered_map<AssetGUID, AssetDatabase::SourceAssetRecord> m_recordsByGuid;
};
class ArtifactDB {
public:
void Clear();
bool Load(const Containers::String& projectRoot, const Containers::String& artifactDbPath);
bool Save(const Containers::String& artifactDbPath) const;
AssetDatabase::ArtifactRecord* FindByGuid(const AssetGUID& guid);
const AssetDatabase::ArtifactRecord* FindByGuid(const AssetGUID& guid) const;
void Upsert(const AssetGUID& guid, const AssetDatabase::ArtifactRecord& record);
void EraseByGuid(const AssetGUID& guid);
const std::unordered_map<AssetGUID, AssetDatabase::ArtifactRecord>& GetRecordsByGuid() const;
private:
std::unordered_map<AssetGUID, AssetDatabase::ArtifactRecord> m_recordsByGuid;
};
} // namespace AssetDatabaseInternal
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,29 @@
#pragma once
#include "IProjectAssetResolver.h"
namespace XCEngine {
namespace Resources {
class IProjectAssetPipelineService : public IProjectAssetResolver {
public:
~IProjectAssetPipelineService() override = default;
virtual void Initialize() = 0;
virtual void Shutdown() = 0;
virtual void SetProjectRoot(const Containers::String& projectRoot) = 0;
virtual Containers::String GetLibraryRoot() const = 0;
virtual bool BootstrapProject() = 0;
virtual void Refresh() = 0;
virtual bool ClearLibraryCache() = 0;
virtual bool RebuildLibraryCache() = 0;
virtual bool ReimportAllAssets() = 0;
virtual bool ReimportAsset(const Containers::String& requestPath,
ProjectAssetImportedAsset& outAsset) = 0;
virtual ProjectAssetImportStatusSnapshot GetLastImportStatus() const = 0;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,29 @@
#pragma once
#include "ProjectAssetTypes.h"
namespace XCEngine {
namespace Resources {
class IProjectAssetResolver {
public:
virtual ~IProjectAssetResolver() = default;
virtual Containers::String GetProjectRoot() const = 0;
virtual bool TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const = 0;
virtual bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ProjectAssetImportedAsset& outAsset) = 0;
virtual bool TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const = 0;
virtual bool TryGetPrimaryAssetPath(const AssetGUID& guid,
Containers::String& outRelativePath) const = 0;
virtual bool TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) const = 0;
virtual void BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const = 0;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,66 @@
#pragma once
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Types.h>
#include <string>
#include <unordered_map>
namespace XCEngine {
namespace Resources {
enum class ArtifactStorageKind : Core::uint8 {
Unknown = 0,
LegacyDirectory = 1,
SingleFileContainer = 2
};
struct ProjectAssetImportStatusSnapshot {
Core::uint64 revision = 0;
bool inProgress = false;
bool success = false;
Containers::String operation;
Containers::String targetPath;
Containers::String message;
Core::uint64 startedAtMs = 0;
Core::uint64 completedAtMs = 0;
Core::uint64 durationMs = 0;
Core::uint32 importedAssetCount = 0;
Core::uint32 removedArtifactCount = 0;
bool HasValue() const {
return revision != 0;
}
};
struct ProjectAssetLookupSnapshot {
std::unordered_map<std::string, AssetGUID> assetGuidByPathKey;
std::unordered_map<AssetGUID, Containers::String> assetPathByGuid;
void Clear() {
assetGuidByPathKey.clear();
assetPathByGuid.clear();
}
};
struct ProjectAssetImportedAsset {
bool exists = false;
bool artifactReady = false;
bool imported = false;
Containers::String absolutePath;
Containers::String relativePath;
AssetGUID assetGuid;
ResourceType resourceType = ResourceType::Unknown;
Containers::String runtimeLoadPath;
Containers::String artifactMainPath;
Containers::String artifactMainEntryPath;
Containers::String artifactDirectory;
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
Containers::String mainEntryName;
LocalID mainLocalID = kMainAssetLocalID;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,4 +1,5 @@
#include <XCEngine/Core/Asset/AssetImportService.h>
#include "AssetImportService.h"
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
#include <XCEngine/Debug/Logger.h>
#include <chrono>
@@ -15,325 +16,24 @@ Containers::String ToContainersString(const std::string& value) {
return Containers::String(value.c_str());
}
Containers::String BuildStatsSuffix(const AssetDatabase::MaintenanceStats& stats) {
Containers::String BuildStatsSuffix(Core::uint32 importedAssetCount,
Core::uint32 removedArtifactCount) {
std::string suffix;
if (stats.importedAssetCount > 0) {
if (importedAssetCount > 0) {
suffix += " imported=";
suffix += std::to_string(stats.importedAssetCount);
suffix += std::to_string(importedAssetCount);
}
if (stats.removedArtifactCount > 0) {
if (removedArtifactCount > 0) {
suffix += " removedOrphans=";
suffix += std::to_string(stats.removedArtifactCount);
suffix += std::to_string(removedArtifactCount);
}
return ToContainersString(suffix);
}
Core::uint64 GetCurrentSteadyTimeMs() {
return static_cast<Core::uint64>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count());
}
} // namespace
void AssetImportService::Initialize() {
}
void AssetImportService::Shutdown() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
m_assetDatabase.Shutdown();
m_projectRoot.Clear();
ResetImportStatusLocked();
}
void AssetImportService::SetProjectRoot(const Containers::String& projectRoot) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot == projectRoot) {
return;
}
if (!m_projectRoot.Empty()) {
m_assetDatabase.Shutdown();
}
m_projectRoot = projectRoot;
ResetImportStatusLocked();
if (!m_projectRoot.Empty()) {
m_assetDatabase.Initialize(m_projectRoot);
}
}
Containers::String AssetImportService::GetProjectRoot() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_projectRoot;
}
Containers::String AssetImportService::GetLibraryRoot() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_assetDatabase.GetLibraryRoot();
}
bool AssetImportService::BootstrapProject() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Bootstrap Project",
Containers::String(),
false,
"Cannot bootstrap project assets without an active project.",
AssetDatabase::MaintenanceStats());
return false;
}
BeginImportStatusLocked(
"Bootstrap Project",
m_projectRoot,
"Bootstrapping project Library state...");
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
FinishImportStatusLocked(
"Bootstrap Project",
m_projectRoot,
true,
Containers::String("Bootstrapped project Library state.") + BuildStatsSuffix(stats),
stats);
return true;
}
void AssetImportService::Refresh() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!m_projectRoot.Empty()) {
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
if (stats.removedArtifactCount > 0) {
FinishImportStatusLocked(
"Refresh",
m_projectRoot,
true,
Containers::String("Refresh removed orphan artifact entries.") + BuildStatsSuffix(stats),
stats);
}
}
}
bool AssetImportService::ClearLibraryCache() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Clear Library",
Containers::String(),
false,
"Cannot clear Library cache without an active project.",
AssetDatabase::MaintenanceStats());
return false;
}
BeginImportStatusLocked(
"Clear Library",
m_assetDatabase.GetLibraryRoot(),
"Clearing Library cache...");
const Containers::String projectRoot = m_projectRoot;
const Containers::String libraryRoot = m_assetDatabase.GetLibraryRoot();
m_assetDatabase.Shutdown();
std::error_code ec;
fs::remove_all(fs::path(libraryRoot.CStr()), ec);
m_assetDatabase.Initialize(projectRoot);
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
const bool succeeded = !ec;
FinishImportStatusLocked(
"Clear Library",
libraryRoot,
succeeded,
succeeded
? Containers::String("Cleared Library cache and rebuilt source asset lookup.") + BuildStatsSuffix(stats)
: Containers::String("Failed to clear Library cache."),
stats);
return succeeded;
}
bool AssetImportService::RebuildLibraryCache() {
if (!ClearLibraryCache()) {
return false;
}
return ReimportAllAssets();
}
bool AssetImportService::ReimportAllAssets() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Reimport All Assets",
Containers::String(),
false,
"Cannot reimport assets without an active project.",
AssetDatabase::MaintenanceStats());
return false;
}
BeginImportStatusLocked(
"Reimport All Assets",
m_projectRoot,
"Reimporting all project assets...");
m_assetDatabase.Refresh();
AssetDatabase::MaintenanceStats stats;
const bool succeeded = m_assetDatabase.ReimportAllAssets(&stats);
FinishImportStatusLocked(
"Reimport All Assets",
m_projectRoot,
succeeded,
succeeded
? Containers::String("Reimported all project assets.") + BuildStatsSuffix(stats)
: Containers::String("Reimport all assets completed with failures.") + BuildStatsSuffix(stats),
stats);
return succeeded;
}
bool AssetImportService::ReimportAsset(const Containers::String& requestPath,
ImportedAsset& outAsset) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
false,
"Cannot reimport an asset without an active project.",
AssetDatabase::MaintenanceStats());
return false;
}
BeginImportStatusLocked(
"Reimport Asset",
requestPath,
Containers::String("Reimporting asset: ") + requestPath);
m_assetDatabase.Refresh();
AssetDatabase::ResolvedAsset resolvedAsset;
AssetDatabase::MaintenanceStats stats;
if (!m_assetDatabase.ReimportAsset(requestPath, resolvedAsset, &stats)) {
const Containers::String databaseError = m_assetDatabase.GetLastErrorMessage();
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
false,
!databaseError.Empty()
? Containers::String("Failed to reimport asset: ") + requestPath + " - " + databaseError
: Containers::String("Failed to reimport asset: ") + requestPath,
stats);
return false;
}
outAsset = ConvertResolvedAsset(resolvedAsset);
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
true,
Containers::String("Reimported asset: ") + requestPath + BuildStatsSuffix(stats),
stats);
return true;
}
AssetImportService::ImportStatusSnapshot AssetImportService::GetLastImportStatus() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_lastImportStatus;
}
bool AssetImportService::TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
outType = ResourceType::Unknown;
return false;
}
return m_assetDatabase.TryGetImportableResourceType(requestPath, outType);
}
bool AssetImportService::EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ImportedAsset& outAsset) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
AssetDatabase::ResolvedAsset resolvedAsset;
if (!m_assetDatabase.EnsureArtifact(requestPath, requestedType, resolvedAsset)) {
const Containers::String databaseError = m_assetDatabase.GetLastErrorMessage();
FinishImportStatusLocked(
"Import Asset",
requestPath,
false,
!databaseError.Empty()
? Containers::String("Failed to build asset artifact: ") + requestPath + " - " + databaseError
: Containers::String("Failed to build asset artifact: ") + requestPath,
AssetDatabase::MaintenanceStats());
return false;
}
outAsset = ConvertResolvedAsset(resolvedAsset);
if (resolvedAsset.imported) {
AssetDatabase::MaintenanceStats stats;
stats.importedAssetCount = 1;
FinishImportStatusLocked(
"Import Asset",
requestPath,
true,
Containers::String("Imported asset artifact: ") + requestPath,
stats);
}
return true;
}
bool AssetImportService::TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase.TryGetAssetRef(requestPath, resourceType, outRef);
}
bool AssetImportService::TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase.TryResolveAssetPath(assetRef, outPath);
}
bool AssetImportService::TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase.TryGetPrimaryAssetPath(guid, outRelativePath);
}
void AssetImportService::BuildLookupSnapshot(LookupSnapshot& outSnapshot) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
outSnapshot.Clear();
if (m_projectRoot.Empty()) {
return;
}
m_assetDatabase.BuildLookupSnapshot(outSnapshot.assetGuidByPathKey, outSnapshot.assetPathByGuid);
}
AssetImportService::ImportedAsset AssetImportService::ConvertResolvedAsset(
AssetImportService::ImportedAsset ConvertResolvedAsset(
const AssetDatabase::ResolvedAsset& resolvedAsset) {
ImportedAsset importedAsset;
AssetImportService::ImportedAsset importedAsset;
importedAsset.exists = resolvedAsset.exists;
importedAsset.artifactReady = resolvedAsset.artifactReady;
importedAsset.imported = resolvedAsset.imported;
@@ -356,6 +56,350 @@ AssetImportService::ImportedAsset AssetImportService::ConvertResolvedAsset(
return importedAsset;
}
Core::uint64 GetCurrentSteadyTimeMs() {
return static_cast<Core::uint64>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count());
}
} // namespace
AssetImportService::AssetImportService() = default;
AssetImportService::~AssetImportService() = default;
void AssetImportService::Initialize() {
if (!m_assetDatabase) {
m_assetDatabase = std::make_unique<AssetDatabase>();
}
}
void AssetImportService::Shutdown() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_assetDatabase) {
m_assetDatabase->Shutdown();
}
m_projectRoot.Clear();
ResetImportStatusLocked();
}
void AssetImportService::SetProjectRoot(const Containers::String& projectRoot) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot == projectRoot) {
return;
}
if (!m_projectRoot.Empty()) {
if (m_assetDatabase) {
m_assetDatabase->Shutdown();
}
}
Initialize();
m_projectRoot = projectRoot;
ResetImportStatusLocked();
if (!m_projectRoot.Empty()) {
m_assetDatabase->Initialize(m_projectRoot);
}
}
Containers::String AssetImportService::GetProjectRoot() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_projectRoot;
}
Containers::String AssetImportService::GetLibraryRoot() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_assetDatabase ? m_assetDatabase->GetLibraryRoot() : Containers::String();
}
bool AssetImportService::BootstrapProject() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Bootstrap Project",
Containers::String(),
false,
"Cannot bootstrap project assets without an active project.",
ImportOperationStats());
return false;
}
BeginImportStatusLocked(
"Bootstrap Project",
m_projectRoot,
"Bootstrapping project Library state...");
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Bootstrap Project",
m_projectRoot,
true,
Containers::String("Bootstrapped project Library state.") +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
serviceStats);
return true;
}
void AssetImportService::Refresh() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!m_projectRoot.Empty()) {
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
if (stats.removedArtifactCount > 0) {
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Refresh",
m_projectRoot,
true,
Containers::String("Refresh removed orphan artifact entries.") +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
serviceStats);
}
}
}
bool AssetImportService::ClearLibraryCache() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Clear Library",
Containers::String(),
false,
"Cannot clear Library cache without an active project.",
ImportOperationStats());
return false;
}
BeginImportStatusLocked(
"Clear Library",
m_assetDatabase->GetLibraryRoot(),
"Clearing Library cache...");
const Containers::String projectRoot = m_projectRoot;
const Containers::String libraryRoot = m_assetDatabase->GetLibraryRoot();
m_assetDatabase->Shutdown();
std::error_code ec;
fs::remove_all(fs::path(libraryRoot.CStr()), ec);
m_assetDatabase->Initialize(projectRoot);
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
const bool succeeded = !ec;
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Clear Library",
libraryRoot,
succeeded,
succeeded
? Containers::String("Cleared Library cache and rebuilt source asset lookup.") +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount)
: Containers::String("Failed to clear Library cache."),
serviceStats);
return succeeded;
}
bool AssetImportService::RebuildLibraryCache() {
if (!ClearLibraryCache()) {
return false;
}
return ReimportAllAssets();
}
bool AssetImportService::ReimportAllAssets() {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Reimport All Assets",
Containers::String(),
false,
"Cannot reimport assets without an active project.",
ImportOperationStats());
return false;
}
BeginImportStatusLocked(
"Reimport All Assets",
m_projectRoot,
"Reimporting all project assets...");
m_assetDatabase->Refresh();
AssetDatabase::MaintenanceStats stats;
const bool succeeded = m_assetDatabase->ReimportAllAssets(&stats);
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Reimport All Assets",
m_projectRoot,
succeeded,
succeeded
? Containers::String("Reimported all project assets.") +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount)
: Containers::String("Reimport all assets completed with failures.") +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
serviceStats);
return succeeded;
}
bool AssetImportService::ReimportAsset(const Containers::String& requestPath,
ImportedAsset& outAsset) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
false,
"Cannot reimport an asset without an active project.",
ImportOperationStats());
return false;
}
BeginImportStatusLocked(
"Reimport Asset",
requestPath,
Containers::String("Reimporting asset: ") + requestPath);
m_assetDatabase->Refresh();
AssetDatabase::ResolvedAsset resolvedAsset;
AssetDatabase::MaintenanceStats stats;
if (!m_assetDatabase->ReimportAsset(requestPath, resolvedAsset, &stats)) {
const Containers::String databaseError = m_assetDatabase->GetLastErrorMessage();
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
false,
!databaseError.Empty()
? Containers::String("Failed to reimport asset: ") + requestPath + " - " + databaseError
: Containers::String("Failed to reimport asset: ") + requestPath,
serviceStats);
return false;
}
outAsset = ConvertResolvedAsset(resolvedAsset);
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Reimport Asset",
requestPath,
true,
Containers::String("Reimported asset: ") +
requestPath +
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
serviceStats);
return true;
}
AssetImportService::ImportStatusSnapshot AssetImportService::GetLastImportStatus() const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_lastImportStatus;
}
bool AssetImportService::TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
outType = ResourceType::Unknown;
return false;
}
return m_assetDatabase && m_assetDatabase->TryGetImportableResourceType(requestPath, outType);
}
bool AssetImportService::EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ImportedAsset& outAsset) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
AssetDatabase::ResolvedAsset resolvedAsset;
if (!m_assetDatabase->EnsureArtifact(requestPath, requestedType, resolvedAsset)) {
const Containers::String databaseError = m_assetDatabase->GetLastErrorMessage();
FinishImportStatusLocked(
"Import Asset",
requestPath,
false,
!databaseError.Empty()
? Containers::String("Failed to build asset artifact: ") + requestPath + " - " + databaseError
: Containers::String("Failed to build asset artifact: ") + requestPath,
ImportOperationStats());
return false;
}
outAsset = ConvertResolvedAsset(resolvedAsset);
if (resolvedAsset.imported) {
AssetDatabase::MaintenanceStats stats;
stats.importedAssetCount = 1;
ImportOperationStats serviceStats;
serviceStats.importedAssetCount = stats.importedAssetCount;
serviceStats.removedArtifactCount = stats.removedArtifactCount;
FinishImportStatusLocked(
"Import Asset",
requestPath,
true,
Containers::String("Imported asset artifact: ") + requestPath,
serviceStats);
}
return true;
}
bool AssetImportService::TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase && m_assetDatabase->TryGetAssetRef(requestPath, resourceType, outRef);
}
bool AssetImportService::TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase && m_assetDatabase->TryResolveAssetPath(assetRef, outPath);
}
bool AssetImportService::TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (m_projectRoot.Empty()) {
return false;
}
return m_assetDatabase && m_assetDatabase->TryGetPrimaryAssetPath(guid, outRelativePath);
}
void AssetImportService::BuildLookupSnapshot(LookupSnapshot& outSnapshot) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
outSnapshot.Clear();
if (m_projectRoot.Empty()) {
return;
}
if (m_assetDatabase) {
m_assetDatabase->BuildLookupSnapshot(outSnapshot.assetGuidByPathKey, outSnapshot.assetPathByGuid);
}
}
void AssetImportService::ResetImportStatusLocked() {
m_lastImportStatus = ImportStatusSnapshot();
m_nextImportStatusRevision = 1;
@@ -382,7 +426,7 @@ void AssetImportService::FinishImportStatusLocked(const Containers::String& oper
const Containers::String& targetPath,
bool success,
const Containers::String& message,
const AssetDatabase::MaintenanceStats& stats) {
const ImportOperationStats& stats) {
const Core::uint64 completedAtMs = GetCurrentSteadyTimeMs();
const Core::uint64 startedAtMs = m_lastImportStatus.startedAtMs != 0
? m_lastImportStatus.startedAtMs

View File

@@ -0,0 +1,76 @@
#pragma once
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
#include <memory>
#include <mutex>
namespace XCEngine {
namespace Resources {
class AssetDatabase;
class AssetImportService : public IProjectAssetPipelineService {
public:
using ImportStatusSnapshot = ProjectAssetImportStatusSnapshot;
using LookupSnapshot = ProjectAssetLookupSnapshot;
using ImportedAsset = ProjectAssetImportedAsset;
AssetImportService();
~AssetImportService() override;
void Initialize() override;
void Shutdown() override;
void SetProjectRoot(const Containers::String& projectRoot) override;
Containers::String GetProjectRoot() const override;
Containers::String GetLibraryRoot() const override;
bool BootstrapProject() override;
void Refresh() override;
bool ClearLibraryCache() override;
bool RebuildLibraryCache() override;
bool ReimportAllAssets() override;
bool ReimportAsset(const Containers::String& requestPath,
ImportedAsset& outAsset) override;
ImportStatusSnapshot GetLastImportStatus() const override;
bool TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const override;
bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ImportedAsset& outAsset) override;
bool TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const override;
bool TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) const override;
bool TryGetPrimaryAssetPath(const AssetGUID& guid,
Containers::String& outRelativePath) const override;
void BuildLookupSnapshot(LookupSnapshot& outSnapshot) const override;
private:
struct ImportOperationStats {
Core::uint32 importedAssetCount = 0;
Core::uint32 removedArtifactCount = 0;
};
void ResetImportStatusLocked();
void BeginImportStatusLocked(const Containers::String& operation,
const Containers::String& targetPath,
const Containers::String& message);
void FinishImportStatusLocked(const Containers::String& operation,
const Containers::String& targetPath,
bool success,
const Containers::String& message,
const ImportOperationStats& stats);
mutable std::recursive_mutex m_mutex;
Containers::String m_projectRoot;
std::unique_ptr<AssetDatabase> m_assetDatabase;
ImportStatusSnapshot m_lastImportStatus;
Core::uint64 m_nextImportStatusRevision = 1;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,153 @@
#include "ProjectAssetService.h"
#include "Pipeline/Core/ImportService/AssetImportService.h"
#include "Pipeline/Core/SourceIndex/ProjectAssetIndex.h"
namespace XCEngine {
namespace Resources {
struct ProjectAssetService::Impl {
mutable AssetImportService assetImportService;
mutable ProjectAssetIndex projectAssetIndex;
};
ProjectAssetService::ProjectAssetService()
: m_impl(std::make_unique<Impl>()) {
}
ProjectAssetService::~ProjectAssetService() = default;
void ProjectAssetService::Initialize() {
m_impl->assetImportService.Initialize();
}
void ProjectAssetService::Shutdown() {
m_impl->assetImportService.Shutdown();
m_impl->projectAssetIndex.ResetProjectRoot();
}
void ProjectAssetService::SetProjectRoot(const Containers::String& projectRoot) {
m_impl->assetImportService.SetProjectRoot(projectRoot);
if (projectRoot.Empty()) {
m_impl->projectAssetIndex.ResetProjectRoot();
}
}
Containers::String ProjectAssetService::GetProjectRoot() const {
return m_impl->assetImportService.GetProjectRoot();
}
Containers::String ProjectAssetService::GetLibraryRoot() const {
return m_impl->assetImportService.GetLibraryRoot();
}
bool ProjectAssetService::BootstrapProject() {
const bool bootstrapped = m_impl->assetImportService.BootstrapProject();
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
return bootstrapped;
}
void ProjectAssetService::Refresh() {
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
return;
}
m_impl->assetImportService.Refresh();
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
}
bool ProjectAssetService::ClearLibraryCache() {
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
return false;
}
const bool cleared = m_impl->assetImportService.ClearLibraryCache();
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
return cleared;
}
bool ProjectAssetService::RebuildLibraryCache() {
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
return false;
}
const bool rebuilt = m_impl->assetImportService.RebuildLibraryCache();
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
return rebuilt;
}
bool ProjectAssetService::ReimportAllAssets() {
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
return false;
}
const bool reimported = m_impl->assetImportService.ReimportAllAssets();
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
return reimported;
}
bool ProjectAssetService::ReimportAsset(const Containers::String& requestPath,
ProjectAssetImportedAsset& outAsset) {
if (m_impl->assetImportService.GetProjectRoot().Empty() || requestPath.Empty()) {
return false;
}
const bool reimported = m_impl->assetImportService.ReimportAsset(requestPath, outAsset);
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
if (reimported && outAsset.assetGuid.IsValid() && !outAsset.relativePath.Empty()) {
m_impl->projectAssetIndex.RememberResolvedPath(outAsset.assetGuid, outAsset.relativePath);
}
return reimported;
}
ProjectAssetImportStatusSnapshot ProjectAssetService::GetLastImportStatus() const {
return m_impl->assetImportService.GetLastImportStatus();
}
bool ProjectAssetService::TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const {
return m_impl->assetImportService.TryGetImportableResourceType(requestPath, outType);
}
bool ProjectAssetService::EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ProjectAssetImportedAsset& outAsset) {
const bool resolved =
m_impl->assetImportService.EnsureArtifact(requestPath, requestedType, outAsset);
if (resolved && outAsset.assetGuid.IsValid() && !outAsset.relativePath.Empty()) {
m_impl->projectAssetIndex.RememberResolvedPath(outAsset.assetGuid, outAsset.relativePath);
}
return resolved;
}
bool ProjectAssetService::TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const {
return m_impl->projectAssetIndex.TryGetAssetRef(
m_impl->assetImportService,
requestPath,
resourceType,
outRef);
}
bool ProjectAssetService::TryGetPrimaryAssetPath(const AssetGUID& guid,
Containers::String& outRelativePath) const {
return m_impl->assetImportService.TryGetPrimaryAssetPath(guid, outRelativePath);
}
bool ProjectAssetService::TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) const {
return m_impl->projectAssetIndex.TryResolveAssetPath(
m_impl->assetImportService,
assetRef,
outPath);
}
void ProjectAssetService::BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const {
m_impl->assetImportService.BuildLookupSnapshot(outSnapshot);
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,51 @@
#pragma once
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
#include <memory>
namespace XCEngine {
namespace Resources {
class ProjectAssetService final : public IProjectAssetPipelineService {
public:
ProjectAssetService();
~ProjectAssetService() override;
void Initialize() override;
void Shutdown() override;
void SetProjectRoot(const Containers::String& projectRoot) override;
Containers::String GetProjectRoot() const override;
Containers::String GetLibraryRoot() const override;
bool BootstrapProject() override;
void Refresh() override;
bool ClearLibraryCache() override;
bool RebuildLibraryCache() override;
bool ReimportAllAssets() override;
bool ReimportAsset(const Containers::String& requestPath,
ProjectAssetImportedAsset& outAsset) override;
ProjectAssetImportStatusSnapshot GetLastImportStatus() const override;
bool TryGetImportableResourceType(const Containers::String& requestPath,
ResourceType& outType) const override;
bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
ProjectAssetImportedAsset& outAsset) override;
bool TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const override;
bool TryGetPrimaryAssetPath(const AssetGUID& guid,
Containers::String& outRelativePath) const override;
bool TryResolveAssetPath(const AssetRef& assetRef,
Containers::String& outPath) const override;
void BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const override;
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,4 @@
#include <XCEngine/Core/Asset/ProjectAssetIndex.h>
#include <XCEngine/Core/Asset/AssetImportService.h>
#include "ProjectAssetIndex.h"
#include <algorithm>
#include <cctype>
@@ -75,11 +73,11 @@ void ProjectAssetIndex::ResetProjectRoot(const Containers::String& projectRoot)
m_assetPathByGuid.clear();
}
void ProjectAssetIndex::RefreshFrom(const AssetImportService& importService) {
AssetImportService::LookupSnapshot snapshot;
const Containers::String projectRoot = importService.GetProjectRoot();
void ProjectAssetIndex::RefreshFrom(const IProjectAssetResolver& resolver) {
ProjectAssetLookupSnapshot snapshot;
const Containers::String projectRoot = resolver.GetProjectRoot();
if (!projectRoot.Empty()) {
importService.BuildLookupSnapshot(snapshot);
resolver.BuildLookupSnapshot(snapshot);
}
std::unique_lock<std::shared_mutex> lock(m_mutex);
@@ -88,7 +86,7 @@ void ProjectAssetIndex::RefreshFrom(const AssetImportService& importService) {
m_assetPathByGuid = std::move(snapshot.assetPathByGuid);
}
bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
bool ProjectAssetIndex::TryGetAssetRef(IProjectAssetPipelineService& pipelineService,
const Containers::String& path,
ResourceType resourceType,
AssetRef& outRef) const {
@@ -108,21 +106,21 @@ bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
}
if (!resolved) {
resolved = importService.TryGetAssetRef(path, resourceType, outRef);
resolved = pipelineService.TryGetAssetRef(path, resourceType, outRef);
if (!resolved) {
const Containers::String projectRoot = importService.GetProjectRoot();
const Containers::String projectRoot = pipelineService.GetProjectRoot();
const Containers::String relativePath = MakeAssetLookupRelativePath(projectRoot, path);
if (!relativePath.Empty() && !projectRoot.Empty()) {
auto* index = const_cast<ProjectAssetIndex*>(this);
importService.Refresh();
index->RefreshFrom(importService);
resolved = importService.TryGetAssetRef(path, resourceType, outRef);
pipelineService.Refresh();
index->RefreshFrom(pipelineService);
resolved = pipelineService.TryGetAssetRef(path, resourceType, outRef);
}
}
if (resolved) {
Containers::String relativePath;
if (importService.TryGetPrimaryAssetPath(outRef.assetGuid, relativePath)) {
if (pipelineService.TryGetPrimaryAssetPath(outRef.assetGuid, relativePath)) {
const_cast<ProjectAssetIndex*>(this)->RememberResolvedPath(outRef.assetGuid, relativePath);
}
}
@@ -131,7 +129,7 @@ bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
return resolved;
}
bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
bool ProjectAssetIndex::TryResolveAssetPath(const IProjectAssetResolver& resolver,
const AssetRef& assetRef,
Containers::String& outPath) const {
if (!assetRef.IsValid()) {
@@ -139,7 +137,7 @@ bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
}
if (assetRef.localID != kMainAssetLocalID) {
return importService.TryResolveAssetPath(assetRef, outPath);
return resolver.TryResolveAssetPath(assetRef, outPath);
}
bool resolved = false;
@@ -153,7 +151,7 @@ bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
}
if (!resolved) {
resolved = importService.TryResolveAssetPath(assetRef, outPath);
resolved = resolver.TryResolveAssetPath(assetRef, outPath);
if (resolved) {
const_cast<ProjectAssetIndex*>(this)->RememberResolvedPath(assetRef.assetGuid, outPath);
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
#include <shared_mutex>
#include <unordered_map>
namespace XCEngine {
namespace Resources {
class ProjectAssetIndex {
public:
void ResetProjectRoot(const Containers::String& projectRoot = Containers::String());
void RefreshFrom(const IProjectAssetResolver& resolver);
bool TryGetAssetRef(IProjectAssetPipelineService& pipelineService,
const Containers::String& path,
ResourceType resourceType,
AssetRef& outRef) const;
bool TryResolveAssetPath(const IProjectAssetResolver& resolver,
const AssetRef& assetRef,
Containers::String& outPath) const;
void RememberResolvedPath(const AssetGUID& assetGuid, const Containers::String& relativePath);
private:
mutable std::shared_mutex m_mutex;
Containers::String m_projectRoot;
std::unordered_map<std::string, AssetGUID> m_assetGuidByPathKey;
std::unordered_map<AssetGUID, Containers::String> m_assetPathByGuid;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,107 @@
# XCEngine 子计划Runtime Rendering FrameData 第一刀
## 1. 为什么现在切这个
截至 `2026-05-01`
- `Runtime/Asset`
- `Runtime/Resources`
- `Runtime/Scene`
- `Runtime/Rendering/Caches`
- `Runtime/Rendering/Shadow`
- `Runtime/Rendering/Materials`
- `Runtime/Rendering/Picking`
`Picking` 已完成后,下一步优先选择 `Runtime/Rendering/FrameData`,原因是:
- `FrameData` 主要由稳定数据头和少量 header-only 工具组成
- 依赖虽然广,但大多是只读消费,不牵涉主流程重构
- 可以继续按低风险方式把 rendering 运行时数据契约落到真实树
- 能为后续 `Extraction` 收口提前建立稳定内部路径
## 2. 本子计划切什么
这一轮聚焦:
- `RenderCameraData.h`
- `RenderEnvironmentData.h`
- `CullingResults.h`
- `VisibleRenderItem.h`
- `VisibleGaussianSplatItem.h`
- `VisibleVolumeItem.h`
- `RenderSceneData.h`
- `RendererListUtils.h`
如果这些文件继续保持 header-only就只迁真实头文件位置并收口 include。
这一轮明确不碰:
- `RenderSceneExtractor.cpp`
- `RenderSceneUtility.cpp`
- `Builtin*Pass` 行为逻辑
- `RenderGraph`
- `RenderPipeline`
除非只是机械性的 include / CMake 路径调整。
## 3. 目标
这一刀至少达到:
- frame data 真实定义迁到 `engine/Runtime/Rendering/FrameData/*`
-`engine/include/XCEngine/Rendering/FrameData/*` public 头收窄成兼容 forwarding 层
- 内部实现侧 include 改用稳定内部路径
- `XCEditor` 编译和 12 秒 editor smoke 继续稳定
## 4. 建议切法
### Step 1先切基础数据头
优先处理:
- `RenderCameraData.h`
- `RenderEnvironmentData.h`
- `CullingResults.h`
原因:
- 是更上层 `RenderSceneData` 的直接基础
- 结构独立、风险最低
### Step 2再切 visible item 与 scene data
随后处理:
- `VisibleRenderItem.h`
- `VisibleGaussianSplatItem.h`
- `VisibleVolumeItem.h`
- `RenderSceneData.h`
原因:
- 构成 render scene 抽取结果的主体数据契约
- 对后续 `Extraction` 收口是前置条件
### Step 3最后切 `RendererListUtils`
统一收口:
- `RendererListUtils.h`
- `engine/CMakeLists.txt`
- 旧 public 头 forwarding
- 内部真实路径 include
- 阶段验证
## 5. 验证要求
本阶段完成后继续保持:
1. 编译 `XCEditor`
2. 执行 12 秒 `editor_ui_smoke_runner`
## 6. 当前状态
截至 `2026-05-01`
- `Runtime/Rendering/Picking` 第一刀已完成
- 下一刀从 `RenderCameraData` 开始

View File

@@ -37,8 +37,8 @@
代码库按执行阶段拆分为:
- `engine/foundation`:基础设施层
- `engine/shared`:跨阶段稳定契约层
- `engine/runtime`:运行时系统
- `engine/Shared`:跨阶段稳定契约层
- `engine/Runtime`:运行时系统
- `pipeline`:导入、构建、烘焙系统
- `editor`:当前编辑器应用树
- `programs`:各类可执行入口
@@ -135,11 +135,11 @@ XCEngine/
passes/
pipelines/
frame_graph/
gpu_cache/
Caches/
audio/
physics/
scripting/
pipeline/
Pipeline/
core/
SourceAssetDB/
ArtifactDB/
@@ -187,7 +187,7 @@ XCEngine/
foundation/
shared/
runtime/
pipeline/
Pipeline/
editor/
e2e/
docs/
@@ -210,7 +210,7 @@ XCEngine/
- 序列化
- 文件系统封装
### 5.2 `engine/shared`
### 5.2 `engine/Shared`
共享契约层承载跨阶段稳定数据结构:
@@ -223,7 +223,7 @@ XCEngine/
这一层只放数据契约,不放 manager、loader、cache、importer、orchestrator。
### 5.3 `engine/runtime`
### 5.3 `engine/Runtime`
运行时层负责:
@@ -282,13 +282,13 @@ editor 内部目录重组不属于第一阶段工作。
当前主要系统映射如下:
- `ResourceManager` -> `engine/runtime/asset/AssetManager`
- `ResourceCache` -> `engine/runtime/asset/ResourceStore`
- `AsyncLoader` -> `engine/runtime/asset/AsyncLoad`
- `ProjectAssetIndex` -> `pipeline/core/SourceIndex`
- `AssetImportService` -> `pipeline/core/ImportService`
- `AssetDatabase` -> `pipeline/core/SourceAssetDB` + `pipeline/core/ArtifactDB`
- `RenderResourceCache` -> `engine/runtime/rendering/gpu_cache`
- `ResourceManager` -> `engine/Runtime/Asset/AssetManager`
- `ResourceCache` -> `engine/Runtime/Asset/ResourceStore`
- `AsyncLoader` -> `engine/Runtime/Asset/AsyncLoad`
- `ProjectAssetIndex` -> `Pipeline/Core/SourceIndex`
- `AssetImportService` -> `Pipeline/Core/ImportService`
- `AssetDatabase` -> `Pipeline/Core/SourceAssetDB` + `Pipeline/Core/ArtifactDB`
- `RenderResourceCache` -> `engine/Runtime/Rendering/Caches`
重构过程中editor 迭代路径先保持在现有项目工作流上,再逐步完成职责分拆。
@@ -375,7 +375,7 @@ xcshadercook
工作项:
- 把文件迁移到 `engine/foundation``engine/shared``engine/runtime``pipeline`
- 把文件迁移到 `engine/foundation``engine/Shared``engine/Runtime``pipeline`
- 更新 include、CMake 和测试
- 除非前置拆分强依赖,否则不在这一阶段重组 editor 内部树
@@ -411,3 +411,29 @@ xcshadercook
4. shared 层只保留稳定数据契约。
5. phases 1 到 3 期间editor 日常迭代路径保持连续可用。
6. 每个阶段完成后都要先编译 `XCEditor`,再执行 12 秒冒烟测试。
## 10. 当前推进状态
截至 `2026-05-01`
- `engine/Shared/Asset` 已承接 `ArtifactContainer``AssetGUID``ResourceTypes``ResourceDependencyGraph` 的真实定义与实现
- `Pipeline/Core` 已承接 `AssetDatabase``AssetImportService``ProjectAssetIndex``ProjectAssetService` 与相关 contracts
- `engine/Runtime/Asset` 已承接 `ResourceManager``ResourceCache``AsyncLoader`
- `engine/Runtime/Resources` 已承接 `Texture``Mesh``AudioClip``Shader``Material`
- 对应旧 `engine/include/XCEngine/Core/Asset/*``engine/include/XCEngine/Resources/*` 的迁移对象已逐步收窄为兼容 forwarding 层
- `engine/Runtime/Scene` 已承接 `Scene``SceneRuntime``SceneManager``RuntimeLoop``ModelSceneInstantiation`
- `Scene` 这一刀已完成 `XCEditor` 编译与 12 秒 editor smoke 验证
当前活跃子计划:
- `Runtime/Rendering/FrameData` 第一刀
下一步优先项:
- `Runtime/Rendering/Caches` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Caches`
- `Runtime/Rendering/Shadow` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Shadow`
- `Runtime/Rendering/Materials` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Materials`
- `Runtime/Rendering/Picking` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Picking`
- 继续收窄旧 `engine/include/XCEngine/Rendering/FrameData/*` public 头
-`RenderCameraData``RenderEnvironmentData``CullingResults``Visible*``RenderSceneData``RendererListUtils` 的真实定义迁到 `engine/Runtime/Rendering/FrameData`
- 在不重组渲染主流程的前提下,继续把低耦合 rendering 支撑块从旧 `include/src` 物理布局中抽离出来

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.15)
if(MSVC AND POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
@@ -101,20 +101,24 @@ endif()
set(XCENGINE_RENDERING_EDITOR_SUPPORT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/ObjectIdCodec.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Picking/RenderObjectIdRegistry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp
)
add_library(XCEngineCore INTERFACE)
target_include_directories(XCEngineCore INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
)
add_library(XCEngineInput INTERFACE)
target_include_directories(XCEngineInput INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
)
@@ -157,6 +161,7 @@ add_library(XCEngineUI STATIC
)
target_include_directories(XCEngineUI PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
)
@@ -183,6 +188,151 @@ else()
target_compile_options(XCEngineUI PRIVATE -Wall)
endif()
set(XCENGINE_SHARED_ASSET_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IResource.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetGUID.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetRef.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactFormats.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactContainer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceDependencyGraph.h
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/AssetGUID/AssetGUID.h
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/AssetGUID/AssetGUID.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ArtifactContainer/ArtifactContainer.h
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ArtifactContainer/ArtifactContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ResourceType/ResourceTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ResourceType/ResourceTypes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/DependencyGraph/ResourceDependencyGraph.h
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/DependencyGraph/ResourceDependencyGraph.cpp
)
set(XCENGINE_PIPELINE_CORE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IProjectAssetResolver.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IProjectAssetPipelineService.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetDatabase.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetImportService.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetIndex.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetService.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/ProjectAssetTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/IProjectAssetResolver.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/IProjectAssetPipelineService.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ImportService/AssetImportService.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ProjectAssetService/ProjectAssetService.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/SourceIndex/ProjectAssetIndex.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Facade/AssetDatabase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ImportService/AssetImportService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ProjectAssetService/ProjectAssetService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/SourceIndex/ProjectAssetIndex.cpp
)
set(XCENGINE_RUNTIME_ASSET_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceHandle.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceManager.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AsyncLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AssetManager/ResourceManager.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AssetManager/ResourceManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/ResourceStore/ResourceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/ResourceStore/ResourceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AsyncLoad/AsyncLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AsyncLoad/AsyncLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/IResourceLoader.cpp
)
function(xcengine_configure_internal_asset_target target_name)
target_include_directories(${target_name} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
${CMAKE_CURRENT_SOURCE_DIR}/third_party
${CMAKE_CURRENT_SOURCE_DIR}/third_party/GLAD/include
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stb
${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/include
)
target_include_directories(${target_name} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/..
)
if(XCENGINE_HAS_NANOVDB)
target_include_directories(${target_name} PRIVATE
${XCENGINE_NANOVDB_INCLUDE_DIR}
)
target_compile_definitions(${target_name} PRIVATE
XCENGINE_HAS_NANOVDB=1
)
endif()
if(XCENGINE_ENABLE_PHYSX)
target_include_directories(${target_name} PRIVATE
${XCENGINE_PHYSX_INCLUDE_DIR}
)
target_compile_definitions(${target_name} PRIVATE
XCENGINE_ENABLE_PHYSX=1
)
else()
target_compile_definitions(${target_name} PRIVATE
XCENGINE_ENABLE_PHYSX=0
)
endif()
target_compile_definitions(${target_name} PRIVATE
XCENGINE_SUPPORT_OPENGL
XCENGINE_SUPPORT_VULKAN
)
if(MSVC)
target_compile_options(${target_name} PRIVATE
/FS
/W3
$<$<CONFIG:Debug,RelWithDebInfo>:/Z7>)
set_target_properties(${target_name} PROPERTIES
MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>"
COMPILE_PDB_NAME "${target_name}-compile"
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb"
COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Debug"
COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Release"
COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/MinSizeRel"
COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/RelWithDebInfo"
)
else()
target_compile_options(${target_name} PRIVATE -Wall)
endif()
endfunction()
add_library(xc_shared_asset STATIC
${XCENGINE_SHARED_ASSET_SOURCES}
)
xcengine_configure_internal_asset_target(xc_shared_asset)
add_library(xc_pipeline_core STATIC
${XCENGINE_PIPELINE_CORE_SOURCES}
)
xcengine_configure_internal_asset_target(xc_pipeline_core)
target_link_libraries(xc_pipeline_core PUBLIC
xc_shared_asset
)
add_library(xc_runtime_asset STATIC
${XCENGINE_RUNTIME_ASSET_SOURCES}
)
xcengine_configure_internal_asset_target(xc_runtime_asset)
target_link_libraries(xc_runtime_asset PUBLIC
xc_shared_asset
)
add_library(XCEngine STATIC
# Core (Types, RefCounted, SmartPtr, Event)
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Types.h
@@ -414,40 +564,12 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIFactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIScreenshot.cpp
# Core/Asset (Resource System Core)
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IResource.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetGUID.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetRef.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactFormats.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactContainer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetDatabase.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetImportService.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetIndex.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceHandle.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceManager.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AsyncLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceDependencyGraph.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetGUID.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ArtifactContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetDatabase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetImportService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ProjectAssetIndex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AsyncLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceTypes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceDependencyGraph.cpp
# Core/IO (File System Abstraction)
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/IResourceLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourcePath.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourceFileSystem.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/FileArchive.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourcePackage.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/IResourceLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/ResourcePath.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/ResourceFileSystem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/FileArchive.cpp
@@ -458,6 +580,9 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/Texture.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/Texture.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/BuiltinResources.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Model/AssimpModelImporter.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Model/Model.h
@@ -469,22 +594,43 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/Mesh.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/Mesh.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshImportSettings.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/Material.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/MaterialLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/MaterialRenderState.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/Material.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialRenderState.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/Shader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderCompilationCache.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Shader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderCompilationCache.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderIR.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderSourceUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderAuthoringParser.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderFileUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderRuntimeBuildUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderArtifactLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringInternal.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Volume/VolumeField.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Volume/VolumeFieldLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioClip/AudioClip.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioClip/AudioLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioClip.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentCompiler.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocuments.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentLoaders.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/Texture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureImportSettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/Texture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureImportSettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/BuiltinResources.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Model/AssimpModelImporter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Model/Model.cpp
@@ -494,36 +640,28 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/GaussianSplatArtifactIO.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/GaussianSplatLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/Internal/GaussianSplatPlyImporter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/Mesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshImportSettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Material/Material.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Material/MaterialLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Shader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderCompilationCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderIR.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderFileUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderFileUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringInternal.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringTextUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringDirectiveUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringShared.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringParserCore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderSourceUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderSourceUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderAuthoringParser.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderAuthoringParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/Mesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshImportSettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/Material.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Shader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderCompilationCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderFileUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderArtifactLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringTextUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringDirectiveUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringShared.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringParserCore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderSourceUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderAuthoringParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Volume/VolumeField.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Volume/VolumeFieldLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioClip/AudioClip.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioClip/AudioLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioClip.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioLoader.cpp
${XCENGINE_OPTIONAL_UI_RESOURCE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/UI/UIDocuments.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/UI/UIDocumentLoaders.cpp
@@ -595,11 +733,20 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/SceneRenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/CullingResults.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderCameraData.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderEnvironmentData.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderSceneData.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RendererListUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleGaussianSplatItem.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleRenderItem.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleVolumeItem.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/CullingResults.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderCameraData.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderEnvironmentData.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderSceneData.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RendererListUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleGaussianSplatItem.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleRenderItem.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleVolumeItem.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/CameraRenderRequest.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/FinalColorPassFactory.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/FinalColorSettings.h
@@ -607,6 +754,8 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/SceneRenderRequestUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/ObjectIdCodec.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneExtractor.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneUtility.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphTypes.h
@@ -619,6 +768,8 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphExecutor.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Materials/RenderMaterialResolve.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Materials/RenderMaterialStateUtils.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPassGraphContract.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipeline.h
@@ -633,8 +784,12 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/SceneRenderSequence.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Caches/RenderResourceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Caches/DirectionalShadowSurfaceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/RenderResourceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Shadow/DirectionalShadowData.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Shadow/DirectionalShadowRuntime.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowData.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowRuntime.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h
@@ -676,7 +831,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPassGraphContract.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPipelineStageGraphContract.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp
@@ -699,7 +854,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraph.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraphCompiler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraphExecutor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/RenderResourceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/RenderResourceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/SceneRenderRequestPlanner.cpp
@@ -707,8 +862,8 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowData.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowRuntime.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowData.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowRuntime.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/NativeSceneRecorder.cpp
@@ -728,11 +883,16 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneRuntime.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/RuntimeLoop.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/ModelSceneInstantiation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneRuntime.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/RuntimeLoop.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/Scene.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/ModelSceneInstantiation.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneRuntime.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/RuntimeLoop.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneManager.h
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/Scene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/ModelSceneInstantiation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneRuntime.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/RuntimeLoop.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneManager.cpp
# Platform
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Platform/PlatformTypes.h
@@ -836,6 +996,7 @@ add_library(XCEngine STATIC
)
target_include_directories(XCEngine PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
${CMAKE_CURRENT_SOURCE_DIR}/third_party
@@ -846,6 +1007,7 @@ target_include_directories(XCEngine PUBLIC
target_include_directories(XCEngine PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/..
)
if(XCENGINE_HAS_NANOVDB)
@@ -866,6 +1028,9 @@ endif()
target_link_libraries(XCEngine PUBLIC
XCEngineUI
xc_shared_asset
xc_pipeline_core
xc_runtime_asset
${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/lib/assimp-vc143-mt.lib
opengl32
Vulkan::Vulkan
@@ -971,6 +1136,7 @@ if(XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT)
)
target_include_directories(XCEngineRenderingEditorSupport PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
${CMAKE_CURRENT_SOURCE_DIR}/src

View File

@@ -1,15 +1,16 @@
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "ResourceManager.h"
#include "Pipeline/Core/ProjectAssetService/ProjectAssetService.h"
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
#include <XCEngine/Core/IO/ResourceFileSystem.h>
#include <XCEngine/Resources/GaussianSplat/GaussianSplatLoader.h>
#include <XCEngine/Resources/AudioClip/AudioLoader.h>
#include "engine/Runtime/Resources/AudioClip/AudioLoader.h"
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include "engine/Runtime/Resources/Material/MaterialLoader.h"
#include <XCEngine/Resources/Model/ModelLoader.h>
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include "engine/Runtime/Resources/Mesh/MeshLoader.h"
#include "engine/Runtime/Resources/Shader/ShaderLoader.h"
#include "engine/Runtime/Resources/Texture/TextureLoader.h"
#include <XCEngine/Resources/UI/UIDocumentLoaders.h>
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
#include <exception>
@@ -81,6 +82,20 @@ void ResourceManager::Initialize() {
EnsureInitialized();
}
void ResourceManager::SetProjectAssetPipelineService(
IProjectAssetPipelineService* projectAssetPipelineService) {
if (m_ownedProjectAssetPipelineService) {
m_ownedProjectAssetPipelineService->Shutdown();
m_ownedProjectAssetPipelineService.reset();
}
m_projectAssetPipelineService = projectAssetPipelineService;
m_projectAssetResolver = projectAssetPipelineService;
if (m_projectAssetPipelineService != nullptr) {
m_projectAssetPipelineService->SetProjectRoot(m_resourceRoot);
}
}
void ResourceManager::EnsureInitialized() {
if (m_asyncLoader) {
return;
@@ -105,7 +120,7 @@ void ResourceManager::EnsureInitialized() {
RegisterBuiltinLoader(*this, g_uiThemeLoader);
RegisterBuiltinLoader(*this, g_uiSchemaLoader);
RegisterBuiltinLoader(*this, g_volumeFieldLoader);
m_assetImportService.Initialize();
EnsureProjectAssetPipelineService();
m_asyncLoader = std::move(asyncLoader);
}
@@ -117,9 +132,14 @@ void ResourceManager::Shutdown() {
m_asyncLoader.reset();
}
m_assetImportService.Shutdown();
if (m_ownedProjectAssetPipelineService) {
m_ownedProjectAssetPipelineService->Shutdown();
m_ownedProjectAssetPipelineService.reset();
}
m_projectAssetPipelineService = nullptr;
m_projectAssetResolver = nullptr;
ResourceFileSystem::Get().Shutdown();
m_projectAssetIndex.ResetProjectRoot();
m_resourceRoot.Clear();
std::lock_guard<std::mutex> inFlightLock(m_inFlightLoadsMutex);
m_inFlightLoads.clear();
@@ -127,15 +147,18 @@ void ResourceManager::Shutdown() {
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
EnsureInitialized();
IProjectAssetPipelineService& projectAssetPipelineService =
EnsureProjectAssetPipelineService();
m_resourceRoot = rootPath;
if (!m_resourceRoot.Empty()) {
ResourceFileSystem::Get().Initialize(rootPath);
m_assetImportService.SetProjectRoot(rootPath);
BootstrapProjectAssets();
projectAssetPipelineService.SetProjectRoot(rootPath);
if (m_ownedProjectAssetPipelineService) {
projectAssetPipelineService.BootstrapProject();
}
} else {
m_assetImportService.SetProjectRoot(Containers::String());
projectAssetPipelineService.SetProjectRoot(Containers::String());
ResourceFileSystem::Get().Shutdown();
m_projectAssetIndex.ResetProjectRoot();
}
}
@@ -143,16 +166,6 @@ const Containers::String& ResourceManager::GetResourceRoot() const {
return m_resourceRoot;
}
bool ResourceManager::BootstrapProjectAssets() {
if (m_resourceRoot.Empty()) {
return false;
}
const bool bootstrapped = m_assetImportService.BootstrapProject();
m_projectAssetIndex.RefreshFrom(m_assetImportService);
return bootstrapped;
}
void ResourceManager::AddRef(ResourceGUID guid) {
std::lock_guard lock(m_mutex);
@@ -415,71 +428,11 @@ void ResourceManager::UnloadGroup(const Containers::Array<ResourceGUID>& guids)
}
}
void ResourceManager::RefreshProjectAssets() {
if (!m_resourceRoot.Empty()) {
m_assetImportService.Refresh();
m_projectAssetIndex.RefreshFrom(m_assetImportService);
}
}
bool ResourceManager::CanReimportProjectAsset(const Containers::String& path) const {
if (m_resourceRoot.Empty() || path.Empty()) {
return false;
}
ResourceType importType = ResourceType::Unknown;
return m_assetImportService.TryGetImportableResourceType(path, importType);
}
bool ResourceManager::ReimportProjectAsset(const Containers::String& path) {
if (m_resourceRoot.Empty() || path.Empty()) {
return false;
}
UnloadAll();
AssetImportService::ImportedAsset importedAsset;
const bool reimported = m_assetImportService.ReimportAsset(path, importedAsset);
m_projectAssetIndex.RefreshFrom(m_assetImportService);
if (reimported && importedAsset.assetGuid.IsValid() && !importedAsset.relativePath.Empty()) {
m_projectAssetIndex.RememberResolvedPath(importedAsset.assetGuid, importedAsset.relativePath);
}
return reimported;
}
bool ResourceManager::ClearProjectLibraryCache() {
if (m_resourceRoot.Empty()) {
return false;
}
UnloadAll();
const bool cleared = m_assetImportService.ClearLibraryCache();
m_projectAssetIndex.RefreshFrom(m_assetImportService);
return cleared;
}
bool ResourceManager::RebuildProjectAssetCache() {
if (m_resourceRoot.Empty()) {
return false;
}
UnloadAll();
const bool rebuilt = m_assetImportService.RebuildLibraryCache();
m_projectAssetIndex.RefreshFrom(m_assetImportService);
return rebuilt;
}
Containers::String ResourceManager::GetProjectLibraryRoot() const {
return m_assetImportService.GetLibraryRoot();
}
AssetImportService::ImportStatusSnapshot ResourceManager::GetProjectAssetImportStatus() const {
return m_assetImportService.GetLastImportStatus();
}
bool ResourceManager::TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const {
const bool resolved = m_projectAssetIndex.TryGetAssetRef(m_assetImportService, path, resourceType, outRef);
const IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
const bool resolved =
projectAssetResolver != nullptr &&
projectAssetResolver->TryGetAssetRef(path, resourceType, outRef);
if (ShouldTraceResourcePath(path)) {
Debug::Logger::Get().Info(
@@ -497,7 +450,10 @@ bool ResourceManager::TryGetAssetRef(const Containers::String& path, ResourceTyp
}
bool ResourceManager::TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath) const {
const bool resolved = m_projectAssetIndex.TryResolveAssetPath(m_assetImportService, assetRef, outPath);
const IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
const bool resolved =
projectAssetResolver != nullptr &&
projectAssetResolver->TryResolveAssetPath(assetRef, outPath);
if (resolved && ShouldTraceResourcePath(outPath)) {
Debug::Logger::Get().Info(
@@ -527,6 +483,18 @@ bool ResourceManager::IsDeferredSceneLoadEnabled() const {
return m_deferredSceneLoadDepth.load() > 0;
}
IProjectAssetPipelineService& ResourceManager::EnsureProjectAssetPipelineService() const {
if (m_projectAssetPipelineService == nullptr) {
auto projectAssetPipelineService = Core::MakeUnique<ProjectAssetService>();
projectAssetPipelineService->Initialize();
m_projectAssetPipelineService = projectAssetPipelineService.get();
m_projectAssetResolver = projectAssetPipelineService.get();
m_ownedProjectAssetPipelineService = std::move(projectAssetPipelineService);
}
return *m_projectAssetPipelineService;
}
LoadResult ResourceManager::LoadResource(const Containers::String& path,
ResourceType type,
ImportSettings* settings) {
@@ -553,17 +521,18 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
Containers::String loadPath = path;
Containers::String cachePath = path;
AssetImportService::ImportedAsset resolvedAsset;
ProjectAssetImportedAsset resolvedAsset;
ResourceType importableType = ResourceType::Unknown;
IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
const bool shouldUseProjectArtifact =
!m_resourceRoot.Empty() &&
m_assetImportService.TryGetImportableResourceType(path, importableType) &&
projectAssetResolver != nullptr &&
projectAssetResolver->TryGetImportableResourceType(path, importableType) &&
importableType == type;
if (shouldUseProjectArtifact &&
m_assetImportService.EnsureArtifact(path, type, resolvedAsset) &&
projectAssetResolver->EnsureArtifact(path, type, resolvedAsset) &&
resolvedAsset.artifactReady) {
m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath);
loadPath = resolvedAsset.runtimeLoadPath;
cachePath = loadPath;
if (ShouldTraceResourcePath(path)) {
@@ -578,13 +547,14 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
Containers::String builtinShaderAssetPath;
if (TryResolveBuiltinShaderAssetPath(path, builtinShaderAssetPath)) {
ResourceType builtinImportableType = ResourceType::Unknown;
AssetImportService::ImportedAsset builtinResolvedAsset;
ProjectAssetImportedAsset builtinResolvedAsset;
if (!m_resourceRoot.Empty() &&
m_assetImportService.TryGetImportableResourceType(
projectAssetResolver != nullptr &&
projectAssetResolver->TryGetImportableResourceType(
builtinShaderAssetPath,
builtinImportableType) &&
builtinImportableType == type &&
m_assetImportService.EnsureArtifact(
projectAssetResolver->EnsureArtifact(
builtinShaderAssetPath,
type,
builtinResolvedAsset) &&

View File

@@ -0,0 +1,185 @@
#pragma once
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Asset/ResourceCache.h>
#include <XCEngine/Core/Asset/AsyncLoader.h>
#include <XCEngine/Core/Asset/ImportSettings.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/HashMap.h>
#include <XCEngine/Threading/Mutex.h>
#include <XCEngine/Debug/Logger.h>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <type_traits>
namespace XCEngine {
namespace Resources {
class ResourceManager {
public:
class ScopedDeferredSceneLoad {
public:
explicit ScopedDeferredSceneLoad(ResourceManager& manager = ResourceManager::Get());
ScopedDeferredSceneLoad(const ScopedDeferredSceneLoad&) = delete;
ScopedDeferredSceneLoad& operator=(const ScopedDeferredSceneLoad&) = delete;
~ScopedDeferredSceneLoad();
private:
ResourceManager* m_manager = nullptr;
};
static ResourceManager& Get();
void Initialize();
void Shutdown();
void SetProjectAssetPipelineService(IProjectAssetPipelineService* projectAssetPipelineService);
void SetResourceRoot(const Containers::String& rootPath);
const Containers::String& GetResourceRoot() const;
template<typename T>
ResourceHandle<T> Load(const Containers::String& path, ImportSettings* settings = nullptr) {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
LoadResult result = LoadResource(path, GetResourceType<T>(), settings);
if (!result || result.resource == nullptr) {
return ResourceHandle<T>();
}
return ResourceHandle<T>(static_cast<T*>(result.resource));
}
template<typename T>
ResourceHandle<T> Load(const AssetRef& assetRef, ImportSettings* settings = nullptr) {
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
Containers::String path;
if (!TryResolveAssetPath(assetRef, path)) {
return ResourceHandle<T>();
}
return Load<T>(path, settings);
}
void LoadAsync(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback);
void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
void UpdateAsyncLoads();
bool IsAsyncLoading() const;
Core::uint32 GetAsyncPendingCount() const;
void Unload(const Containers::String& path);
void Unload(ResourceGUID guid);
void UnloadUnused();
void UnloadAll();
void AddRef(ResourceGUID guid);
void Release(ResourceGUID guid);
Core::uint32 GetRefCount(ResourceGUID guid) const;
void RegisterLoader(IResourceLoader* loader);
void UnregisterLoader(ResourceType type);
IResourceLoader* GetLoader(ResourceType type) const;
void SetMemoryBudget(size_t bytes);
size_t GetMemoryUsage() const;
size_t GetMemoryBudget() const;
void FlushCache();
IResource* Find(const Containers::String& path);
IResource* Find(ResourceGUID guid);
bool Exists(const Containers::String& path) const;
bool Exists(ResourceGUID guid) const;
Containers::String ResolvePath(const Containers::String& relativePath) const;
template<typename T>
void LoadGroup(const Containers::Array<Containers::String>& paths,
std::function<void(ResourceHandle<T>)> callback) {
for (const auto& path : paths) {
LoadAsync(path, GetResourceType<T>(), [callback](LoadResult result) {
if (result && result.resource) {
callback(ResourceHandle<T>(static_cast<T*>(result.resource)));
} else {
callback(ResourceHandle<T>());
}
});
}
}
Containers::Array<Containers::String> GetResourcePaths() const;
void UnloadGroup(const Containers::Array<ResourceGUID>& guids);
bool TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const;
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath) const;
void BeginDeferredSceneLoad();
void EndDeferredSceneLoad();
bool IsDeferredSceneLoadEnabled() const;
private:
struct InFlightLoadKey {
ResourceGUID guid;
ResourceType type = ResourceType::Unknown;
bool operator==(const InFlightLoadKey& other) const {
return guid == other.guid && type == other.type;
}
};
struct InFlightLoadKeyHasher {
size_t operator()(const InFlightLoadKey& key) const noexcept {
return std::hash<ResourceGUID>{}(key.guid) ^
(static_cast<size_t>(key.type) << 1);
}
};
struct InFlightLoadState {
bool completed = false;
bool success = false;
Containers::String errorMessage;
std::condition_variable condition;
};
ResourceManager() = default;
~ResourceManager() = default;
void EnsureInitialized();
IResource* FindInCache(ResourceGUID guid);
void AddToCache(ResourceGUID guid, IResource* resource);
IResourceLoader* FindLoader(ResourceType type);
void ReloadResource(ResourceGUID guid);
LoadResult LoadResource(const Containers::String& path, ResourceType type, ImportSettings* settings);
IProjectAssetPipelineService& EnsureProjectAssetPipelineService() const;
Containers::String m_resourceRoot;
Containers::HashMap<ResourceGUID, IResource*> m_resourceCache;
Containers::HashMap<ResourceGUID, Core::uint32> m_refCounts;
Containers::HashMap<ResourceGUID, Containers::String> m_guidToPath;
Containers::HashMap<ResourceType, IResourceLoader*> m_loaders;
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024;
mutable Core::UniqueRef<IProjectAssetPipelineService> m_ownedProjectAssetPipelineService;
mutable IProjectAssetPipelineService* m_projectAssetPipelineService = nullptr;
mutable IProjectAssetResolver* m_projectAssetResolver = nullptr;
ResourceCache m_cache;
Core::UniqueRef<AsyncLoader> m_asyncLoader;
Threading::Mutex m_mutex;
std::mutex m_initializeMutex;
std::mutex m_inFlightLoadsMutex;
std::unordered_map<InFlightLoadKey, std::shared_ptr<InFlightLoadState>, InFlightLoadKeyHasher> m_inFlightLoads;
std::atomic<Core::uint32> m_deferredSceneLoadDepth{0};
friend class ResourceHandleBase;
friend class AsyncLoader;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,6 @@
#include <XCEngine/Core/Asset/AsyncLoader.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include "AsyncLoader.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
#include <XCEngine/Debug/Logger.h>
namespace XCEngine {

View File

@@ -0,0 +1,108 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Core/Asset/ImportSettings.h>
#include <atomic>
#include <condition_variable>
#include <deque>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
namespace XCEngine {
namespace Resources {
struct LoadRequest {
Containers::String path;
ResourceType type;
std::function<void(LoadResult)> callback;
ImportSettings* settings;
Core::uint64 requestId;
LoadRequest() : requestId(0), settings(nullptr) {}
LoadRequest(const Containers::String& p, ResourceType t,
std::function<void(LoadResult)> cb, ImportSettings* s = nullptr)
: path(p), type(t), callback(std::move(cb)),
settings(s), requestId(GenerateRequestId()) {}
LoadRequest(LoadRequest&& other) noexcept
: path(std::move(other.path)), type(other.type),
callback(std::move(other.callback)), settings(other.settings),
requestId(other.requestId) {
other.settings = nullptr;
}
LoadRequest& operator=(LoadRequest&& other) noexcept {
if (this != &other) {
path = std::move(other.path);
type = other.type;
callback = std::move(other.callback);
settings = other.settings;
requestId = other.requestId;
other.settings = nullptr;
}
return *this;
}
LoadRequest(const LoadRequest&) = default;
LoadRequest& operator=(const LoadRequest&) = default;
private:
static Core::uint64 GenerateRequestId();
};
struct CompletedLoadRequest {
LoadRequest request;
LoadResult result;
CompletedLoadRequest(LoadRequest inRequest, LoadResult inResult)
: request(std::move(inRequest)), result(std::move(inResult)) {}
};
class AsyncLoader {
public:
static AsyncLoader& Get();
void Initialize(Core::uint32 workerThreadCount = 2);
void Shutdown();
void Submit(const Containers::String& path, ResourceType type,
std::function<void(LoadResult)> callback);
void Submit(const Containers::String& path, ResourceType type, ImportSettings* settings,
std::function<void(LoadResult)> callback);
void Update();
bool IsLoading() const { return m_pendingCount > 0; }
Core::uint32 GetPendingCount() const { return m_pendingCount; }
float GetProgress() const;
void CancelAll();
void Cancel(Core::uint64 requestId);
~AsyncLoader() = default;
AsyncLoader() = default;
private:
void SubmitInternal(LoadRequest request);
IResourceLoader* FindLoader(ResourceType type) const;
void WorkerThread();
void QueueCompleted(LoadRequest request, LoadResult result);
std::mutex m_queueMutex;
std::condition_variable m_pendingCondition;
std::deque<LoadRequest> m_pendingQueue;
std::mutex m_completedMutex;
std::deque<CompletedLoadRequest> m_completedQueue;
std::vector<std::thread> m_workerThreads;
std::atomic<bool> m_running{false};
std::atomic<Core::uint32> m_pendingCount{0};
std::atomic<Core::uint32> m_completedCount{0};
std::atomic<Core::uint64> m_totalRequested{0};
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,5 +1,6 @@
#include <XCEngine/Core/Asset/ResourceCache.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "ResourceCache.h"
#include <XCEngine/Core/Asset/IResource.h>
namespace XCEngine {
namespace Resources {

View File

@@ -0,0 +1,64 @@
#pragma once
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/HashMap.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Threading/Mutex.h>
#include <algorithm>
namespace XCEngine {
namespace Resources {
class IResource;
struct CacheEntry {
IResource* resource;
ResourceGUID guid;
size_t memorySize;
Core::uint64 lastAccessTime;
Core::uint32 accessCount;
CacheEntry() : resource(nullptr), memorySize(0), lastAccessTime(0), accessCount(0) {}
CacheEntry(IResource* res, size_t size);
static Core::uint64 GetCurrentTick();
};
class ResourceCache {
public:
ResourceCache();
~ResourceCache();
void Add(ResourceGUID guid, IResource* resource);
void Remove(ResourceGUID guid);
IResource* Find(ResourceGUID guid) const;
void Touch(ResourceGUID guid);
size_t GetSize() const { return m_cache.Size(); }
size_t GetMemoryUsage() const { return m_memoryUsage; }
void SetMemoryBudget(size_t bytes);
size_t GetMemoryBudget() const { return m_memoryBudget; }
void OnMemoryPressure(size_t requiredBytes);
void OnZeroRefCount(ResourceGUID guid);
void Flush();
void Clear();
Containers::Array<ResourceGUID> GetLRUList(size_t count) const;
private:
void Evict(size_t requiredBytes);
void UpdateMemoryStats();
Containers::HashMap<ResourceGUID, CacheEntry> m_cache;
Containers::Array<ResourceGUID> m_lruOrder;
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024;
Threading::Mutex m_mutex;
mutable Threading::Mutex m_cacheMutex;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,7 +1,7 @@
#include "Rendering/Caches/DirectionalShadowSurfaceCache.h"
#include "DirectionalShadowSurfaceCache.h"
#include "Rendering/RenderContext.h"
#include "Rendering/Shadow/DirectionalShadowData.h"
#include "engine/Runtime/Rendering/Shadow/DirectionalShadowData.h"
#include "RHI/RHIDevice.h"
#include "RHI/RHIResourceView.h"
#include "RHI/RHITexture.h"

View File

@@ -0,0 +1,51 @@
#pragma once
#include <XCEngine/Rendering/RenderSurface.h>
namespace XCEngine {
namespace RHI {
class RHIDevice;
class RHIResourceView;
class RHITexture;
} // namespace RHI
namespace Rendering {
struct DirectionalShadowRenderPlan;
struct RenderContext;
struct DirectionalShadowSurfaceAllocation {
RenderSurface surface = {};
RHI::RHIResourceView* depthShaderView = nullptr;
bool IsValid() const {
return surface.GetDepthAttachment() != nullptr &&
depthShaderView != nullptr;
}
};
class DirectionalShadowSurfaceCache {
public:
DirectionalShadowSurfaceCache() = default;
DirectionalShadowSurfaceCache(const DirectionalShadowSurfaceCache&) = delete;
DirectionalShadowSurfaceCache& operator=(const DirectionalShadowSurfaceCache&) = delete;
~DirectionalShadowSurfaceCache();
const DirectionalShadowSurfaceAllocation* Resolve(
const RenderContext& context,
const DirectionalShadowRenderPlan& plan);
private:
bool Matches(const RenderContext& context, const DirectionalShadowRenderPlan& plan) const;
void Reset();
RHI::RHIDevice* m_device = nullptr;
uint32_t m_width = 0;
uint32_t m_height = 0;
RHI::RHITexture* m_depthTexture = nullptr;
RHI::RHIResourceView* m_depthView = nullptr;
DirectionalShadowSurfaceAllocation m_allocation = {};
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include "Rendering/Caches/RenderResourceCache.h"
#include "RenderResourceCache.h"
#include "Debug/Logger.h"

View File

@@ -0,0 +1,160 @@
#pragma once
#include <XCEngine/RHI/RHIBuffer.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Resources/GaussianSplat/GaussianSplat.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Resources/Texture/Texture.h>
#include <XCEngine/Resources/Volume/VolumeField.h>
#include <unordered_map>
namespace XCEngine {
namespace Rendering {
class RenderResourceCache {
public:
enum class GaussianSplatResidencyState {
Uninitialized = 0,
CpuReady,
GpuUploading,
GpuReady,
Failed
};
struct CachedMesh {
RHI::RHIBuffer* vertexBuffer = nullptr;
RHI::RHIResourceView* vertexBufferView = nullptr;
RHI::RHIBuffer* indexBuffer = nullptr;
RHI::RHIResourceView* indexBufferView = nullptr;
uint32_t vertexCount = 0;
uint32_t indexCount = 0;
uint32_t vertexStride = 0;
bool uses32BitIndices = false;
};
struct CachedTexture {
RHI::RHITexture* texture = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
uint32_t width = 0;
uint32_t height = 0;
};
struct CachedBufferView {
RHI::RHIResourceView* resourceView = nullptr;
};
struct CachedVolumeField {
RHI::RHIBuffer* payloadBuffer = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
uint32_t elementStride = 0;
uint32_t elementCount = 0;
uint64_t payloadSize = 0;
Resources::VolumeStorageKind storageKind = Resources::VolumeStorageKind::Unknown;
};
struct CachedGaussianSplatSection {
RHI::RHIBuffer* buffer = nullptr;
RHI::RHIResourceView* shaderResourceView = nullptr;
Resources::GaussianSplatSectionType type = Resources::GaussianSplatSectionType::Unknown;
Resources::GaussianSplatSectionFormat format = Resources::GaussianSplatSectionFormat::Unknown;
uint32_t elementStride = 0;
uint32_t elementCount = 0;
uint64_t payloadSize = 0;
};
struct CachedGaussianSplat {
GaussianSplatResidencyState residencyState = GaussianSplatResidencyState::Uninitialized;
uint32_t contentVersion = 0;
uint32_t splatCount = 0;
uint32_t chunkCount = 0;
uint32_t cameraCount = 0;
Math::Bounds bounds;
CachedGaussianSplatSection positions;
CachedGaussianSplatSection other;
CachedGaussianSplatSection color;
CachedGaussianSplatSection sh;
CachedGaussianSplatSection chunks;
};
~RenderResourceCache();
void Shutdown();
const CachedMesh* GetOrCreateMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh);
const CachedTexture* GetOrCreateTexture(RHI::RHIDevice* device, const Resources::Texture* texture);
const CachedVolumeField* GetOrCreateVolumeField(
RHI::RHIDevice* device,
const Resources::VolumeField* volumeField);
const CachedGaussianSplat* GetOrCreateGaussianSplat(
RHI::RHIDevice* device,
const Resources::GaussianSplat* gaussianSplat);
const CachedBufferView* GetOrCreateBufferView(
RHI::RHIDevice* device,
RHI::RHIBuffer* buffer,
RHI::ResourceViewType viewType,
const RHI::ResourceViewDesc& viewDesc);
private:
struct BufferViewCacheKey {
const RHI::RHIBuffer* buffer = nullptr;
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
uint32_t format = 0;
RHI::ResourceViewDimension dimension = RHI::ResourceViewDimension::Unknown;
uint64_t bufferLocation = 0;
uint32_t firstElement = 0;
uint32_t elementCount = 0;
uint32_t structureByteStride = 0;
bool operator==(const BufferViewCacheKey& other) const {
return buffer == other.buffer &&
viewType == other.viewType &&
format == other.format &&
dimension == other.dimension &&
bufferLocation == other.bufferLocation &&
firstElement == other.firstElement &&
elementCount == other.elementCount &&
structureByteStride == other.structureByteStride;
}
};
struct BufferViewCacheKeyHash {
size_t operator()(const BufferViewCacheKey& key) const noexcept;
};
bool UploadMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh, CachedMesh& cachedMesh);
bool UploadTexture(RHI::RHIDevice* device, const Resources::Texture* texture, CachedTexture& cachedTexture);
bool UploadVolumeField(
RHI::RHIDevice* device,
const Resources::VolumeField* volumeField,
CachedVolumeField& cachedVolumeField);
bool UploadGaussianSplat(
RHI::RHIDevice* device,
const Resources::GaussianSplat* gaussianSplat,
CachedGaussianSplat& cachedGaussianSplat);
bool UploadGaussianSplatSection(
RHI::RHIDevice* device,
const Resources::GaussianSplat* gaussianSplat,
Resources::GaussianSplatSectionType sectionType,
Resources::GaussianSplatSectionFormat requiredFormat,
uint32_t requiredStride,
bool requiredSection,
CachedGaussianSplatSection& cachedSection);
bool CreateBufferView(
RHI::RHIDevice* device,
RHI::RHIBuffer* buffer,
RHI::ResourceViewType viewType,
const RHI::ResourceViewDesc& viewDesc,
CachedBufferView& cachedBufferView);
std::unordered_map<const Resources::Mesh*, CachedMesh> m_meshCache;
std::unordered_map<const Resources::Texture*, CachedTexture> m_textureCache;
std::unordered_map<const Resources::VolumeField*, CachedVolumeField> m_volumeFieldCache;
std::unordered_map<const Resources::GaussianSplat*, CachedGaussianSplat> m_gaussianSplatCache;
std::unordered_map<BufferViewCacheKey, CachedBufferView, BufferViewCacheKeyHash> m_bufferViewCache;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,104 @@
#pragma once
#include <XCEngine/Core/Types.h>
#include <cstddef>
#include <limits>
#include <vector>
namespace XCEngine {
namespace Rendering {
enum class RendererListType : Core::uint32 {
AllVisible = 0,
Opaque = 1,
Transparent = 2,
ShadowCaster = 3,
ObjectId = 4
};
enum class RendererSortMode : Core::uint32 {
None = 0,
FrontToBack = 1,
BackToFront = 2
};
struct FilteringSettings {
Core::int32 renderQueueMin = std::numeric_limits<Core::int32>::lowest();
Core::int32 renderQueueMax = std::numeric_limits<Core::int32>::max();
Core::uint32 renderLayerMask = std::numeric_limits<Core::uint32>::max();
bool requireShadowCasting = false;
bool requireRenderObjectId = false;
bool operator==(const FilteringSettings& other) const {
return renderQueueMin == other.renderQueueMin &&
renderQueueMax == other.renderQueueMax &&
renderLayerMask == other.renderLayerMask &&
requireShadowCasting == other.requireShadowCasting &&
requireRenderObjectId == other.requireRenderObjectId;
}
};
struct SortingSettings {
RendererSortMode sortMode = RendererSortMode::None;
bool operator==(const SortingSettings& other) const {
return sortMode == other.sortMode;
}
};
struct RendererListDesc {
RendererListType type = RendererListType::AllVisible;
FilteringSettings filtering = {};
SortingSettings sorting = {};
bool operator==(const RendererListDesc& other) const {
return type == other.type &&
filtering == other.filtering &&
sorting == other.sorting;
}
};
struct RendererList {
RendererListDesc desc = {};
std::vector<Core::uint32> visibleRenderItemIndices;
bool Empty() const {
return visibleRenderItemIndices.empty();
}
size_t Size() const {
return visibleRenderItemIndices.size();
}
};
struct CullingResults {
std::vector<RendererList> rendererLists;
void Clear() {
rendererLists.clear();
}
RendererList* FindRendererList(RendererListType type) {
for (RendererList& rendererList : rendererLists) {
if (rendererList.desc.type == type) {
return &rendererList;
}
}
return nullptr;
}
const RendererList* FindRendererList(RendererListType type) const {
for (const RendererList& rendererList : rendererLists) {
if (rendererList.desc.type == type) {
return &rendererList;
}
}
return nullptr;
}
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,54 @@
#pragma once
#include <XCEngine/Core/Math/Color.h>
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <cstdint>
namespace XCEngine {
namespace Rendering {
enum class RenderClearFlags : uint8_t {
None = 0,
Color = 1 << 0,
Depth = 1 << 1,
All = (1 << 0) | (1 << 1)
};
constexpr RenderClearFlags operator|(RenderClearFlags lhs, RenderClearFlags rhs) {
return static_cast<RenderClearFlags>(
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
constexpr RenderClearFlags operator&(RenderClearFlags lhs, RenderClearFlags rhs) {
return static_cast<RenderClearFlags>(
static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr bool HasRenderClearFlag(RenderClearFlags flags, RenderClearFlags flag) {
return static_cast<uint8_t>(flags & flag) != 0;
}
struct RenderCameraData {
Math::Matrix4x4 view = Math::Matrix4x4::Identity();
Math::Matrix4x4 projection = Math::Matrix4x4::Identity();
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
Math::Vector3 worldPosition = Math::Vector3::Zero();
Math::Vector3 worldRight = Math::Vector3::Right();
Math::Vector3 worldUp = Math::Vector3::Up();
Math::Vector3 worldForward = Math::Vector3::Forward();
Math::Color clearColor = Math::Color::Black();
RenderClearFlags clearFlags = RenderClearFlags::All;
bool perspectiveProjection = true;
float verticalFovRadians = 60.0f * Math::DEG_TO_RAD;
float orthographicSize = 5.0f;
float aspectRatio = 1.0f;
float nearClipPlane = 0.1f;
float farClipPlane = 1000.0f;
uint32_t viewportWidth = 0;
uint32_t viewportHeight = 0;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,55 @@
#pragma once
#include <XCEngine/Core/Math/Color.h>
#include <cstdint>
namespace XCEngine {
namespace Resources {
class Material;
} // namespace Resources
} // namespace XCEngine
namespace XCEngine {
namespace Rendering {
enum class RenderEnvironmentMode : uint32_t {
None = 0,
ProceduralSkybox,
MaterialSkybox
};
struct ProceduralSkyboxData {
Math::Color topColor = Math::Color(0.18f, 0.36f, 0.74f, 1.0f);
Math::Color horizonColor = Math::Color(0.78f, 0.84f, 0.92f, 1.0f);
Math::Color bottomColor = Math::Color(0.92f, 0.93f, 0.95f, 1.0f);
};
struct MaterialSkyboxData {
const Resources::Material* material = nullptr;
bool IsValid() const {
return material != nullptr;
}
};
struct RenderEnvironmentData {
RenderEnvironmentMode mode = RenderEnvironmentMode::None;
ProceduralSkyboxData skybox = {};
MaterialSkyboxData materialSkybox = {};
bool HasProceduralSkybox() const {
return mode == RenderEnvironmentMode::ProceduralSkybox;
}
bool HasMaterialSkybox() const {
return mode == RenderEnvironmentMode::MaterialSkybox && materialSkybox.IsValid();
}
bool HasSkybox() const {
return HasProceduralSkybox() || HasMaterialSkybox();
}
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,107 @@
#pragma once
#include <XCEngine/Core/Math/Vector2.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Resources/Shader/ShaderKeywordTypes.h>
#include "engine/Runtime/Rendering/FrameData/CullingResults.h"
#include "engine/Runtime/Rendering/FrameData/RenderCameraData.h"
#include "engine/Runtime/Rendering/FrameData/RenderEnvironmentData.h"
#include "engine/Runtime/Rendering/FrameData/VisibleGaussianSplatItem.h"
#include "engine/Runtime/Rendering/FrameData/VisibleRenderItem.h"
#include "engine/Runtime/Rendering/FrameData/VisibleVolumeItem.h"
#include "engine/Runtime/Rendering/Shadow/DirectionalShadowData.h"
#include <array>
#include <cstdint>
#include <vector>
namespace XCEngine {
namespace Components {
class CameraComponent;
} // namespace Components
namespace Rendering {
struct RenderDirectionalLightData {
bool enabled = false;
bool castsShadows = false;
Math::Vector3 direction = Math::Vector3::Back();
float intensity = 1.0f;
Math::Color color = Math::Color::White();
};
enum class RenderLightType : uint32_t {
Directional = 0,
Point = 1,
Spot = 2
};
struct RenderAdditionalLightData {
RenderLightType type = RenderLightType::Point;
bool enabled = false;
bool castsShadows = false;
Math::Color color = Math::Color::White();
float intensity = 1.0f;
Math::Vector3 position = Math::Vector3::Zero();
Math::Vector3 direction = Math::Vector3::Back();
float range = 0.0f;
float spotAngle = 0.0f;
};
struct RenderLightingData {
static constexpr uint32_t kMaxAdditionalLightCount = 8u;
RenderDirectionalLightData mainDirectionalLight;
RenderDirectionalShadowData mainDirectionalShadow;
std::array<RenderAdditionalLightData, kMaxAdditionalLightCount> additionalLights = {};
uint32_t additionalLightCount = 0u;
bool HasMainDirectionalLight() const {
return mainDirectionalLight.enabled;
}
bool HasMainDirectionalShadow() const {
return mainDirectionalShadow.IsValid();
}
bool HasAdditionalLights() const {
return additionalLightCount > 0u;
}
};
struct RenderSceneData {
Components::CameraComponent* camera = nullptr;
RenderCameraData cameraData;
RenderEnvironmentData environment;
RenderLightingData lighting;
Resources::ShaderKeywordSet globalShaderKeywords;
CullingResults cullingResults;
std::vector<VisibleRenderItem> visibleItems;
std::vector<VisibleGaussianSplatItem> visibleGaussianSplats;
std::vector<VisibleVolumeItem> visibleVolumes;
bool HasCamera() const {
return camera != nullptr;
}
RendererList* FindRendererList(RendererListType type) {
return cullingResults.FindRendererList(type);
}
const RendererList* FindRendererList(RendererListType type) const {
return cullingResults.FindRendererList(type);
}
const VisibleRenderItem* TryGetVisibleRenderItem(Core::uint32 index) const {
if (index >= visibleItems.size()) {
return nullptr;
}
return &visibleItems[index];
}
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,219 @@
#pragma once
#include <XCEngine/Components/MeshRendererComponent.h>
#include <XCEngine/Resources/Material/Material.h>
#include "engine/Runtime/Rendering/FrameData/RenderSceneData.h"
#include <algorithm>
#include <utility>
#include <vector>
namespace XCEngine {
namespace Rendering {
inline FilteringSettings BuildDefaultFilteringSettings(RendererListType type) {
FilteringSettings filtering = {};
switch (type) {
case RendererListType::AllVisible:
break;
case RendererListType::Opaque:
filtering.renderQueueMax =
static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent) - 1;
break;
case RendererListType::Transparent:
filtering.renderQueueMin =
static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
break;
case RendererListType::ShadowCaster:
filtering.requireShadowCasting = true;
break;
case RendererListType::ObjectId:
filtering.requireRenderObjectId = true;
break;
}
return filtering;
}
inline SortingSettings BuildDefaultSortingSettings(RendererListType type) {
SortingSettings sorting = {};
switch (type) {
case RendererListType::Opaque:
case RendererListType::ShadowCaster:
case RendererListType::ObjectId:
sorting.sortMode = RendererSortMode::FrontToBack;
break;
case RendererListType::Transparent:
sorting.sortMode = RendererSortMode::BackToFront;
break;
case RendererListType::AllVisible:
sorting.sortMode = RendererSortMode::None;
break;
}
return sorting;
}
inline RendererListDesc BuildDefaultRendererListDesc(RendererListType type) {
RendererListDesc desc = {};
desc.type = type;
desc.filtering = BuildDefaultFilteringSettings(type);
desc.sorting = BuildDefaultSortingSettings(type);
return desc;
}
inline bool MatchesFilteringSettings(
const VisibleRenderItem& visibleItem,
const FilteringSettings& filtering) {
if (visibleItem.renderQueue < filtering.renderQueueMin ||
visibleItem.renderQueue > filtering.renderQueueMax) {
return false;
}
if (visibleItem.renderLayer >= 32u ||
((filtering.renderLayerMask &
(1u << visibleItem.renderLayer)) == 0u)) {
return false;
}
if (filtering.requireShadowCasting &&
visibleItem.meshRenderer != nullptr &&
!visibleItem.meshRenderer->GetCastShadows()) {
return false;
}
if (filtering.requireRenderObjectId &&
!IsValidRenderObjectId(visibleItem.renderObjectId)) {
return false;
}
return true;
}
inline bool MatchesRendererListDesc(
const VisibleRenderItem& visibleItem,
const RendererListDesc& desc) {
return MatchesFilteringSettings(visibleItem, desc.filtering);
}
inline RendererList BuildRendererList(
RendererListType type,
const std::vector<VisibleRenderItem>& visibleItems) {
RendererList rendererList = {};
rendererList.desc = BuildDefaultRendererListDesc(type);
rendererList.visibleRenderItemIndices.reserve(visibleItems.size());
for (Core::uint32 visibleItemIndex = 0;
visibleItemIndex < static_cast<Core::uint32>(visibleItems.size());
++visibleItemIndex) {
if (!MatchesRendererListDesc(
visibleItems[visibleItemIndex],
rendererList.desc)) {
continue;
}
rendererList.visibleRenderItemIndices.push_back(visibleItemIndex);
}
return rendererList;
}
template <typename Visitor>
inline void VisitRendererListVisibleItems(
const RenderSceneData& sceneData,
RendererListType rendererListType,
Visitor&& visitor) {
const RendererList* rendererList = sceneData.FindRendererList(rendererListType);
if (rendererList != nullptr) {
for (Core::uint32 visibleItemIndex : rendererList->visibleRenderItemIndices) {
const VisibleRenderItem* visibleItem =
sceneData.TryGetVisibleRenderItem(visibleItemIndex);
if (visibleItem == nullptr) {
continue;
}
visitor(*visibleItem);
}
return;
}
const RendererListDesc fallbackDesc = BuildDefaultRendererListDesc(rendererListType);
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
if (!MatchesRendererListDesc(visibleItem, fallbackDesc)) {
continue;
}
visitor(visibleItem);
}
}
template <typename Visitor>
inline void VisitRendererListVisibleItems(
const RenderSceneData& sceneData,
const RendererListDesc& rendererListDesc,
Visitor&& visitor) {
const RendererListDesc defaultDesc =
BuildDefaultRendererListDesc(
rendererListDesc.type);
if (rendererListDesc == defaultDesc) {
VisitRendererListVisibleItems(
sceneData,
rendererListDesc.type,
std::forward<Visitor>(visitor));
return;
}
std::vector<const VisibleRenderItem*> matchedItems;
matchedItems.reserve(sceneData.visibleItems.size());
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
if (!MatchesRendererListDesc(
visibleItem,
rendererListDesc)) {
continue;
}
matchedItems.push_back(&visibleItem);
}
switch (rendererListDesc.sorting.sortMode) {
case RendererSortMode::FrontToBack:
std::stable_sort(
matchedItems.begin(),
matchedItems.end(),
[](const VisibleRenderItem* lhs, const VisibleRenderItem* rhs) {
return lhs != nullptr &&
rhs != nullptr &&
lhs->cameraDistanceSq <
rhs->cameraDistanceSq;
});
break;
case RendererSortMode::BackToFront:
std::stable_sort(
matchedItems.begin(),
matchedItems.end(),
[](const VisibleRenderItem* lhs, const VisibleRenderItem* rhs) {
return lhs != nullptr &&
rhs != nullptr &&
lhs->cameraDistanceSq >
rhs->cameraDistanceSq;
});
break;
case RendererSortMode::None:
default:
break;
}
for (const VisibleRenderItem* visibleItem : matchedItems) {
if (visibleItem == nullptr) {
continue;
}
visitor(*visibleItem);
}
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,30 @@
#pragma once
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Components {
class GameObject;
class GaussianSplatRendererComponent;
} // namespace Components
namespace Resources {
class GaussianSplat;
class Material;
} // namespace Resources
namespace Rendering {
struct VisibleGaussianSplatItem {
Components::GameObject* gameObject = nullptr;
Components::GaussianSplatRendererComponent* gaussianSplatRenderer = nullptr;
Resources::GaussianSplat* gaussianSplat = nullptr;
const Resources::Material* material = nullptr;
Core::int32 renderQueue = 0;
float cameraDistanceSq = 0.0f;
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,35 @@
#pragma once
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
#include "engine/Runtime/Rendering/Picking/ObjectIdCodec.h"
namespace XCEngine {
namespace Components {
class GameObject;
class MeshFilterComponent;
class MeshRendererComponent;
} // namespace Components
namespace Rendering {
struct VisibleRenderItem {
Components::GameObject* gameObject = nullptr;
Components::MeshFilterComponent* meshFilter = nullptr;
Components::MeshRendererComponent* meshRenderer = nullptr;
Resources::Mesh* mesh = nullptr;
const Resources::Material* material = nullptr;
RenderObjectId renderObjectId = kInvalidRenderObjectId;
Core::uint32 materialIndex = 0;
Core::uint32 sectionIndex = 0;
bool hasSection = false;
Core::uint32 renderLayer = 0;
Core::int32 renderQueue = 0;
float cameraDistanceSq = 0.0f;
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,30 @@
#pragma once
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Components {
class GameObject;
class VolumeRendererComponent;
} // namespace Components
namespace Resources {
class Material;
class VolumeField;
} // namespace Resources
namespace Rendering {
struct VisibleVolumeItem {
Components::GameObject* gameObject = nullptr;
Components::VolumeRendererComponent* volumeRenderer = nullptr;
Resources::VolumeField* volumeField = nullptr;
const Resources::Material* material = nullptr;
Core::int32 renderQueue = 0;
float cameraDistanceSq = 0.0f;
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,509 @@
#pragma once
#include <XCEngine/Components/MeshRendererComponent.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/RHI/RHIBuffer.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/RHITypes.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
#include <cstdlib>
#include <cstddef>
namespace XCEngine {
namespace Rendering {
enum class BuiltinSkyboxTextureMode : Core::uint8 {
None = 0,
Panoramic = 1,
Cubemap = 2
};
struct BuiltinSkyboxMaterialData {
Math::Vector4 tint = Math::Vector4::One();
float exposure = 1.0f;
float rotationDegrees = 0.0f;
BuiltinSkyboxTextureMode textureMode = BuiltinSkyboxTextureMode::None;
};
struct BuiltinDepthStyleMaterialConstants {
Math::Vector4 baseColorFactor = Math::Vector4::One();
Math::Vector4 alphaCutoffParams = Math::Vector4(0.5f, 0.0f, 0.0f, 0.0f);
};
struct MaterialConstantLayoutView {
const Resources::MaterialConstantFieldDesc* fields = nullptr;
size_t count = 0;
size_t size = 0;
bool IsValid() const {
return fields != nullptr && count > 0 && size > 0;
}
};
struct MaterialConstantPayloadView {
const void* data = nullptr;
size_t size = 0;
MaterialConstantLayoutView layout = {};
bool IsValid() const {
return data != nullptr && size > 0 && layout.IsValid() && layout.size == size;
}
};
struct MaterialBufferResourceView {
RHI::RHIBuffer* buffer = nullptr;
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
RHI::ResourceViewDesc viewDesc = {};
bool IsValid() const {
return buffer != nullptr &&
(viewType == RHI::ResourceViewType::ShaderResource ||
viewType == RHI::ResourceViewType::UnorderedAccess) &&
viewDesc.dimension != RHI::ResourceViewDimension::Unknown;
}
};
inline bool IsMaterialBufferResourceType(Resources::ShaderResourceType type) {
switch (type) {
case Resources::ShaderResourceType::StructuredBuffer:
case Resources::ShaderResourceType::RawBuffer:
case Resources::ShaderResourceType::RWStructuredBuffer:
case Resources::ShaderResourceType::RWRawBuffer:
return true;
default:
return false;
}
}
inline const Resources::ShaderPropertyDesc* FindShaderPropertyBySemantic(
const Resources::Material* material,
const Containers::String& semantic) {
if (material == nullptr || material->GetShader() == nullptr) {
return nullptr;
}
const Containers::String normalizedSemantic = NormalizeBuiltinPassMetadataValue(semantic);
for (const Resources::ShaderPropertyDesc& property : material->GetShader()->GetProperties()) {
if (NormalizeBuiltinPassMetadataValue(property.semantic) == normalizedSemantic) {
return &property;
}
}
return nullptr;
}
inline Math::Vector4 ResolveBuiltinBaseColorFactor(const Resources::Material* material) {
if (material == nullptr) {
return Math::Vector4::One();
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColor")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Color ||
property->type == Resources::ShaderPropertyType::Vector)) {
return material->GetFloat4(property->name);
}
}
return Math::Vector4::One();
}
inline const Resources::Texture* ResolveBuiltinBaseColorTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColorTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
return textureHandle.Get();
}
}
return nullptr;
}
inline float ResolveBuiltinAlphaCutoff(const Resources::Material* material) {
if (material == nullptr) {
return 0.5f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "AlphaCutoff")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 0.5f;
}
inline bool IsCubemapSkyboxTextureType(Resources::TextureType type) {
return type == Resources::TextureType::TextureCube ||
type == Resources::TextureType::TextureCubeArray;
}
inline bool IsPanoramicSkyboxTextureType(Resources::TextureType type) {
return type == Resources::TextureType::Texture2D ||
type == Resources::TextureType::Texture2DArray;
}
inline const Resources::Texture* ResolveSkyboxPanoramicTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxPanoramicTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
return nullptr;
}
inline const Resources::Texture* ResolveSkyboxCubemapTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsCubemapSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
return nullptr;
}
inline BuiltinSkyboxTextureMode ResolveSkyboxTextureMode(const Resources::Material* material) {
if (ResolveSkyboxPanoramicTexture(material) != nullptr) {
return BuiltinSkyboxTextureMode::Panoramic;
}
if (ResolveSkyboxCubemapTexture(material) != nullptr) {
return BuiltinSkyboxTextureMode::Cubemap;
}
return BuiltinSkyboxTextureMode::None;
}
inline Math::Vector4 ResolveSkyboxTint(const Resources::Material* material) {
if (material == nullptr) {
return Math::Vector4::One();
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Tint")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Color ||
property->type == Resources::ShaderPropertyType::Vector)) {
return material->GetFloat4(property->name);
}
}
return Math::Vector4::One();
}
inline float ResolveSkyboxExposure(const Resources::Material* material) {
if (material == nullptr) {
return 1.0f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Exposure")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 1.0f;
}
inline float ResolveSkyboxRotationDegrees(const Resources::Material* material) {
if (material == nullptr) {
return 0.0f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Rotation")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 0.0f;
}
inline BuiltinSkyboxMaterialData BuildBuiltinSkyboxMaterialData(const Resources::Material* material) {
BuiltinSkyboxMaterialData data = {};
data.tint = ResolveSkyboxTint(material);
data.exposure = ResolveSkyboxExposure(material);
data.rotationDegrees = ResolveSkyboxRotationDegrees(material);
data.textureMode = ResolveSkyboxTextureMode(material);
return data;
}
inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Resources::Material* material) {
if (material == nullptr || material->GetShader() == nullptr) {
return {};
}
const Containers::Array<Resources::MaterialConstantFieldDesc>& constantLayout = material->GetConstantLayout();
const Containers::Array<Core::uint8>& constantBufferData = material->GetConstantBufferData();
if (constantLayout.Empty() || constantBufferData.Empty()) {
return {};
}
MaterialConstantLayoutView layoutView = {};
layoutView.fields = constantLayout.Data();
layoutView.count = constantLayout.Size();
layoutView.size = constantBufferData.Size();
return { constantBufferData.Data(), constantBufferData.Size(), layoutView };
}
inline BuiltinDepthStyleMaterialConstants BuildBuiltinDepthStyleMaterialConstants(
const Resources::Material* material) {
BuiltinDepthStyleMaterialConstants constants = {};
constants.baseColorFactor = ResolveBuiltinBaseColorFactor(material);
constants.alphaCutoffParams = Math::Vector4(ResolveBuiltinAlphaCutoff(material), 0.0f, 0.0f, 0.0f);
return constants;
}
inline MaterialConstantPayloadView ResolveBuiltinDepthStyleMaterialConstantPayload(
const Resources::Material* material,
BuiltinDepthStyleMaterialConstants& outConstants,
Resources::MaterialConstantFieldDesc (&outLayout)[2]) {
outConstants = BuildBuiltinDepthStyleMaterialConstants(material);
outLayout[0].name = "gBaseColorFactor";
outLayout[0].type = Resources::MaterialPropertyType::Float4;
outLayout[0].offset = 0u;
outLayout[0].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[0].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].name = "gAlphaCutoffParams";
outLayout[1].type = Resources::MaterialPropertyType::Float4;
outLayout[1].offset = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
MaterialConstantLayoutView layoutView = {};
layoutView.fields = outLayout;
layoutView.count = 2u;
layoutView.size = sizeof(BuiltinDepthStyleMaterialConstants);
return { &outConstants, sizeof(BuiltinDepthStyleMaterialConstants), layoutView };
}
inline bool TryResolveMaterialBufferResourceView(
const Resources::Material* material,
const BuiltinPassResourceBindingDesc& binding,
MaterialBufferResourceView& outView) {
outView = {};
if (material == nullptr || !IsMaterialBufferResourceType(binding.resourceType)) {
return false;
}
const Resources::MaterialBufferBinding* materialBinding = material->FindBufferBinding(binding.name);
if (materialBinding == nullptr || materialBinding->buffer == nullptr) {
return false;
}
outView.buffer = materialBinding->buffer;
outView.viewDesc.firstElement = materialBinding->viewDesc.firstElement;
outView.viewDesc.elementCount = materialBinding->viewDesc.elementCount;
switch (binding.resourceType) {
case Resources::ShaderResourceType::StructuredBuffer:
outView.viewType = RHI::ResourceViewType::ShaderResource;
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
outView.viewDesc.structureByteStride =
materialBinding->viewDesc.structureByteStride > 0
? materialBinding->viewDesc.structureByteStride
: materialBinding->buffer->GetStride();
break;
case Resources::ShaderResourceType::RawBuffer:
outView.viewType = RHI::ResourceViewType::ShaderResource;
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
break;
case Resources::ShaderResourceType::RWStructuredBuffer:
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
outView.viewDesc.structureByteStride =
materialBinding->viewDesc.structureByteStride > 0
? materialBinding->viewDesc.structureByteStride
: materialBinding->buffer->GetStride();
break;
case Resources::ShaderResourceType::RWRawBuffer:
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
break;
default:
return false;
}
if (outView.viewDesc.dimension == RHI::ResourceViewDimension::StructuredBuffer &&
outView.viewDesc.structureByteStride == 0) {
outView = {};
return false;
}
return outView.IsValid();
}
inline const Resources::Material* ResolveMaterial(
const Components::MeshRendererComponent* meshRenderer,
const Resources::Mesh* mesh,
Core::uint32 materialIndex) {
if (meshRenderer != nullptr && materialIndex < meshRenderer->GetMaterialCount()) {
if (const Resources::Material* material = meshRenderer->GetMaterial(materialIndex)) {
return material;
}
}
if (mesh != nullptr && materialIndex < mesh->GetMaterials().Size()) {
if (const Resources::Material* material = mesh->GetMaterials()[materialIndex]) {
return material;
}
}
if (meshRenderer != nullptr && meshRenderer->GetMaterialCount() > 0) {
if (const Resources::Material* material = meshRenderer->GetMaterial(0)) {
return material;
}
}
if (mesh != nullptr && mesh->GetMaterials().Size() > 0) {
return mesh->GetMaterials()[0];
}
return nullptr;
}
inline const Resources::Material* ResolveMaterial(const VisibleRenderItem& visibleItem) {
if (visibleItem.material != nullptr) {
return visibleItem.material;
}
return ResolveMaterial(visibleItem.meshRenderer, visibleItem.mesh, visibleItem.materialIndex);
}
inline bool TryResolveRenderQueueTagValue(
const Containers::String& queueValue,
Core::int32& outRenderQueue) {
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(queueValue);
if (normalized.Empty()) {
return false;
}
if (normalized == Containers::String("background")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Background);
return true;
}
if (normalized == Containers::String("geometry")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
return true;
}
if (normalized == Containers::String("alphatest")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::AlphaTest);
return true;
}
if (normalized == Containers::String("transparent")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
return true;
}
if (normalized == Containers::String("overlay")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Overlay);
return true;
}
char* end = nullptr;
const long parsedValue = std::strtol(normalized.CStr(), &end, 10);
if (end != nullptr && *end == '\0') {
outRenderQueue = static_cast<Core::int32>(parsedValue);
return true;
}
return false;
}
inline bool TryResolveShaderPassRenderQueue(const Resources::ShaderPass& shaderPass, Core::int32& outRenderQueue) {
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("queue") &&
TryResolveRenderQueueTagValue(tag.value, outRenderQueue)) {
return true;
}
}
return false;
}
inline Core::int32 ResolveMaterialRenderQueue(const Resources::Material* material) {
const Core::int32 defaultQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
if (material == nullptr) {
return defaultQueue;
}
const Core::int32 materialQueue = material->GetRenderQueue();
if (materialQueue != defaultQueue) {
return materialQueue;
}
if (const Resources::Shader* shader = material->GetShader()) {
for (const Resources::ShaderPass& pass : shader->GetPasses()) {
Core::int32 shaderQueue = defaultQueue;
if (TryResolveShaderPassRenderQueue(pass, shaderQueue)) {
return shaderQueue;
}
}
}
return materialQueue;
}
inline bool IsTransparentRenderQueue(Core::int32 renderQueue) {
return renderQueue >= static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
}
inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) {
if (material == nullptr) {
return false;
}
const Resources::Shader* shader = material->GetShader();
if (shader == nullptr) {
return false;
}
for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) {
if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) {
return true;
}
}
return false;
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,311 @@
#pragma once
#include <cstring>
#include <XCEngine/Rendering/Execution/RenderStateBlock.h>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHITypes.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Shader/Shader.h>
namespace XCEngine {
namespace Rendering {
inline RHI::CullMode ToRHICullMode(Resources::MaterialCullMode mode) {
switch (mode) {
case Resources::MaterialCullMode::Front:
return RHI::CullMode::Front;
case Resources::MaterialCullMode::Back:
return RHI::CullMode::Back;
case Resources::MaterialCullMode::None:
default:
return RHI::CullMode::None;
}
}
inline RHI::ComparisonFunc ToRHIComparisonFunc(Resources::MaterialComparisonFunc func) {
switch (func) {
case Resources::MaterialComparisonFunc::Never:
return RHI::ComparisonFunc::Never;
case Resources::MaterialComparisonFunc::Equal:
return RHI::ComparisonFunc::Equal;
case Resources::MaterialComparisonFunc::LessEqual:
return RHI::ComparisonFunc::LessEqual;
case Resources::MaterialComparisonFunc::Greater:
return RHI::ComparisonFunc::Greater;
case Resources::MaterialComparisonFunc::NotEqual:
return RHI::ComparisonFunc::NotEqual;
case Resources::MaterialComparisonFunc::GreaterEqual:
return RHI::ComparisonFunc::GreaterEqual;
case Resources::MaterialComparisonFunc::Always:
return RHI::ComparisonFunc::Always;
case Resources::MaterialComparisonFunc::Less:
default:
return RHI::ComparisonFunc::Less;
}
}
inline RHI::BlendFactor ToRHIBlendFactor(Resources::MaterialBlendFactor factor) {
switch (factor) {
case Resources::MaterialBlendFactor::Zero:
return RHI::BlendFactor::Zero;
case Resources::MaterialBlendFactor::SrcColor:
return RHI::BlendFactor::SrcColor;
case Resources::MaterialBlendFactor::InvSrcColor:
return RHI::BlendFactor::InvSrcColor;
case Resources::MaterialBlendFactor::SrcAlpha:
return RHI::BlendFactor::SrcAlpha;
case Resources::MaterialBlendFactor::InvSrcAlpha:
return RHI::BlendFactor::InvSrcAlpha;
case Resources::MaterialBlendFactor::DstAlpha:
return RHI::BlendFactor::DstAlpha;
case Resources::MaterialBlendFactor::InvDstAlpha:
return RHI::BlendFactor::InvDstAlpha;
case Resources::MaterialBlendFactor::DstColor:
return RHI::BlendFactor::DstColor;
case Resources::MaterialBlendFactor::InvDstColor:
return RHI::BlendFactor::InvDstColor;
case Resources::MaterialBlendFactor::SrcAlphaSat:
return RHI::BlendFactor::SrcAlphaSat;
case Resources::MaterialBlendFactor::BlendFactor:
return RHI::BlendFactor::BlendFactor;
case Resources::MaterialBlendFactor::InvBlendFactor:
return RHI::BlendFactor::InvBlendFactor;
case Resources::MaterialBlendFactor::Src1Color:
return RHI::BlendFactor::Src1Color;
case Resources::MaterialBlendFactor::InvSrc1Color:
return RHI::BlendFactor::InvSrc1Color;
case Resources::MaterialBlendFactor::Src1Alpha:
return RHI::BlendFactor::Src1Alpha;
case Resources::MaterialBlendFactor::InvSrc1Alpha:
return RHI::BlendFactor::InvSrc1Alpha;
case Resources::MaterialBlendFactor::One:
default:
return RHI::BlendFactor::One;
}
}
inline RHI::BlendOp ToRHIBlendOp(Resources::MaterialBlendOp op) {
switch (op) {
case Resources::MaterialBlendOp::Subtract:
return RHI::BlendOp::Subtract;
case Resources::MaterialBlendOp::ReverseSubtract:
return RHI::BlendOp::ReverseSubtract;
case Resources::MaterialBlendOp::Min:
return RHI::BlendOp::Min;
case Resources::MaterialBlendOp::Max:
return RHI::BlendOp::Max;
case Resources::MaterialBlendOp::Add:
default:
return RHI::BlendOp::Add;
}
}
inline RHI::StencilOp ToRHIStencilOp(Resources::MaterialStencilOp op) {
switch (op) {
case Resources::MaterialStencilOp::Zero:
return RHI::StencilOp::Zero;
case Resources::MaterialStencilOp::Replace:
return RHI::StencilOp::Replace;
case Resources::MaterialStencilOp::IncrSat:
return RHI::StencilOp::IncrSat;
case Resources::MaterialStencilOp::DecrSat:
return RHI::StencilOp::DecrSat;
case Resources::MaterialStencilOp::Invert:
return RHI::StencilOp::Invert;
case Resources::MaterialStencilOp::IncrWrap:
return RHI::StencilOp::Incr;
case Resources::MaterialStencilOp::DecrWrap:
return RHI::StencilOp::Decr;
case Resources::MaterialStencilOp::Keep:
default:
return RHI::StencilOp::Keep;
}
}
inline Resources::MaterialRenderState ResolveEffectiveRenderState(
const Resources::ShaderPass* shaderPass,
const Resources::Material* material) {
Resources::MaterialRenderState renderState = {};
if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) {
renderState = shaderPass->fixedFunctionState;
} else if (material != nullptr) {
renderState = material->GetRenderState();
}
if (material != nullptr && material->HasRenderStateOverride()) {
renderState = material->GetRenderState();
}
return renderState;
}
inline Resources::MaterialRenderState ApplyRenderStateBlock(
Resources::MaterialRenderState renderState,
const RenderStateBlock* renderStateBlock) {
if (renderStateBlock == nullptr ||
!renderStateBlock->HasOverrides()) {
return renderState;
}
if (renderStateBlock->HasDepthOverride()) {
renderState.depthWriteEnable =
renderStateBlock->depthState.writeEnabled;
renderState.depthFunc =
renderStateBlock->depthState.compareFunction;
}
if (renderStateBlock->HasStencilOverride()) {
renderState.stencil.enabled =
renderStateBlock->stencilState.enabled;
renderState.stencil.readMask =
renderStateBlock->stencilState.readMask;
renderState.stencil.writeMask =
renderStateBlock->stencilState.writeMask;
renderState.stencil.reference =
renderStateBlock->stencilReference;
renderState.stencil.front.failOp =
renderStateBlock->stencilState.frontFace.failOp;
renderState.stencil.front.passOp =
renderStateBlock->stencilState.frontFace.passOp;
renderState.stencil.front.depthFailOp =
renderStateBlock->stencilState.frontFace.depthFailOp;
renderState.stencil.front.func =
renderStateBlock->stencilState.frontFace.compareFunction;
renderState.stencil.back.failOp =
renderStateBlock->stencilState.backFace.failOp;
renderState.stencil.back.passOp =
renderStateBlock->stencilState.backFace.passOp;
renderState.stencil.back.depthFailOp =
renderStateBlock->stencilState.backFace.depthFailOp;
renderState.stencil.back.func =
renderStateBlock->stencilState.backFace.compareFunction;
}
return renderState;
}
inline RHI::RasterizerDesc BuildRasterizerState(const Resources::MaterialRenderState& renderState) {
RHI::RasterizerDesc desc = {};
desc.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
desc.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
desc.depthClipEnable = true;
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
desc.depthBias = renderState.depthBiasUnits;
desc.slopeScaledDepthBias = renderState.depthBiasFactor;
return desc;
}
inline RHI::BlendDesc BuildBlendState(const Resources::MaterialRenderState& renderState) {
RHI::BlendDesc desc = {};
desc.blendEnable = renderState.blendEnable;
desc.srcBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlend));
desc.dstBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlend));
desc.srcBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlendAlpha));
desc.dstBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlendAlpha));
desc.blendOp = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOp));
desc.blendOpAlpha = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOpAlpha));
desc.colorWriteMask = renderState.colorWriteMask;
return desc;
}
inline RHI::DepthStencilStateDesc BuildDepthStencilState(const Resources::MaterialRenderState& renderState) {
RHI::DepthStencilStateDesc desc = {};
desc.depthTestEnable = renderState.depthTestEnable;
desc.depthWriteEnable = renderState.depthWriteEnable;
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
desc.stencilEnable = renderState.stencil.enabled;
desc.stencilReadMask = renderState.stencil.readMask;
desc.stencilWriteMask = renderState.stencil.writeMask;
desc.front.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.failOp));
desc.front.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.passOp));
desc.front.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.depthFailOp));
desc.front.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.front.func));
desc.back.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.failOp));
desc.back.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.passOp));
desc.back.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.depthFailOp));
desc.back.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.back.func));
return desc;
}
inline void ApplyRenderState(const Resources::MaterialRenderState& renderState, RHI::GraphicsPipelineDesc& pipelineDesc) {
pipelineDesc.rasterizerState = BuildRasterizerState(renderState);
pipelineDesc.blendState = BuildBlendState(renderState);
pipelineDesc.depthStencilState = BuildDepthStencilState(renderState);
}
inline void ApplyMaterialRenderState(const Resources::Material* material, RHI::GraphicsPipelineDesc& pipelineDesc) {
ApplyRenderState(ResolveEffectiveRenderState(nullptr, material), pipelineDesc);
}
inline void ApplyResolvedRenderState(
const Resources::ShaderPass* shaderPass,
const Resources::Material* material,
RHI::GraphicsPipelineDesc& pipelineDesc) {
ApplyRenderState(ResolveEffectiveRenderState(shaderPass, material), pipelineDesc);
}
inline void ApplyDynamicRenderState(
const Resources::MaterialRenderState& renderState,
RHI::RHICommandList& commandList) {
if (renderState.stencil.enabled) {
commandList.SetStencilRef(renderState.stencil.reference);
}
}
inline Resources::MaterialRenderState BuildStaticPipelineRenderStateKey(
const Resources::MaterialRenderState& renderState) {
Resources::MaterialRenderState keyState = renderState;
keyState.stencil.reference = 0;
return keyState;
}
struct MaterialRenderStateHash {
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
size_t hash = 2166136261u;
auto combine = [&hash](size_t value) {
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
};
auto combineFloat = [&combine](float value) {
Core::uint32 bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
combine(static_cast<size_t>(bits));
};
combine(static_cast<size_t>(state.blendEnable));
combine(static_cast<size_t>(state.srcBlend));
combine(static_cast<size_t>(state.dstBlend));
combine(static_cast<size_t>(state.srcBlendAlpha));
combine(static_cast<size_t>(state.dstBlendAlpha));
combine(static_cast<size_t>(state.blendOp));
combine(static_cast<size_t>(state.blendOpAlpha));
combine(static_cast<size_t>(state.colorWriteMask));
combine(static_cast<size_t>(state.depthTestEnable));
combine(static_cast<size_t>(state.depthWriteEnable));
combine(static_cast<size_t>(state.depthFunc));
combine(static_cast<size_t>(state.cullMode));
combineFloat(state.depthBiasFactor);
combine(static_cast<size_t>(state.depthBiasUnits));
combine(static_cast<size_t>(state.stencil.enabled));
combine(static_cast<size_t>(state.stencil.readMask));
combine(static_cast<size_t>(state.stencil.writeMask));
combine(static_cast<size_t>(state.stencil.reference));
combine(static_cast<size_t>(state.stencil.front.failOp));
combine(static_cast<size_t>(state.stencil.front.passOp));
combine(static_cast<size_t>(state.stencil.front.depthFailOp));
combine(static_cast<size_t>(state.stencil.front.func));
combine(static_cast<size_t>(state.stencil.back.failOp));
combine(static_cast<size_t>(state.stencil.back.passOp));
combine(static_cast<size_t>(state.stencil.back.depthFailOp));
combine(static_cast<size_t>(state.stencil.back.func));
return hash;
}
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,56 @@
#pragma once
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Rendering {
using RenderObjectId = Core::uint32;
using EncodedObjectId = RenderObjectId;
static constexpr RenderObjectId kInvalidRenderObjectId = 0u;
inline bool IsValidRenderObjectId(RenderObjectId renderObjectId) {
return renderObjectId != kInvalidRenderObjectId;
}
inline EncodedObjectId EncodeRenderObjectIdToEncodedId(RenderObjectId renderObjectId) {
return renderObjectId;
}
inline EncodedObjectId EncodeRenderObjectIdToUInt32(RenderObjectId renderObjectId) {
return EncodeRenderObjectIdToEncodedId(renderObjectId);
}
inline Math::Vector4 EncodeRenderObjectIdToColor(RenderObjectId renderObjectId) {
const EncodedObjectId encodedId = EncodeRenderObjectIdToEncodedId(renderObjectId);
constexpr float kInv255 = 1.0f / 255.0f;
return Math::Vector4(
static_cast<float>((encodedId >> 0) & 0xFFu) * kInv255,
static_cast<float>((encodedId >> 8) & 0xFFu) * kInv255,
static_cast<float>((encodedId >> 16) & 0xFFu) * kInv255,
static_cast<float>((encodedId >> 24) & 0xFFu) * kInv255);
}
inline EncodedObjectId DecodeEncodedObjectIdFromColor(
Core::uint8 r,
Core::uint8 g,
Core::uint8 b,
Core::uint8 a) {
return static_cast<EncodedObjectId>(r) |
(static_cast<EncodedObjectId>(g) << 8u) |
(static_cast<EncodedObjectId>(b) << 16u) |
(static_cast<EncodedObjectId>(a) << 24u);
}
inline RenderObjectId DecodeRenderObjectIdFromColor(
Core::uint8 r,
Core::uint8 g,
Core::uint8 b,
Core::uint8 a) {
return DecodeEncodedObjectIdFromColor(r, g, b, a);
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Rendering/Picking/RenderObjectIdRegistry.h>
#include "engine/Runtime/Rendering/Picking/RenderObjectIdRegistry.h"
#include <limits>

View File

@@ -0,0 +1,48 @@
#pragma once
#include <XCEngine/Core/Types.h>
#include "engine/Runtime/Rendering/Picking/ObjectIdCodec.h"
#include <mutex>
#include <unordered_map>
namespace XCEngine {
namespace Rendering {
class RenderObjectIdRegistry {
public:
static RenderObjectIdRegistry& Get();
RenderObjectIdRegistry(const RenderObjectIdRegistry&) = delete;
RenderObjectIdRegistry& operator=(const RenderObjectIdRegistry&) = delete;
RenderObjectId GetOrAllocateRenderObjectId(Core::uint64 runtimeObjectId);
bool TryGetRenderObjectId(
Core::uint64 runtimeObjectId,
RenderObjectId& outRenderObjectId) const;
bool TryResolveRuntimeObjectId(
RenderObjectId renderObjectId,
Core::uint64& outRuntimeObjectId) const;
Core::uint64 ResolveRuntimeObjectId(RenderObjectId renderObjectId) const;
void Reset();
Core::uint64 GetGeneration() const;
private:
RenderObjectIdRegistry() = default;
RenderObjectId AllocateRenderObjectIdLocked();
void BumpGenerationLocked();
mutable std::mutex m_mutex = {};
RenderObjectId m_nextRenderObjectId = 1u;
Core::uint64 m_generation = 1u;
std::unordered_map<Core::uint64, RenderObjectId>
m_renderObjectIdByRuntimeObjectId = {};
std::unordered_map<RenderObjectId, Core::uint64>
m_runtimeObjectIdByRenderObjectId = {};
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include "Rendering/Shadow/DirectionalShadowData.h"
#include "DirectionalShadowData.h"
namespace XCEngine {
namespace Rendering {

View File

@@ -0,0 +1,82 @@
#pragma once
#include <XCEngine/Core/Math/Matrix4.h>
#include <XCEngine/Core/Math/Vector2.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Rendering/FrameData/RenderCameraData.h>
#include <XCEngine/Rendering/Shadow/DirectionalShadowSettings.h>
#include <cstdint>
namespace XCEngine {
namespace RHI {
class RHIResourceView;
} // namespace RHI
namespace Rendering {
struct DirectionalShadowRenderPlan {
bool enabled = false;
Math::Vector3 lightDirection = Math::Vector3::Back();
Math::Vector3 focusPoint = Math::Vector3::Zero();
float orthographicHalfExtent = 0.0f;
float texelWorldSize = 0.0f;
float nearClipPlane = 0.1f;
float farClipPlane = 0.0f;
DirectionalShadowSamplingSettings sampling = {};
DirectionalShadowCasterBiasSettings casterBias = {};
uint32_t mapWidth = 0;
uint32_t mapHeight = 0;
RenderCameraData cameraData = {};
bool IsValid() const {
return enabled &&
mapWidth > 0 &&
mapHeight > 0 &&
cameraData.viewportWidth == mapWidth &&
cameraData.viewportHeight == mapHeight;
}
};
struct RenderDirectionalShadowMapMetrics {
Math::Vector2 inverseMapSize = Math::Vector2::Zero();
float worldTexelSize = 0.0f;
float padding = 0.0f;
};
static_assert(
sizeof(RenderDirectionalShadowMapMetrics) == sizeof(float) * 4u,
"RenderDirectionalShadowMapMetrics must stay float4-sized for GPU constant layout");
struct RenderDirectionalShadowSamplingData {
float enabled = 0.0f;
DirectionalShadowSamplingSettings settings = {};
};
static_assert(
sizeof(RenderDirectionalShadowSamplingData) == sizeof(float) * 4u,
"RenderDirectionalShadowSamplingData must stay float4-sized for GPU constant layout");
struct RenderDirectionalShadowCasterBiasData {
DirectionalShadowCasterBiasSettings settings = {};
};
struct RenderDirectionalShadowData {
bool enabled = false;
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
RenderDirectionalShadowMapMetrics mapMetrics = {};
RenderDirectionalShadowSamplingData sampling = {};
RenderDirectionalShadowCasterBiasData casterBias = {};
RHI::RHIResourceView* shadowMap = nullptr;
bool IsValid() const {
return enabled && shadowMap != nullptr;
}
};
RenderDirectionalShadowData BuildRenderDirectionalShadowData(
const DirectionalShadowRenderPlan& plan,
RHI::RHIResourceView* shadowMapView);
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,6 +1,7 @@
#include "Rendering/Shadow/DirectionalShadowRuntime.h"
#include "DirectionalShadowRuntime.h"
#include "Rendering/Execution/CameraFramePlan.h"
#include <XCEngine/Rendering/RenderPipeline.h>
namespace XCEngine {

View File

@@ -0,0 +1,30 @@
#pragma once
#include <XCEngine/Rendering/Execution/DirectionalShadowExecutionState.h>
#include "engine/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.h"
namespace XCEngine {
namespace Rendering {
struct CameraFramePlan;
class RenderPipeline;
class DirectionalShadowRuntime {
public:
DirectionalShadowRuntime() = default;
DirectionalShadowRuntime(const DirectionalShadowRuntime&) = delete;
DirectionalShadowRuntime& operator=(const DirectionalShadowRuntime&) = delete;
~DirectionalShadowRuntime() = default;
bool ResolveExecutionState(
const CameraFramePlan& plan,
const RenderPipeline& pipeline,
DirectionalShadowExecutionState& outShadowState);
private:
DirectionalShadowSurfaceCache m_surfaceCache;
};
} // namespace Rendering
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/AudioClip/AudioClip.h>
#include "AudioClip.h"
namespace XCEngine {
namespace Resources {

View File

@@ -0,0 +1,105 @@
#pragma once
#include <XCEngine/Core/Asset/IResource.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Types.h>
#include <vector>
namespace XCEngine {
namespace Resources {
enum class AudioFormat : Core::uint8 {
Unknown,
WAV,
OGG,
MP3,
FLAC
};
enum class AudioType : Core::uint8 {
SoundEffect,
Music,
Voice,
Ambient
};
class AudioClip : public IResource {
public:
AudioClip();
virtual ~AudioClip() override;
ResourceType GetType() const override { return ResourceType::AudioClip; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void SetPCMData(const Containers::Array<Core::uint8>& data);
const Containers::Array<Core::uint8>& GetPCMData() const { return m_pcmData; }
size_t GetPCMDataSize() const { return m_pcmData.Size(); }
// Legacy compatibility: audio data now means decoded/interpretable PCM bytes.
void SetAudioData(const Containers::Array<Core::uint8>& data) { SetPCMData(data); }
const Containers::Array<Core::uint8>& GetAudioData() const { return GetPCMData(); }
void SetSampleRate(Core::uint32 rate);
Core::uint32 GetSampleRate() const { return m_sampleRate; }
void SetChannels(Core::uint32 channels);
Core::uint32 GetChannels() const { return m_channels; }
void SetBitsPerSample(Core::uint32 bits);
Core::uint32 GetBitsPerSample() const { return m_bitsPerSample; }
void SetDuration(float seconds) { m_duration = seconds; }
float GetDuration() const { return m_duration; }
const std::vector<float>& GetDecodedPCMData() const;
bool HasDecodedPCMData() const { return m_decodedPCMValid; }
Core::uint64 GetFrameCount() const;
Core::uint64 GetSampleCount() const;
void SetAudioFormat(AudioFormat format) { m_format = format; }
AudioFormat GetAudioFormat() const { return m_format; }
void SetAudioType(AudioType type) { m_audioType = type; }
AudioType GetAudioType() const { return m_audioType; }
void SetIs3D(bool is3D) { m_is3D = is3D; }
bool Is3D() const { return m_is3D; }
void SetLoop(bool loop) { m_loop = loop; }
bool IsLoop() const { return m_loop; }
class IRHIAudioBuffer* GetRHIResource() const { return m_rhiResource; }
void SetRHIResource(class IRHIAudioBuffer* resource);
private:
void RefreshDerivedData();
void RefreshMemorySize();
void InvalidateDecodedPCMData();
void BuildDecodedPCMData() const;
Containers::Array<Core::uint8> m_pcmData;
mutable std::vector<float> m_decodedPCMData;
mutable bool m_decodedPCMValid = false;
Core::uint32 m_sampleRate = 44100;
Core::uint32 m_channels = 2;
Core::uint32 m_bitsPerSample = 16;
float m_duration = 0.0f;
AudioFormat m_format = AudioFormat::WAV;
AudioType m_audioType = AudioType::SoundEffect;
bool m_is3D = false;
bool m_loop = false;
class IRHIAudioBuffer* m_rhiResource = nullptr;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,6 @@
#include <XCEngine/Resources/AudioClip/AudioLoader.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include "AudioLoader.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
#include <cstring>
namespace XCEngine {

View File

@@ -0,0 +1,26 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "AudioClip.h"
namespace XCEngine {
namespace Resources {
class AudioLoader : public IResourceLoader {
public:
AudioLoader();
virtual ~AudioLoader() override;
ResourceType GetResourceType() const override { return ResourceType::AudioClip; }
Containers::Array<Containers::String> GetSupportedExtensions() const override;
bool CanLoad(const Containers::String& path) const override;
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
ImportSettings* GetDefaultSettings() const override;
private:
bool ParseWAVData(const Containers::Array<Core::uint8>& data, AudioClip* audioClip);
AudioFormat DetectAudioFormat(const Containers::String& path, const Containers::Array<Core::uint8>& data);
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,5 @@
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "Material.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include <algorithm>
#include <cctype>

View File

@@ -0,0 +1,221 @@
#pragma once
#include <XCEngine/Core/Asset/IResource.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Resources/Texture/Texture.h>
#include "MaterialRenderState.h"
#include "engine/Runtime/Resources/Shader/Shader.h"
#include <XCEngine/Core/Containers/HashMap.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Core/Math/Vector2.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <cstring>
#include <memory>
#include <vector>
namespace XCEngine {
namespace RHI {
class RHIBuffer;
}
namespace Resources {
enum class MaterialRenderQueue : Core::int32 {
Background = 1000,
Geometry = 2000,
AlphaTest = 2450,
Transparent = 3000,
Overlay = 4000
};
enum class MaterialPropertyType {
Float, Float2, Float3, Float4,
Int, Int2, Int3, Int4,
Bool, Texture, Cubemap
};
struct MaterialProperty {
Containers::String name;
MaterialPropertyType type;
union Value {
float floatValue[4];
Core::int32 intValue[4];
bool boolValue;
Value() { memset(this, 0, sizeof(Value)); }
} value;
Core::uint32 refCount = 0;
MaterialProperty() : type(MaterialPropertyType::Float), refCount(0) {}
};
struct MaterialConstantFieldDesc {
Containers::String name;
MaterialPropertyType type = MaterialPropertyType::Float;
Core::uint32 offset = 0;
Core::uint32 size = 0;
Core::uint32 alignedSize = 0;
};
struct MaterialTagEntry {
Containers::String name;
Containers::String value;
};
struct PendingTextureLoadState {
IResource* resource = nullptr;
Containers::String errorMessage;
bool completed = false;
};
struct MaterialTextureBinding {
Containers::String name;
Core::uint32 slot = 0;
ResourceHandle<Texture> texture;
AssetRef textureRef;
Containers::String texturePath;
std::shared_ptr<PendingTextureLoadState> pendingLoad;
};
struct MaterialBufferBindingViewDesc {
Core::uint32 firstElement = 0;
Core::uint32 elementCount = 0;
Core::uint32 structureByteStride = 0;
};
struct MaterialBufferBinding {
Containers::String name;
RHI::RHIBuffer* buffer = nullptr;
MaterialBufferBindingViewDesc viewDesc = {};
};
class Material : public IResource {
public:
Material();
virtual ~Material() override;
ResourceType GetType() const override { return ResourceType::Material; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void SetShader(const ResourceHandle<class Shader>& shader);
class Shader* GetShader() const { return m_shader.Get(); }
void SetRenderQueue(Core::int32 renderQueue);
void SetRenderQueue(MaterialRenderQueue renderQueue);
Core::int32 GetRenderQueue() const { return m_renderQueue; }
void SetRenderState(const MaterialRenderState& renderState);
const MaterialRenderState& GetRenderState() const { return m_renderState; }
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
void SetRenderStateOverrideEnabled(bool enabled);
void SetTag(const Containers::String& name, const Containers::String& value);
Containers::String GetTag(const Containers::String& name) const;
bool HasTag(const Containers::String& name) const;
void RemoveTag(const Containers::String& name);
void ClearTags();
Core::uint32 GetTagCount() const { return static_cast<Core::uint32>(m_tags.Size()); }
Containers::String GetTagName(Core::uint32 index) const;
Containers::String GetTagValue(Core::uint32 index) const;
const Containers::Array<MaterialTagEntry>& GetTags() const { return m_tags; }
void EnableKeyword(const Containers::String& keyword);
void DisableKeyword(const Containers::String& keyword);
void SetKeywordEnabled(const Containers::String& keyword, bool enabled);
bool IsKeywordEnabled(const Containers::String& keyword) const;
void ClearKeywords();
Core::uint32 GetKeywordCount() const { return static_cast<Core::uint32>(m_keywordSet.enabledKeywords.Size()); }
Containers::String GetKeyword(Core::uint32 index) const;
const ShaderKeywordSet& GetKeywordSet() const { return m_keywordSet; }
void SetFloat(const Containers::String& name, float value);
void SetFloat2(const Containers::String& name, const Math::Vector2& value);
void SetFloat3(const Containers::String& name, const Math::Vector3& value);
void SetFloat4(const Containers::String& name, const Math::Vector4& value);
void SetInt(const Containers::String& name, Core::int32 value);
void SetBool(const Containers::String& name, bool value);
void SetTexture(const Containers::String& name, const ResourceHandle<Texture>& texture);
void SetBuffer(const Containers::String& name, RHI::RHIBuffer* buffer);
void SetBuffer(
const Containers::String& name,
RHI::RHIBuffer* buffer,
const MaterialBufferBindingViewDesc& viewDesc);
void SetTextureAssetRef(const Containers::String& name,
const AssetRef& textureRef,
const Containers::String& texturePath = Containers::String());
void SetTexturePath(const Containers::String& name, const Containers::String& texturePath);
float GetFloat(const Containers::String& name) const;
Math::Vector2 GetFloat2(const Containers::String& name) const;
Math::Vector3 GetFloat3(const Containers::String& name) const;
Math::Vector4 GetFloat4(const Containers::String& name) const;
Core::int32 GetInt(const Containers::String& name) const;
bool GetBool(const Containers::String& name) const;
ResourceHandle<Texture> GetTexture(const Containers::String& name) const;
RHI::RHIBuffer* GetBuffer(const Containers::String& name) const;
const MaterialBufferBinding* FindBufferBinding(const Containers::String& name) const;
Core::uint32 GetTextureBindingCount() const { return static_cast<Core::uint32>(m_textureBindings.Size()); }
Core::uint32 GetBufferBindingCount() const { return static_cast<Core::uint32>(m_bufferBindings.Size()); }
Containers::String GetTextureBindingName(Core::uint32 index) const;
AssetRef GetTextureBindingAssetRef(Core::uint32 index) const;
Containers::String GetTextureBindingPath(Core::uint32 index) const;
ResourceHandle<Texture> GetTextureBindingLoadedTexture(Core::uint32 index) const;
ResourceHandle<Texture> GetTextureBindingTexture(Core::uint32 index) const;
const Containers::Array<MaterialTextureBinding>& GetTextureBindings() const { return m_textureBindings; }
const Containers::Array<MaterialBufferBinding>& GetBufferBindings() const { return m_bufferBindings; }
std::vector<MaterialProperty> GetProperties() const;
const Containers::Array<Core::uint8>& GetConstantBufferData() const { return m_constantBufferData; }
const Containers::Array<MaterialConstantFieldDesc>& GetConstantLayout() const { return m_constantLayout; }
const MaterialConstantFieldDesc* FindConstantField(const Containers::String& name) const;
void UpdateConstantBuffer();
Core::uint64 GetChangeVersion() const { return m_changeVersion; }
void RecalculateMemorySize();
bool HasProperty(const Containers::String& name) const;
void RemoveProperty(const Containers::String& name);
void ClearAllProperties();
void RemoveBufferBinding(const Containers::String& name);
void ClearBufferBindings();
private:
const ShaderPropertyDesc* FindShaderPropertyDesc(const Containers::String& name) const;
bool CanAssignPropertyType(const Containers::String& name, MaterialPropertyType type) const;
bool ResetPropertyToShaderDefault(const Containers::String& name);
void SyncShaderSchemaProperties(bool removeUnknownProperties);
void SyncShaderRuntimeBufferBindings(bool removeUnknownBindings);
void BeginAsyncTextureLoad(Core::uint32 index);
void ResolvePendingTextureBinding(Core::uint32 index);
void ResolvePendingTextureBindings();
void MarkChanged(bool updateConstantBuffer);
void UpdateMemorySize();
void SyncShaderSchemaKeywords(bool removeUnknownKeywords);
ResourceHandle<class Shader> m_shader;
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
MaterialRenderState m_renderState;
bool m_hasRenderStateOverride = false;
Containers::Array<MaterialTagEntry> m_tags;
ShaderKeywordSet m_keywordSet;
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
Containers::Array<MaterialConstantFieldDesc> m_constantLayout;
Containers::Array<Core::uint8> m_constantBufferData;
Containers::Array<MaterialTextureBinding> m_textureBindings;
Containers::Array<MaterialBufferBinding> m_bufferBindings;
Core::uint64 m_changeVersion = 1;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,10 +1,10 @@
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include "MaterialLoader.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
#include "engine/Runtime/Resources/Shader/ShaderLoader.h"
#include <cctype>
#include <cstring>

View File

@@ -0,0 +1,26 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "Material.h"
namespace XCEngine {
namespace Resources {
class MaterialLoader : public IResourceLoader {
public:
MaterialLoader();
virtual ~MaterialLoader() override;
ResourceType GetResourceType() const override { return ResourceType::Material; }
Containers::Array<Containers::String> GetSupportedExtensions() const override;
bool CanLoad(const Containers::String& path) const override;
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
ImportSettings* GetDefaultSettings() const override;
private:
bool ParseMaterialData(const Containers::Array<Core::uint8>& data, Material* material);
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,147 @@
#pragma once
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Resources {
enum class MaterialCullMode : Core::uint8 {
None = 0,
Front = 1,
Back = 2
};
enum class MaterialComparisonFunc : Core::uint8 {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7
};
enum class MaterialBlendOp : Core::uint8 {
Add = 0,
Subtract = 1,
ReverseSubtract = 2,
Min = 3,
Max = 4
};
enum class MaterialBlendFactor : Core::uint8 {
Zero = 0,
One = 1,
SrcColor = 2,
InvSrcColor = 3,
SrcAlpha = 4,
InvSrcAlpha = 5,
DstAlpha = 6,
InvDstAlpha = 7,
DstColor = 8,
InvDstColor = 9,
SrcAlphaSat = 10,
BlendFactor = 11,
InvBlendFactor = 12,
Src1Color = 13,
InvSrc1Color = 14,
Src1Alpha = 15,
InvSrc1Alpha = 16
};
enum class MaterialStencilOp : Core::uint8 {
Keep = 0,
Zero = 1,
Replace = 2,
IncrSat = 3,
DecrSat = 4,
Invert = 5,
IncrWrap = 6,
DecrWrap = 7
};
struct MaterialStencilFaceState {
MaterialStencilOp failOp = MaterialStencilOp::Keep;
MaterialStencilOp passOp = MaterialStencilOp::Keep;
MaterialStencilOp depthFailOp = MaterialStencilOp::Keep;
MaterialComparisonFunc func = MaterialComparisonFunc::Always;
bool operator==(const MaterialStencilFaceState& other) const {
return failOp == other.failOp &&
passOp == other.passOp &&
depthFailOp == other.depthFailOp &&
func == other.func;
}
bool operator!=(const MaterialStencilFaceState& other) const {
return !(*this == other);
}
};
struct MaterialStencilState {
bool enabled = false;
Core::uint8 readMask = 0xFF;
Core::uint8 writeMask = 0xFF;
Core::uint8 reference = 0;
MaterialStencilFaceState front = {};
MaterialStencilFaceState back = {};
bool operator==(const MaterialStencilState& other) const {
return enabled == other.enabled &&
readMask == other.readMask &&
writeMask == other.writeMask &&
reference == other.reference &&
front == other.front &&
back == other.back;
}
bool operator!=(const MaterialStencilState& other) const {
return !(*this == other);
}
};
struct MaterialRenderState {
bool blendEnable = false;
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
MaterialBlendFactor dstBlend = MaterialBlendFactor::Zero;
MaterialBlendFactor srcBlendAlpha = MaterialBlendFactor::One;
MaterialBlendFactor dstBlendAlpha = MaterialBlendFactor::Zero;
MaterialBlendOp blendOp = MaterialBlendOp::Add;
MaterialBlendOp blendOpAlpha = MaterialBlendOp::Add;
Core::uint8 colorWriteMask = 0xF;
bool depthTestEnable = true;
bool depthWriteEnable = true;
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
MaterialCullMode cullMode = MaterialCullMode::None;
float depthBiasFactor = 0.0f;
Core::int32 depthBiasUnits = 0;
MaterialStencilState stencil = {};
bool operator==(const MaterialRenderState& other) const {
return blendEnable == other.blendEnable &&
srcBlend == other.srcBlend &&
dstBlend == other.dstBlend &&
srcBlendAlpha == other.srcBlendAlpha &&
dstBlendAlpha == other.dstBlendAlpha &&
blendOp == other.blendOp &&
blendOpAlpha == other.blendOpAlpha &&
colorWriteMask == other.colorWriteMask &&
depthTestEnable == other.depthTestEnable &&
depthWriteEnable == other.depthWriteEnable &&
depthFunc == other.depthFunc &&
cullMode == other.cullMode &&
depthBiasFactor == other.depthBiasFactor &&
depthBiasUnits == other.depthBiasUnits &&
stencil == other.stencil;
}
bool operator!=(const MaterialRenderState& other) const {
return !(*this == other);
}
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,6 @@
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Texture/Texture.h>
#include "Mesh.h"
#include "engine/Runtime/Resources/Material/Material.h"
#include "engine/Runtime/Resources/Texture/Texture.h"
#include <cstring>
#include <unordered_set>

View File

@@ -0,0 +1,112 @@
#pragma once
#include <XCEngine/Core/Asset/IResource.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Math/Bounds.h>
#include <XCEngine/Core/Math/Vector2.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Resources {
class Material;
class Texture;
enum class VertexAttribute : Core::uint32 {
Position = 1 << 0, Normal = 1 << 1, Tangent = 1 << 2,
Color = 1 << 3, UV0 = 1 << 4, UV1 = 1 << 5,
UV2 = 1 << 6, UV3 = 1 << 7, BoneWeights = 1 << 8, BoneIndices = 1 << 9,
Bitangent = 1 << 10
};
inline VertexAttribute operator|(VertexAttribute a, VertexAttribute b) {
return static_cast<VertexAttribute>(static_cast<Core::uint32>(a) | static_cast<Core::uint32>(b));
}
inline VertexAttribute operator&(VertexAttribute a, VertexAttribute b) {
return static_cast<VertexAttribute>(static_cast<Core::uint32>(a) & static_cast<Core::uint32>(b));
}
inline bool HasVertexAttribute(VertexAttribute attributes, VertexAttribute flag) {
return (static_cast<Core::uint32>(attributes) & static_cast<Core::uint32>(flag)) != 0;
}
struct StaticMeshVertex {
Math::Vector3 position = Math::Vector3::Zero();
Math::Vector3 normal = Math::Vector3::Zero();
Math::Vector3 tangent = Math::Vector3::Zero();
Math::Vector3 bitangent = Math::Vector3::Zero();
Math::Vector2 uv0 = Math::Vector2::Zero();
Math::Vector2 uv1 = Math::Vector2::Zero();
Math::Vector4 color = Math::Vector4::One();
};
struct MeshSection {
Core::uint32 baseVertex;
Core::uint32 vertexCount;
Core::uint32 startIndex;
Core::uint32 indexCount;
Core::uint32 materialID;
Math::Bounds bounds;
};
class Mesh : public IResource {
public:
Mesh();
virtual ~Mesh() override;
ResourceType GetType() const override { return ResourceType::Mesh; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void SetVertexData(const void* data, size_t size, Core::uint32 vertexCount,
Core::uint32 vertexStride, VertexAttribute attributes);
const void* GetVertexData() const { return m_vertexData.Data(); }
size_t GetVertexDataSize() const { return m_vertexData.Size(); }
Core::uint32 GetVertexCount() const { return m_vertexCount; }
Core::uint32 GetVertexStride() const { return m_vertexStride; }
VertexAttribute GetVertexAttributes() const { return m_attributes; }
void SetIndexData(const void* data, size_t size, Core::uint32 indexCount, bool use32Bit);
const void* GetIndexData() const { return m_indexData.Data(); }
size_t GetIndexDataSize() const { return m_indexData.Size(); }
Core::uint32 GetIndexCount() const { return m_indexCount; }
bool IsUse32BitIndex() const { return m_use32BitIndex; }
void SetBounds(const Math::Bounds& bounds) { m_bounds = bounds; }
const Math::Bounds& GetBounds() const { return m_bounds; }
void AddSection(const MeshSection& section);
void AddMaterial(Material* material);
void AddTexture(Texture* texture);
const Containers::Array<MeshSection>& GetSections() const { return m_sections; }
const Containers::Array<Material*>& GetMaterials() const { return m_materials; }
const Containers::Array<Texture*>& GetTextures() const { return m_textures; }
Material* GetMaterial(Core::uint32 index) const;
private:
void UpdateMemorySize();
Core::uint32 m_vertexCount = 0;
Core::uint32 m_vertexStride = 0;
VertexAttribute m_attributes = VertexAttribute::Position;
Core::uint32 m_indexCount = 0;
bool m_use32BitIndex = false;
Containers::Array<Core::uint8> m_vertexData;
Containers::Array<Core::uint8> m_indexData;
Containers::Array<MeshSection> m_sections;
Containers::Array<Material*> m_materials;
Containers::Array<Texture*> m_textures;
Math::Bounds m_bounds;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
#include "MeshImportSettings.h"
#include <cstdlib>
#include <cstdio>
#include <sstream>

View File

@@ -0,0 +1,85 @@
#pragma once
#include <XCEngine/Core/Asset/ImportSettings.h>
#include "Mesh.h"
#include <XCEngine/Core/Math/Vector3.h>
namespace XCEngine {
namespace Resources {
enum class MeshImportFlags : Core::uint32 {
None = 0,
FlipUVs = 1 << 0,
FlipWindingOrder = 1 << 1,
GenerateNormals = 1 << 2,
GenerateTangents = 1 << 3,
OptimizeMesh = 1 << 4,
ImportSkinning = 1 << 5,
ImportAnimations = 1 << 6,
ImportMaterials = 1 << 7,
ImportCameras = 1 << 8,
ImportLights = 1 << 9
};
inline MeshImportFlags operator|(MeshImportFlags a, MeshImportFlags b) {
return static_cast<MeshImportFlags>(static_cast<Core::uint32>(a) | static_cast<Core::uint32>(b));
}
inline MeshImportFlags operator&(MeshImportFlags a, MeshImportFlags b) {
return static_cast<MeshImportFlags>(static_cast<Core::uint32>(a) & static_cast<Core::uint32>(b));
}
inline MeshImportFlags operator~(MeshImportFlags a) {
return static_cast<MeshImportFlags>(~static_cast<Core::uint32>(a));
}
class MeshImportSettings : public ImportSettings {
public:
MeshImportSettings();
virtual ~MeshImportSettings() override;
Core::UniqueRef<ImportSettings> Clone() const override;
bool LoadFromJSON(const Containers::String& json) override;
Containers::String SaveToJSON() const override;
void SetImportFlags(MeshImportFlags flags) { m_importFlags = flags; }
MeshImportFlags GetImportFlags() const { return m_importFlags; }
void AddImportFlag(MeshImportFlags flag) { m_importFlags = static_cast<MeshImportFlags>(static_cast<Core::uint32>(m_importFlags) | static_cast<Core::uint32>(flag)); }
void RemoveImportFlag(MeshImportFlags flag) { m_importFlags = static_cast<MeshImportFlags>(static_cast<Core::uint32>(m_importFlags) & ~static_cast<Core::uint32>(flag)); }
bool HasImportFlag(MeshImportFlags flag) const { return (static_cast<Core::uint32>(m_importFlags) & static_cast<Core::uint32>(flag)) != 0; }
void SetScale(float scale) { m_scale = scale; }
float GetScale() const { return m_scale; }
void SetOffset(const Math::Vector3& offset) { m_offset = offset; }
const Math::Vector3& GetOffset() const { return m_offset; }
void SetAxisConversion(bool convert) { m_axisConversion = convert; }
bool GetAxisConversion() const { return m_axisConversion; }
void SetMergeMeshes(bool merge) { m_mergeMeshes = merge; }
bool GetMergeMeshes() const { return m_mergeMeshes; }
void SetOptimizeThreshold(float threshold) { m_optimizeThreshold = threshold; }
float GetOptimizeThreshold() const { return m_optimizeThreshold; }
void SetImportScale(float scale) { m_importScale = scale; }
float GetImportScale() const { return m_importScale; }
void SetThreshold(float threshold) { m_threshold = threshold; }
float GetThreshold() const { return m_threshold; }
private:
MeshImportFlags m_importFlags = MeshImportFlags::ImportMaterials;
float m_scale = 1.0f;
Math::Vector3 m_offset = Math::Vector3::Zero();
bool m_axisConversion = true;
bool m_mergeMeshes = false;
float m_optimizeThreshold = 0.3f;
float m_importScale = 1.0f;
float m_threshold = 0.0f;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,15 +1,15 @@
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include "MeshLoader.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Texture/Texture.h>
#include "engine/Runtime/Resources/Material/MaterialLoader.h"
#include "MeshImportSettings.h"
#include "engine/Runtime/Resources/Material/Material.h"
#include "engine/Runtime/Resources/Texture/Texture.h"
#include <XCEngine/Resources/Texture/TextureImportHeuristics.h>
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "engine/Runtime/Resources/Texture/TextureLoader.h"
#include "engine/Runtime/Resources/Texture/TextureImportSettings.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include <XCEngine/Core/Math/Matrix4.h>
#include <assimp/Importer.hpp>
#include <assimp/material.h>

View File

@@ -0,0 +1,22 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "Mesh.h"
namespace XCEngine {
namespace Resources {
class MeshLoader : public IResourceLoader {
public:
MeshLoader();
virtual ~MeshLoader() override;
ResourceType GetResourceType() const override { return ResourceType::Mesh; }
Containers::Array<Containers::String> GetSupportedExtensions() const override;
bool CanLoad(const Containers::String& path) const override;
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
ImportSettings* GetDefaultSettings() const override;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,7 +1,7 @@
#include "ShaderFileUtils.h"
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include <filesystem>
#include <fstream>

View File

@@ -3,7 +3,7 @@
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include "../Shader.h"
#include <string>

View File

@@ -5,10 +5,10 @@
#include "ShaderAuthoringInternal.h"
#include "ShaderFileUtils.h"
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include "../ShaderLoader.h"
#include <algorithm>
#include <cctype>

View File

@@ -3,7 +3,8 @@
#include "../ShaderIR.h"
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include "../Shader.h"
#include <string>

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/Shader/Shader.h>
#include "Shader.h"
#include <utility>

View File

@@ -0,0 +1,201 @@
#pragma once
#include <XCEngine/Core/Asset/IResource.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Resources/Shader/ShaderKeywordTypes.h>
#include "engine/Runtime/Resources/Material/MaterialRenderState.h"
namespace XCEngine {
namespace Resources {
enum class ShaderType : Core::uint8 {
Vertex,
Fragment,
Geometry,
Compute,
Hull,
Domain
};
enum class ShaderLanguage : Core::uint8 {
GLSL,
HLSL,
SPIRV
};
enum class ShaderBackend : Core::uint8 {
Generic = 0,
D3D12,
OpenGL,
Vulkan
};
enum class ShaderPropertyType : Core::uint8 {
Float = 0,
Range,
Int,
Vector,
Color,
Texture2D,
TextureCube
};
enum class ShaderResourceType : Core::uint8 {
ConstantBuffer = 0,
Texture2D,
TextureCube,
Sampler,
StructuredBuffer,
RawBuffer,
RWStructuredBuffer,
RWRawBuffer
};
struct ShaderUniform {
Containers::String name;
Core::uint32 location;
Core::uint32 size;
Core::uint32 type;
};
struct ShaderAttribute {
Containers::String name;
Core::uint32 location;
Core::uint32 size;
Core::uint32 type;
};
struct ShaderPassTagEntry {
Containers::String name;
Containers::String value;
};
struct ShaderPropertyDesc {
Containers::String name;
Containers::String displayName;
ShaderPropertyType type = ShaderPropertyType::Float;
Containers::String defaultValue;
Containers::String semantic;
};
struct ShaderResourceBindingDesc {
Containers::String name;
ShaderResourceType type = ShaderResourceType::ConstantBuffer;
Core::uint32 set = 0;
Core::uint32 binding = 0;
Containers::String semantic;
};
struct ShaderBackendCompiledBinary {
ShaderBackend backend = ShaderBackend::Generic;
Containers::Array<Core::uint8> payload;
};
struct ShaderStageVariant {
ShaderType stage = ShaderType::Fragment;
ShaderLanguage language = ShaderLanguage::GLSL;
ShaderBackend backend = ShaderBackend::Generic;
ShaderKeywordSet requiredKeywords;
Containers::String entryPoint;
Containers::String profile;
Containers::String sourceCode;
Containers::Array<Core::uint8> compiledBinary;
Containers::Array<ShaderBackendCompiledBinary> backendCompiledBinaries;
const Containers::Array<Core::uint8>* GetCompiledBinaryForBackend(
ShaderBackend targetBackend) const;
void SetCompiledBinaryForBackend(
ShaderBackend targetBackend,
const Containers::Array<Core::uint8>& binary);
void SetCompiledBinaryForBackend(
ShaderBackend targetBackend,
Containers::Array<Core::uint8>&& binary);
};
struct ShaderPass {
Containers::String name;
bool hasFixedFunctionState = false;
MaterialRenderState fixedFunctionState;
Containers::Array<ShaderPassTagEntry> tags;
Containers::Array<ShaderResourceBindingDesc> resources;
Containers::Array<ShaderKeywordDeclaration> keywordDeclarations;
Containers::Array<ShaderStageVariant> variants;
};
class Shader : public IResource {
public:
Shader();
virtual ~Shader() override;
ResourceType GetType() const override { return ResourceType::Shader; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
void AddUniform(const ShaderUniform& uniform);
const Containers::Array<ShaderUniform>& GetUniforms() const { return m_uniforms; }
void AddAttribute(const ShaderAttribute& attribute);
const Containers::Array<ShaderAttribute>& GetAttributes() const { return m_attributes; }
void AddProperty(const ShaderPropertyDesc& property);
void ClearProperties();
const Containers::Array<ShaderPropertyDesc>& GetProperties() const { return m_properties; }
const ShaderPropertyDesc* FindProperty(const Containers::String& propertyName) const;
void SetFallback(const Containers::String& fallback);
const Containers::String& GetFallback() const { return m_fallback; }
void AddPass(const ShaderPass& pass);
void ClearPasses();
Core::uint32 GetPassCount() const { return static_cast<Core::uint32>(m_passes.Size()); }
const Containers::Array<ShaderPass>& GetPasses() const { return m_passes; }
void AddPassVariant(const Containers::String& passName, const ShaderStageVariant& variant);
void SetPassTag(
const Containers::String& passName,
const Containers::String& tagName,
const Containers::String& tagValue);
void AddPassResourceBinding(
const Containers::String& passName,
const ShaderResourceBindingDesc& binding);
void AddPassKeywordDeclaration(
const Containers::String& passName,
const ShaderKeywordDeclaration& declaration);
bool HasPass(const Containers::String& passName) const;
const ShaderPass* FindPass(const Containers::String& passName) const;
ShaderPass* FindPass(const Containers::String& passName);
bool PassDeclaresKeyword(
const Containers::String& passName,
const Containers::String& keyword) const;
bool DeclaresKeyword(const Containers::String& keyword) const;
const ShaderResourceBindingDesc* FindPassResourceBinding(
const Containers::String& passName,
const Containers::String& resourceName) const;
const ShaderStageVariant* FindVariant(
const Containers::String& passName,
ShaderType stage,
ShaderBackend backend = ShaderBackend::Generic,
const ShaderKeywordSet& enabledKeywords = ShaderKeywordSet()) const;
class IRHIShader* GetRHIResource() const { return m_rhiResource; }
void SetRHIResource(class IRHIShader* resource);
private:
ShaderPass& GetOrCreatePass(const Containers::String& passName);
Containers::Array<ShaderUniform> m_uniforms;
Containers::Array<ShaderAttribute> m_attributes;
Containers::Array<ShaderPropertyDesc> m_properties;
Containers::Array<ShaderPass> m_passes;
Containers::String m_fallback;
class IRHIShader* m_rhiResource = nullptr;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/Shader/ShaderCompilationCache.h>
#include "ShaderCompilationCache.h"
#include <algorithm>
#include <cstring>
@@ -246,8 +246,9 @@ void ShaderCompilationCache::SaveDatabase() const {
}
}
bool ShaderCompilationCache::Store(const ShaderCacheEntry& entry,
Containers::String* outErrorMessage) {
bool ShaderCompilationCache::Store(
const ShaderCacheEntry& entry,
Containers::String* outErrorMessage) {
if (!IsInitialized()) {
if (outErrorMessage != nullptr) {
*outErrorMessage = MakeError("ShaderCompilationCache is not initialized.");
@@ -319,9 +320,10 @@ bool ShaderCompilationCache::Store(const ShaderCacheEntry& entry,
return true;
}
bool ShaderCompilationCache::TryLoad(const ShaderCompileKey& key,
ShaderCacheEntry& outEntry,
Containers::String* outErrorMessage) const {
bool ShaderCompilationCache::TryLoad(
const ShaderCompileKey& key,
ShaderCacheEntry& outEntry,
Containers::String* outErrorMessage) const {
outEntry = {};
if (!IsInitialized()) {
if (outErrorMessage != nullptr) {

View File

@@ -0,0 +1,92 @@
#pragma once
#include <XCEngine/Core/Asset/AssetGUID.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Types.h>
#include "Shader.h"
#include <unordered_map>
namespace XCEngine {
namespace Resources {
constexpr Core::uint32 kShaderCompilationCacheSchemaVersion = 1;
enum class ShaderBytecodeFormat : Core::uint32 {
Unknown = 0,
DXIL,
DXBC,
SPIRV,
GLSLSource,
OpenGLProgramBinary
};
struct ShaderCompileKey {
Containers::String shaderPath;
Containers::String sourceHash;
Containers::String dependencyHash;
Containers::String passName;
Containers::String entryPoint;
Containers::String profile;
Containers::String compilerName;
Containers::String compilerVersion;
Containers::String optionsSignature;
ShaderType stage = ShaderType::Fragment;
ShaderLanguage sourceLanguage = ShaderLanguage::HLSL;
ShaderBackend backend = ShaderBackend::Generic;
Containers::Array<Containers::String> keywords;
void Normalize();
Containers::String BuildSignature() const;
Containers::String BuildCacheKey() const;
};
struct ShaderCacheEntry {
ShaderCompileKey key;
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
Containers::Array<Core::uint8> payload;
};
class ShaderCompilationCache {
public:
void Initialize(const Containers::String& libraryRoot);
void Shutdown();
bool IsInitialized() const { return !m_libraryRoot.Empty(); }
const Containers::String& GetLibraryRoot() const { return m_libraryRoot; }
const Containers::String& GetDatabasePath() const { return m_databasePath; }
Core::uint32 GetRecordCount() const { return static_cast<Core::uint32>(m_records.size()); }
Containers::String BuildCacheKey(const ShaderCompileKey& key) const;
Containers::String BuildCacheRelativePath(const ShaderCompileKey& key) const;
Containers::String BuildCacheAbsolutePath(const ShaderCompileKey& key) const;
bool Store(const ShaderCacheEntry& entry,
Containers::String* outErrorMessage = nullptr);
bool TryLoad(const ShaderCompileKey& key,
ShaderCacheEntry& outEntry,
Containers::String* outErrorMessage = nullptr) const;
private:
struct ShaderCacheRecord {
ShaderBackend backend = ShaderBackend::Generic;
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
Containers::String relativePath;
Core::uint64 payloadSize = 0;
};
void LoadDatabase();
void SaveDatabase() const;
static Containers::String BuildBackendDirectoryName(ShaderBackend backend);
static AssetGUID ComputeKeyGuid(const Containers::String& cacheKey);
Containers::String m_libraryRoot;
Containers::String m_databasePath;
std::unordered_map<std::string, ShaderCacheRecord> m_records;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -2,7 +2,8 @@
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include "Shader.h"
#include <vector>

View File

@@ -1,12 +1,12 @@
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include "ShaderLoader.h"
#include "Internal/ShaderArtifactLoader.h"
#include "Internal/ShaderAuthoringLoader.h"
#include "ShaderAuthoringParser.h"
#include "Internal/ShaderFileUtils.h"
#include "Internal/ShaderRuntimeBuildUtils.h"
#include "ShaderAuthoringParser.h"
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
#include <XCEngine/Resources/BuiltinResources.h>
#include <string>
@@ -30,7 +30,7 @@ bool ShaderLoader::CanLoad(const Containers::String& path) const {
return true;
}
const Containers::String ext = GetExtension(path).ToLower();
const Containers::String ext = GetShaderPathExtension(path).ToLower();
return ext == "shader" || ext == "xcshader";
}
@@ -112,7 +112,9 @@ bool ShaderLoader::CollectSourceDependencies(
return CollectShaderAuthoringDependencyPaths(path, sourceText, outDependencies, outError);
}
ShaderType ShaderLoader::DetectShaderType(const Containers::String& path, const Containers::String& source) {
ShaderType ShaderLoader::DetectShaderType(
const Containers::String& path,
const Containers::String& source) {
(void)source;
return DetectShaderTypeFromPath(path);
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "Shader.h"
namespace XCEngine {
namespace Resources {
class ShaderLoader : public IResourceLoader {
public:
ShaderLoader();
virtual ~ShaderLoader() override;
ResourceType GetResourceType() const override { return ResourceType::Shader; }
Containers::Array<Containers::String> GetSupportedExtensions() const override;
bool CanLoad(const Containers::String& path) const override;
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
ImportSettings* GetDefaultSettings() const override;
bool CollectSourceDependencies(const Containers::String& path,
Containers::Array<Containers::String>& outDependencies) const;
bool CollectSourceDependencies(const Containers::String& path,
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) const;
private:
ShaderType DetectShaderType(const Containers::String& path, const Containers::String& source);
bool ParseShaderSource(const Containers::String& source, Shader* shader);
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,6 @@
#include "ShaderSourceUtils.h"
#include <XCEngine/Core/Asset/ResourceManager.h>
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include <cctype>
#include <filesystem>

View File

@@ -1,7 +1,8 @@
#pragma once
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include "Shader.h"
#include <string>

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/Texture/Texture.h>
#include "Texture.h"
#include <cstring>
namespace XCEngine {

View File

@@ -0,0 +1,73 @@
#pragma once
#include <XCEngine/Core/Asset/IResource.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Resources {
enum class TextureType {
Texture2D, Texture3D, TextureCube, Texture2DArray, TextureCubeArray
};
enum class TextureFormat {
Unknown, R8_UNORM, RG8_UNORM, RGBA8_UNORM, RGBA8_SRGB,
R16_FLOAT, RG16_FLOAT, RGBA16_FLOAT,
R32_FLOAT, RG32_FLOAT, RGBA32_FLOAT,
D16_UNORM, D24_UNORM_S8_UINT, D32_FLOAT, D32_FLOAT_S8_X24_UINT,
BC1_UNORM, BC1_UNORM_SRGB, BC2_UNORM, BC2_UNORM_SRGB,
BC3_UNORM, BC3_UNORM_SRGB, BC4_UNORM, BC5_UNORM, BC6H_UF16,
BC7_UNORM, BC7_UNORM_SRGB
};
enum class TextureUsage : Core::uint8 {
None = 0, ShaderResource = 1 << 0, RenderTarget = 1 << 1,
DepthStencil = 1 << 2, UnorderedAccess = 1 << 3,
TransferSrc = 1 << 4, TransferDst = 1 << 5
};
class Texture : public IResource {
public:
Texture();
virtual ~Texture() override;
ResourceType GetType() const override { return ResourceType::Texture; }
const Containers::String& GetName() const override { return m_name; }
const Containers::String& GetPath() const override { return m_path; }
ResourceGUID GetGUID() const override { return m_guid; }
bool IsValid() const override { return m_isValid; }
size_t GetMemorySize() const override { return m_memorySize; }
void Release() override;
Core::uint32 GetWidth() const { return m_width; }
Core::uint32 GetHeight() const { return m_height; }
Core::uint32 GetDepth() const { return m_depth; }
Core::uint32 GetMipLevels() const { return m_mipLevels; }
Core::uint32 GetArraySize() const { return m_arraySize; }
TextureType GetTextureType() const { return m_textureType; }
TextureFormat GetFormat() const { return m_format; }
TextureUsage GetUsage() const { return m_usage; }
const void* GetPixelData() const { return m_pixelData.Data(); }
size_t GetPixelDataSize() const { return m_pixelData.Size(); }
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
Core::uint32 mipLevels, TextureType type, TextureFormat format,
const void* data, size_t dataSize, Core::uint32 arraySize = 1);
bool GenerateMipmaps();
private:
Core::uint32 m_width = 0;
Core::uint32 m_height = 0;
Core::uint32 m_depth = 1;
Core::uint32 m_mipLevels = 1;
Core::uint32 m_arraySize = 1;
TextureType m_textureType = TextureType::Texture2D;
TextureFormat m_format = TextureFormat::RGBA8_UNORM;
TextureUsage m_usage = TextureUsage::ShaderResource;
Containers::Array<Core::uint8> m_pixelData;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
#include "TextureImportSettings.h"
namespace XCEngine {
namespace Resources {

View File

@@ -0,0 +1,91 @@
#pragma once
#include <XCEngine/Core/Asset/ImportSettings.h>
#include "Texture.h"
#include <XCEngine/Core/Math/Vector3.h>
namespace XCEngine {
namespace Resources {
enum class MipmapFilter {
Box,
Kaiser
};
enum class CompressionQuality {
Low,
Medium,
High,
Ultra
};
class TextureImportSettings : public ImportSettings {
public:
TextureImportSettings();
virtual ~TextureImportSettings() override;
Core::UniqueRef<ImportSettings> Clone() const override;
bool LoadFromJSON(const Containers::String& json) override;
Containers::String SaveToJSON() const override;
void SetTextureType(TextureType type) { m_textureType = type; }
TextureType GetTextureType() const { return m_textureType; }
void SetTargetFormat(TextureFormat format) { m_targetFormat = format; }
TextureFormat GetTargetFormat() const { return m_targetFormat; }
void SetGenerateMipmaps(bool generate) { m_generateMipmaps = generate; }
bool GetGenerateMipmaps() const { return m_generateMipmaps; }
void SetMipmapFilter(MipmapFilter filter) { m_mipmapFilter = filter; }
MipmapFilter GetMipmapFilter() const { return m_mipmapFilter; }
void SetMaxAnisotropy(Core::uint32 anisotropy) { m_maxAnisotropy = anisotropy; }
Core::uint32 GetMaxAnisotropy() const { return m_maxAnisotropy; }
void SetSRGB(bool srgb) { m_sRGB = srgb; }
bool GetSRGB() const { return m_sRGB; }
void SetFlipVertical(bool flip) { m_flipVertical = flip; }
bool GetFlipVertical() const { return m_flipVertical; }
void SetFlipHorizontal(bool flip) { m_flipHorizontal = flip; }
bool GetFlipHorizontal() const { return m_flipHorizontal; }
void SetBorderColor(const Math::Vector3& color) { m_borderColor = color; }
const Math::Vector3& GetBorderColor() const { return m_borderColor; }
void SetCompressionQuality(CompressionQuality quality) { m_compressionQuality = quality; }
CompressionQuality GetCompressionQuality() const { return m_compressionQuality; }
void SetUseHardwareCompression(bool use) { m_useHardwareCompression = use; }
bool GetUseHardwareCompression() const { return m_useHardwareCompression; }
void SetMaxSize(Core::uint32 size) { m_maxSize = size; }
Core::uint32 GetMaxSize() const { return m_maxSize; }
void SetGenerateNormalMap(bool generate) { m_generateNormalMap = generate; }
bool GetGenerateNormalMap() const { return m_generateNormalMap; }
void SetNormalMapStrength(float strength) { m_normalMapStrength = strength; }
float GetNormalMapStrength() const { return m_normalMapStrength; }
private:
TextureType m_textureType = TextureType::Texture2D;
TextureFormat m_targetFormat = TextureFormat::Unknown;
bool m_generateMipmaps = true;
MipmapFilter m_mipmapFilter = MipmapFilter::Box;
Core::uint32 m_maxAnisotropy = 16;
bool m_sRGB = false;
bool m_flipVertical = false;
bool m_flipHorizontal = false;
Math::Vector3 m_borderColor = Math::Vector3::Zero();
CompressionQuality m_compressionQuality = CompressionQuality::High;
bool m_useHardwareCompression = true;
Core::uint32 m_maxSize = 0;
bool m_generateNormalMap = false;
float m_normalMapStrength = 1.0f;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,9 +1,9 @@
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include "TextureLoader.h"
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
#include "TextureImportSettings.h"
#include <stb_image.h>
#include <cstring>
#include <filesystem>

View File

@@ -0,0 +1,28 @@
#pragma once
#include <XCEngine/Core/Asset/ImportSettings.h>
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "Texture.h"
namespace XCEngine {
namespace Resources {
class TextureLoader : public IResourceLoader {
public:
TextureLoader();
virtual ~TextureLoader() override;
ResourceType GetResourceType() const override { return ResourceType::Texture; }
Containers::Array<Containers::String> GetSupportedExtensions() const override;
bool CanLoad(const Containers::String& path) const override;
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
LoadResult LoadFromMemory(
const Containers::String& path,
const void* data,
size_t dataSize,
const ImportSettings* settings = nullptr) const;
ImportSettings* GetDefaultSettings() const override;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,10 +1,11 @@
#include <XCEngine/Scene/ModelSceneInstantiation.h>
#include "ModelSceneInstantiation.h"
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Components/MeshFilterComponent.h>
#include <XCEngine/Components/MeshRendererComponent.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Scene/Scene.h>
#include "Scene.h"
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
namespace XCEngine {

View File

@@ -0,0 +1,37 @@
#pragma once
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Resources/Model/Model.h>
#include <vector>
namespace XCEngine {
namespace Components {
class GameObject;
class Scene;
}
struct ModelSceneInstantiationResult {
Components::GameObject* rootObject = nullptr;
std::vector<Components::GameObject*> nodeObjects;
std::vector<Components::GameObject*> meshObjects;
};
bool InstantiateModelHierarchy(
Components::Scene& scene,
const Resources::Model& model,
const Resources::AssetRef& modelAssetRef,
Components::GameObject* parent = nullptr,
ModelSceneInstantiationResult* outResult = nullptr,
Containers::String* outErrorMessage = nullptr);
bool InstantiateModelHierarchy(
Components::Scene& scene,
const Containers::String& modelPath,
Components::GameObject* parent = nullptr,
ModelSceneInstantiationResult* outResult = nullptr,
Containers::String* outErrorMessage = nullptr);
} // namespace XCEngine

View File

@@ -1,4 +1,4 @@
#include "Scene/RuntimeLoop.h"
#include "RuntimeLoop.h"
#include <algorithm>

View File

@@ -0,0 +1,47 @@
#pragma once
#include "SceneRuntime.h"
#include <cstdint>
namespace XCEngine {
namespace Components {
class RuntimeLoop {
public:
struct Settings {
float fixedDeltaTime = 1.0f / 50.0f;
float maxFrameDeltaTime = 0.1f;
uint32_t maxFixedStepsPerFrame = 4;
};
explicit RuntimeLoop(Settings settings = {});
void SetSettings(const Settings& settings);
const Settings& GetSettings() const { return m_settings; }
void Start(Scene* scene);
void Stop();
void ReplaceScene(Scene* scene);
void Tick(float deltaTime);
void Pause();
void Resume();
void StepFrame();
bool IsRunning() const { return m_sceneRuntime.IsRunning(); }
bool IsPaused() const { return m_paused; }
Scene* GetScene() const { return m_sceneRuntime.GetScene(); }
Physics::PhysicsWorld* GetPhysicsWorld() const { return m_sceneRuntime.GetPhysicsWorld(); }
float GetFixedAccumulator() const { return m_fixedAccumulator; }
private:
SceneRuntime m_sceneRuntime;
Settings m_settings = {};
float m_fixedAccumulator = 0.0f;
bool m_paused = false;
bool m_stepRequested = false;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -1,10 +1,12 @@
#include "Scene/Scene.h"
#include "Scene.h"
#include "Components/ComponentFactoryRegistry.h"
#include "Debug/Logger.h"
#include "Components/GameObject.h"
#include "Components/TransformComponent.h"
#include <sstream>
#include "Debug/Logger.h"
#include <algorithm>
#include <sstream>
#include <unordered_map>
namespace XCEngine {
@@ -18,10 +20,6 @@ bool ShouldTraceSceneComponentPayload(const std::string& payload) {
payload.find("New Material.mat") != std::string::npos;
}
} // namespace
namespace {
struct PendingComponentData {
std::string type;
std::string payload;
@@ -66,9 +64,15 @@ std::string UnescapeString(const std::string& value) {
if (value[i] == '\\' && i + 1 < value.size()) {
++i;
switch (value[i]) {
case 'n': unescaped.push_back('\n'); break;
case 'r': unescaped.push_back('\r'); break;
default: unescaped.push_back(value[i]); break;
case 'n':
unescaped.push_back('\n');
break;
case 'r':
unescaped.push_back('\r');
break;
default:
unescaped.push_back(value[i]);
break;
}
} else {
unescaped.push_back(value[i]);
@@ -155,21 +159,21 @@ Scene::~Scene() {
GameObject* Scene::CreateGameObject(const std::string& name, GameObject* parent) {
auto gameObject = std::make_unique<GameObject>(name);
GameObject* ptr = gameObject.get();
GameObject::GetGlobalRegistry()[ptr->m_id] = ptr;
m_gameObjectIDs.insert(ptr->m_id);
m_gameObjects.emplace(ptr->m_id, std::move(gameObject));
ptr->m_scene = this;
if (parent) {
ptr->SetParent(parent);
} else {
m_rootGameObjects.push_back(ptr->m_id);
}
ptr->Awake();
m_onGameObjectCreated.Invoke(ptr);
return ptr;
}
@@ -188,14 +192,13 @@ void Scene::DestroyGameObject(GameObject* gameObject) {
} else {
m_rootGameObjects.erase(
std::remove(m_rootGameObjects.begin(), m_rootGameObjects.end(), gameObject->m_id),
m_rootGameObjects.end()
);
m_rootGameObjects.end());
}
m_onGameObjectDestroyed.Invoke(gameObject);
gameObject->OnDestroy();
m_gameObjectIDs.erase(gameObject->m_id);
GameObject::GetGlobalRegistry().erase(gameObject->m_id);
m_gameObjects.erase(gameObject->m_id);
@@ -301,7 +304,9 @@ void Scene::DeserializeFromString(const std::string& data) {
GameObject::ID maxId = 0;
while (std::getline(input, line)) {
if (line.empty() || line[0] == '#') continue;
if (line.empty() || line[0] == '#') {
continue;
}
if (line == "gameobject_begin") {
pendingObjects.emplace_back();
@@ -385,16 +390,17 @@ void Scene::DeserializeFromString(const std::string& data) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[Scene] Deserialize component objectId=") +
Containers::String(std::to_string(pending.id).c_str()) +
" name=" +
Containers::String(pending.name.c_str()) +
" type=" +
Containers::String(componentData.type.c_str()) +
" payload=" +
Containers::String(componentData.payload.c_str()));
Containers::String(std::to_string(pending.id).c_str()) +
" name=" +
Containers::String(pending.name.c_str()) +
" type=" +
Containers::String(componentData.type.c_str()) +
" payload=" +
Containers::String(componentData.payload.c_str()));
}
if (Component* component = ComponentFactoryRegistry::Get().CreateComponent(go.get(), componentData.type)) {
if (Component* component =
ComponentFactoryRegistry::Get().CreateComponent(go.get(), componentData.type)) {
if (!componentData.payload.empty()) {
std::istringstream componentStream(componentData.payload);
component->Deserialize(componentStream);
@@ -467,9 +473,9 @@ void Scene::Load(const std::string& filePath) {
Debug::Logger::Get().Info(
Debug::LogCategory::FileSystem,
Containers::String("[Scene] Load bytes=") +
Containers::String(std::to_string(buffer.str().size()).c_str()) +
" file=" +
Containers::String(filePath.c_str()));
Containers::String(std::to_string(buffer.str().size()).c_str()) +
" file=" +
Containers::String(filePath.c_str()));
DeserializeFromString(buffer.str());
}

View File

@@ -0,0 +1,122 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <XCEngine/Core/Event.h>
#include <XCEngine/Components/GameObject.h>
namespace XCEngine {
namespace Components {
class Scene {
public:
using GameObjectID = uint64_t;
static constexpr GameObjectID INVALID_GAMEOBJECT_ID = 0;
Scene();
explicit Scene(const std::string& name);
~Scene();
const std::string& GetName() const { return m_name; }
void SetName(const std::string& name) { m_name = name; }
bool IsActive() const { return m_active; }
void SetActive(bool active) { m_active = active; }
GameObject* CreateGameObject(const std::string& name, GameObject* parent = nullptr);
void DestroyGameObject(GameObject* gameObject);
GameObject* Find(const std::string& name) const;
GameObject* FindByID(GameObjectID id) const;
GameObject* FindGameObjectWithTag(const std::string& tag) const;
template<typename T>
T* FindObjectOfType() const {
for (auto* go : GetRootGameObjects()) {
if (T* comp = go->GetComponent<T>()) {
return comp;
}
if (T* comp = FindInChildren<T>(go)) {
return comp;
}
}
return nullptr;
}
template<typename T>
std::vector<T*> FindObjectsOfType() const {
std::vector<T*> results;
for (auto* go : GetRootGameObjects()) {
if (T* comp = go->GetComponent<T>()) {
results.push_back(comp);
}
FindInChildren<T>(go, results);
}
return results;
}
std::vector<GameObject*> GetRootGameObjects() const;
void Update(float deltaTime);
void FixedUpdate(float fixedDeltaTime);
void LateUpdate(float deltaTime);
void Save(const std::string& filePath);
void Load(const std::string& filePath);
std::string SerializeToString() const;
void DeserializeFromString(const std::string& data);
Core::Event<GameObject*>& OnGameObjectCreated() { return m_onGameObjectCreated; }
Core::Event<GameObject*>& OnGameObjectDestroyed() { return m_onGameObjectDestroyed; }
Core::Event<GameObject*, Component*>& OnComponentAdded() { return m_onComponentAdded; }
Core::Event<GameObject*, Component*>& OnComponentRemoved() { return m_onComponentRemoved; }
private:
GameObject* FindInChildren(GameObject* parent, const std::string& name) const;
template<typename T>
T* FindInChildren(GameObject* parent) const {
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
GameObject* child = parent->GetChild(i);
if (T* comp = child->GetComponent<T>()) {
return comp;
}
if (T* comp = FindInChildren<T>(child)) {
return comp;
}
}
return nullptr;
}
template<typename T>
void FindInChildren(GameObject* parent, std::vector<T*>& results) const {
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
GameObject* child = parent->GetChild(i);
if (T* comp = child->GetComponent<T>()) {
results.push_back(comp);
}
FindInChildren<T>(child, results);
}
}
std::string m_name;
bool m_active = true;
std::unordered_map<GameObjectID, std::unique_ptr<GameObject>> m_gameObjects;
std::vector<GameObjectID> m_rootGameObjects;
std::unordered_set<GameObjectID> m_gameObjectIDs;
Core::Event<GameObject*> m_onGameObjectCreated;
Core::Event<GameObject*> m_onGameObjectDestroyed;
Core::Event<GameObject*, Component*> m_onComponentAdded;
Core::Event<GameObject*, Component*> m_onComponentRemoved;
friend class GameObject;
friend class SceneManager;
};
} // namespace Components
} // namespace XCEngine

Some files were not shown because too many files have changed in this diff Show More