Enforce Content:CloneAssetFile() running on the main thread, to avoid a bug that occurs when a particle emitter is created from one of the templates due to the creation coming from the content thread.

This commit is contained in:
Menotdan
2024-04-11 17:37:31 -04:00
parent b2f9da4113
commit e137d31839

View File

@@ -14,6 +14,7 @@
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/Graphics.h"
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
@@ -688,101 +689,135 @@ bool Content::FastTmpAssetClone(const StringView& path, String& resultPath)
return false; return false;
} }
bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPath, const Guid& dstId) class CloneAssetFileTask : public MainThreadTask
{ {
PROFILE_CPU(); public:
ASSERT(FileSystem::AreFilePathsEqual(srcPath, dstPath) == false && dstId.IsValid()); StringView dstPath;
StringView srcPath;
Guid dstId;
bool* output;
LOG(Info, "Cloning asset \'{0}\' to \'{1}\'({2}).", srcPath, dstPath, dstId); protected:
bool Run() override
// Check source file
if (!FileSystem::FileExists(srcPath))
{ {
LOG(Warning, "Missing source file."); *output = Content::CloneAssetFile(dstPath, srcPath, dstId);
return true;
}
// Special case for json resources
if (JsonStorageProxy::IsValidExtension(FileSystem::GetExtension(srcPath).ToLower()))
{
if (FileSystem::CopyFile(dstPath, srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
if (JsonStorageProxy::ChangeId(dstPath, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
return false; return false;
} }
};
// Check if destination file is missing bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPath, const Guid& dstId)
if (!FileSystem::FileExists(dstPath)) {
// Best to run this on the main thread to avoid clone conflicts.
if (IsInMainThread())
{ {
// Use quick file copy PROFILE_CPU();
if (FileSystem::CopyFile(dstPath, srcPath)) ASSERT(FileSystem::AreFilePathsEqual(srcPath, dstPath) == false && dstId.IsValid());
LOG(Info, "Cloning asset \'{0}\' to \'{1}\'({2}).", srcPath, dstPath, dstId);
// Check source file
if (!FileSystem::FileExists(srcPath))
{ {
LOG(Warning, "Cannot copy file to destination."); LOG(Warning, "Missing source file.");
return true; return true;
} }
// Change ID // Special case for json resources
auto storage = ContentStorageManager::GetStorage(dstPath); if (JsonStorageProxy::IsValidExtension(FileSystem::GetExtension(srcPath).ToLower()))
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage == nullptr || storage->ChangeAssetID(e, dstId))
{ {
LOG(Warning, "Cannot change asset ID."); if (FileSystem::CopyFile(dstPath, srcPath))
return true; {
LOG(Warning, "Cannot copy file to destination.");
return true;
}
if (JsonStorageProxy::ChangeId(dstPath, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
return false;
}
// Check if destination file is missing
if (!FileSystem::FileExists(dstPath))
{
// Use quick file copy
if (FileSystem::CopyFile(dstPath, srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Change ID
auto storage = ContentStorageManager::GetStorage(dstPath);
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage == nullptr || storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
else
{
// Use temporary file
String tmpPath = Globals::TemporaryFolder / Guid::New().ToString(Guid::FormatType::D);
if (FileSystem::CopyFile(tmpPath, srcPath))
{
LOG(Warning, "Cannot copy file.");
return true;
}
// Change asset ID
{
auto storage = ContentStorageManager::GetStorage(tmpPath);
if (!storage)
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
// Unlock destination file
ContentStorageManager::EnsureAccess(dstPath);
// Copy temp file to the destination
if (FileSystem::CopyFile(dstPath, tmpPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Cleanup
FileSystem::DeleteFile(tmpPath);
// Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath))
{
storage->Reload();
}
} }
} }
else else
{ {
// Use temporary file CloneAssetFileTask* task = New<CloneAssetFileTask>();
String tmpPath = Globals::TemporaryFolder / Guid::New().ToString(Guid::FormatType::D); task->dstId = dstId;
if (FileSystem::CopyFile(tmpPath, srcPath)) task->dstPath = dstPath;
{ task->srcPath = srcPath;
LOG(Warning, "Cannot copy file.");
return true;
}
// Change asset ID bool result = false;
{ task->output = &result;
auto storage = ContentStorageManager::GetStorage(tmpPath); task->Start();
if (!storage) task->Wait();
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
// Unlock destination file return result;
ContentStorageManager::EnsureAccess(dstPath);
// Copy temp file to the destination
if (FileSystem::CopyFile(dstPath, tmpPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Cleanup
FileSystem::DeleteFile(tmpPath);
// Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath))
{
storage->Reload();
}
} }
return false; return false;