Optimize content modifications watching events processing in large projects

This commit is contained in:
2026-04-24 23:47:22 +02:00
parent c33b2cc11e
commit edb3badcb3
3 changed files with 26 additions and 10 deletions
+16 -10
View File
@@ -24,7 +24,7 @@ namespace FlaxEditor.Modules
private bool _rebuildInitFlag;
private int _itemsCreated;
private int _itemsDeleted;
private readonly HashSet<MainContentFolderTreeNode> _dirtyNodes = new HashSet<MainContentFolderTreeNode>();
private readonly HashSet<ContentFolderTreeNode> _dirtyNodes = new HashSet<ContentFolderTreeNode>();
/// <summary>
/// The project directory.
@@ -1309,27 +1309,32 @@ namespace FlaxEditor.Modules
internal void OnDirectoryEvent(MainContentFolderTreeNode node, FileSystemEventArgs e)
{
// Ensure to be ready for external events
// Ignore events during fast setup
if (_isDuringFastSetup)
return;
ContentFolderTreeNode dirtyNode = node;
// TODO: maybe we could make it faster! since we have a path so it would be easy to just create or delete given file. but remember about subdirectories
// Filter the node based on modified path
// (eg. if we have event for 'Content/Folder1/Folder2' and node is 'Content/Folder1' then we should process but skip other 'Content' subfolders)
var path = StringUtils.NormalizePath(Path.GetDirectoryName(e.FullPath));
var pathItem = node.Folder.Find(path) as ContentFolder;
if (pathItem != null)
{
dirtyNode = pathItem.Node;
}
// Switch type
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
case WatcherChangeTypes.Deleted:
case WatcherChangeTypes.Renamed:
{
lock (_dirtyNodes)
{
_dirtyNodes.Add(node);
_dirtyNodes.Add(dirtyNode);
}
break;
}
}
}
private void OnScriptsReload()
{
@@ -1383,14 +1388,15 @@ namespace FlaxEditor.Modules
// Update all dirty content tree nodes
lock (_dirtyNodes)
{
Profiler.BeginEvent("ContentDatabase.Refresh");
foreach (var node in _dirtyNodes)
{
LoadFolder(node, true);
if (_enableEvents)
WorkspaceModified?.Invoke();
}
if (_enableEvents && _dirtyNodes.Count != 0)
WorkspaceModified?.Invoke();
_dirtyNodes.Clear();
Profiler.EndEvent();
}
// Lazy-rebuilds
@@ -113,6 +113,8 @@ CreateAssetContext::CreateAssetContext(const StringView& inputPath, const String
CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
{
PROFILE_CPU();
PROFILE_MEM(Content);
ASSERT(callback.IsBinded());
// Call action
@@ -207,6 +209,9 @@ void CreateAssetContext::AddMeta(JsonWriter& writer) const
void CreateAssetContext::ApplyChanges()
{
PROFILE_CPU();
PROFILE_MEM(Content);
// Get access
auto storage = ContentStorageManager::TryGetStorage(TargetAssetPath);
if (storage && storage->IsLoaded())
@@ -274,6 +279,8 @@ bool AssetsImportingManager::Create(const String& tag, const StringView& outputP
bool AssetsImportingManager::Import(const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
{
PROFILE_CPU();
PROFILE_MEM(Content);
LOG(Info, "Importing file '{0}' to '{1}'...", inputPath, outputPath);
// Check if input file exists
@@ -347,6 +354,7 @@ String AssetsImportingManager::GetImportPath(const String& path)
bool AssetsImportingManager::Create(const Function<CreateAssetResult(CreateAssetContext&)>& callback, const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
{
PROFILE_CPU();
PROFILE_MEM(Content);
ZoneText(*outputPath, outputPath.Length());
const auto startTime = Platform::GetTimeSeconds();
@@ -310,6 +310,7 @@ namespace
{
if (action == FileSystemAction::Delete)
return;
PROFILE_CPU();
// Get list of assets using this shader file
Array<Asset*> toReload;
@@ -515,6 +516,7 @@ namespace
{
if (action == FileSystemAction::Delete || !path.EndsWith(TEXT(".shader")))
return;
PROFILE_CPU();
LOG(Info, "Shader \'{0}\' has been modified.", path);