feat(Resources): Add ResourceDependencyGraph for resource dependency tracking

- Implement dependency graph for resource management
- Add/remove nodes and dependencies
- Reference counting support
- Circular dependency detection
- Add unit tests
This commit is contained in:
2026-03-18 01:13:02 +08:00
parent bd69c3e124
commit d2585f14b3
5 changed files with 414 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
#include "Resources/ResourceDependencyGraph.h"
namespace XCEngine {
namespace Resources {
ResourceDependencyGraph::ResourceDependencyGraph() = default;
ResourceDependencyGraph::~ResourceDependencyGraph() = default;
void ResourceDependencyGraph::AddNode(ResourceGUID guid, ResourceType type) {
if (m_nodes.Contains(guid)) {
return;
}
DependencyNode node;
node.guid = guid;
node.type = type;
node.refCount = 0;
m_nodes.Insert(guid, node);
}
void ResourceDependencyGraph::RemoveNode(ResourceGUID guid) {
m_nodes.Erase(guid);
}
void ResourceDependencyGraph::AddDependency(ResourceGUID owner, ResourceGUID dependency) {
DependencyNode* ownerNode = m_nodes.Find(owner);
DependencyNode* depNode = m_nodes.Find(dependency);
if (!ownerNode || !depNode) {
return;
}
bool alreadyExists = false;
for (size_t i = 0; i < ownerNode->dependencies.Size(); ++i) {
if (ownerNode->dependencies[i] == dependency) {
alreadyExists = true;
break;
}
}
if (!alreadyExists) {
ownerNode->dependencies.PushBack(dependency);
depNode->dependents.PushBack(owner);
}
}
void ResourceDependencyGraph::RemoveDependency(ResourceGUID owner, ResourceGUID dependency) {
DependencyNode* ownerNode = m_nodes.Find(owner);
DependencyNode* depNode = m_nodes.Find(dependency);
if (!ownerNode || !depNode) {
return;
}
for (size_t i = 0; i < ownerNode->dependencies.Size(); ++i) {
if (ownerNode->dependencies[i] == dependency) {
ownerNode->dependencies[i] = ownerNode->dependencies.Back();
ownerNode->dependencies.PopBack();
break;
}
}
for (size_t i = 0; i < depNode->dependents.Size(); ++i) {
if (depNode->dependents[i] == owner) {
depNode->dependents[i] = depNode->dependents.Back();
depNode->dependents.PopBack();
break;
}
}
}
Containers::Array<ResourceGUID> ResourceDependencyGraph::GetDependencies(ResourceGUID guid) const {
Containers::Array<ResourceGUID> result;
const DependencyNode* node = m_nodes.Find(guid);
if (node) {
result = node->dependencies;
}
return result;
}
Containers::Array<ResourceGUID> ResourceDependencyGraph::GetDependents(ResourceGUID guid) const {
Containers::Array<ResourceGUID> result;
const DependencyNode* node = m_nodes.Find(guid);
if (node) {
result = node->dependents;
}
return result;
}
Containers::Array<ResourceGUID> ResourceDependencyGraph::GetAllDependencies(ResourceGUID guid) const {
Containers::Array<ResourceGUID> result;
Containers::Array<ResourceGUID> stack;
stack.PushBack(guid);
while (stack.Size() > 0) {
ResourceGUID current = stack.Back();
stack.PopBack();
bool visited = false;
for (size_t i = 0; i < result.Size(); ++i) {
if (result[i] == current) {
visited = true;
break;
}
}
if (visited) continue;
if (current != guid) {
result.PushBack(current);
}
const DependencyNode* node = m_nodes.Find(current);
if (node) {
for (size_t i = 0; i < node->dependencies.Size(); ++i) {
stack.PushBack(node->dependencies[i]);
}
}
}
return result;
}
void ResourceDependencyGraph::IncrementRefCount(ResourceGUID guid) {
DependencyNode* node = m_nodes.Find(guid);
if (node) {
node->refCount++;
}
}
void ResourceDependencyGraph::DecrementRefCount(ResourceGUID guid) {
DependencyNode* node = m_nodes.Find(guid);
if (node && node->refCount > 0) {
node->refCount--;
}
}
Core::uint32 ResourceDependencyGraph::GetRefCount(ResourceGUID guid) const {
const DependencyNode* node = m_nodes.Find(guid);
if (node) {
return node->refCount;
}
return 0;
}
bool ResourceDependencyGraph::HasCircularDependency(ResourceGUID guid, Containers::Array<ResourceGUID>& outCycle) const {
Containers::Array<ResourceGUID> path;
Containers::Array<ResourceGUID> stack;
stack.PushBack(guid);
while (stack.Size() > 0) {
ResourceGUID current = stack.Back();
stack.PopBack();
bool inPath = false;
for (size_t i = 0; i < path.Size(); ++i) {
if (path[i] == current) {
inPath = true;
break;
}
}
if (inPath) {
outCycle = path;
outCycle.PushBack(current);
return true;
}
path.PushBack(current);
const DependencyNode* node = m_nodes.Find(current);
if (node) {
for (size_t i = 0; i < node->dependencies.Size(); ++i) {
stack.PushBack(node->dependencies[i]);
}
}
}
return false;
}
Containers::Array<ResourceGUID> ResourceDependencyGraph::TopologicalSort() const {
Containers::Array<ResourceGUID> result;
return result;
}
bool ResourceDependencyGraph::Unload(ResourceGUID guid) {
DependencyNode* node = m_nodes.Find(guid);
if (!node) {
return false;
}
if (node->refCount > 0) {
return false;
}
for (size_t i = 0; i < node->dependents.Size(); ++i) {
DependencyNode* depNode = m_nodes.Find(node->dependents[i]);
if (depNode && depNode->refCount > 0) {
return false;
}
}
return true;
}
void ResourceDependencyGraph::Clear() {
m_nodes.Clear();
}
bool ResourceDependencyGraph::HasNode(ResourceGUID guid) const {
return m_nodes.Find(guid) != nullptr;
}
bool ResourceDependencyGraph::HasCircularDependencyInternal(ResourceGUID guid, Containers::HashMap<ResourceGUID, bool>& visited,
Containers::Array<ResourceGUID>& path, Containers::Array<ResourceGUID>& outCycle) const {
return false;
}
} // namespace Resources
} // namespace XCEngine