You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetEntryChangeset.cpp

306 lines
13 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetEntryChangeset.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetEntryChange.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetEntryChangeset::AssetEntryChangeset(
AZStd::shared_ptr<AssetDatabase::AssetDatabaseConnection> databaseConnection,
AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry)
: m_databaseConnection(databaseConnection)
, m_rootEntry(rootEntry)
, m_fullUpdate(false)
, m_updated(false)
{}
AssetEntryChangeset::~AssetEntryChangeset()
{
for (auto change : m_changes)
{
delete change;
}
}
void AssetEntryChangeset::PopulateEntries()
{
m_fullUpdate = true;
}
void AssetEntryChangeset::Update()
{
if (m_fullUpdate)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_fullUpdate = false;
m_updated = true;
QueryEntireDatabase();
m_fileIdsToAdd.clear();
m_sourceUuidsToAdd.clear();
m_productAssetIdsToAdd.clear();
}
else
{
QueryChangeset();
}
}
void AssetEntryChangeset::Synchronize()
{
using namespace AssetDatabase;
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
if (m_updated)
{
m_rootEntry->SetInitialUpdate(true);
m_rootEntry->Update(m_relativePath.c_str());
m_updated = false;
}
// iterate through new changes and try to apply them
// if application of change fails, try them again next tick
AZStd::vector<AssetEntryChange*> changesFailed;
for (auto change : m_changes)
{
if (change->Apply(m_rootEntry))
{
delete change;
}
else
{
changesFailed.push_back(change);
}
}
#if AZ_DEBUG_BUILD
if (m_changes.size() > 0)
{
AZ_TracePrintf("Asset Browser DEBUG", "%d/%d data changes applied\n", m_changes.size() - changesFailed.size(), m_changes.size());
}
#endif
// try again next time.
m_changes = changesFailed;
if (m_rootEntry->IsInitialUpdate())
{
m_rootEntry->SetInitialUpdate(false);
}
}
void AssetEntryChangeset::AddFile(const AZ::s64& fileId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_fileIdsToAdd.push_back(fileId);
}
void AssetEntryChangeset::RemoveFile(const AZ::s64& fileId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew RemoveFileChange(fileId));
}
void AssetEntryChangeset::AddSource(const AZ::Uuid& sourceUuid)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_sourceUuidsToAdd.push_back(sourceUuid);
}
void AssetEntryChangeset::RemoveSource(const AZ::Uuid& sourceUuid)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew RemoveSourceChange(sourceUuid));
}
void AssetEntryChangeset::AddProduct(const AZ::Data::AssetId& assetId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_productAssetIdsToAdd.push_back(assetId);
}
void AssetEntryChangeset::RemoveProduct(const AZ::Data::AssetId& assetId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
auto findPredicate = [&assetId](const AssetEntryChange* toCheck)
{
if (const AddProductChange* productChange = azrtti_cast<const AddProductChange*>(toCheck))
{
return productChange->GetAssetId() == assetId;
}
return false;
};
// make sure any pending "add" commands are erased.
m_productAssetIdsToAdd.erase(AZStd::remove(m_productAssetIdsToAdd.begin(), m_productAssetIdsToAdd.end(), assetId), m_productAssetIdsToAdd.end());
auto foundExisting = AZStd::find_if(m_changes.begin(), m_changes.end(), findPredicate);
if (foundExisting != m_changes.end())
{
// remove the still-pending "add Product" from the list, no longer necesary.
m_changes.erase(foundExisting);
}
m_changes.push_back(aznew RemoveProductChange(assetId));
}
void AssetEntryChangeset::QueryEntireDatabase()
{
using namespace AssetDatabase;
// querying the asset database for the root folder
m_databaseConnection->QueryScanFolderByDisplayName(
"root",
[=](ScanFolderDatabaseEntry& scanFolderDatabaseEntry)
{
m_relativePath = scanFolderDatabaseEntry.m_scanFolder.c_str();
return true;
});
// query all scanfolders
m_databaseConnection->QueryScanFoldersTable(
[&](ScanFolderDatabaseEntry& scanFolder)
{
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (scanFolder.m_isRoot)
{
return true;
}
m_changes.push_back(aznew AddScanFolderChange(scanFolder));
return m_databaseConnection->QueryFilesByScanFolderID(scanFolder.m_scanFolderID,
[&](FileDatabaseEntry& file)
{
m_changes.push_back(aznew AddFileChange(file));
return m_databaseConnection->QuerySourceBySourceNameScanFolderID(file.m_fileName.c_str(), scanFolder.m_scanFolderID,
[&](SourceDatabaseEntry& source)
{
m_changes.push_back(aznew AddSourceChange({ file.m_fileID, source }));
return m_databaseConnection->QueryProductBySourceID(source.m_sourceID,
[&](ProductDatabaseEntry& product)
{
m_changes.push_back(aznew AddProductChange({ source.m_sourceGuid, product }));
return true;
});
});
});
});
}
// this function translates from a series of incoming change notifies into an actual
// changeset that can then be applied to the model. Change notifies are brief and may contain
// only minimal information such as fileId. This transforms them into larger sequences of changes
// that include the creation of intermediate parent(s) if necessary.
void AssetEntryChangeset::QueryChangeset()
{
using namespace AssetDatabase;
AZStd::vector<AZ::s64> fileIdsToAdd;
AZStd::vector<AZ::Uuid> sourceUuidsToAdd;
AZStd::vector<AZ::Data::AssetId> productAssetIdsToAdd;
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
fileIdsToAdd = AZStd::move(m_fileIdsToAdd);
m_fileIdsToAdd.clear();
sourceUuidsToAdd = AZStd::move(m_sourceUuidsToAdd);
m_sourceUuidsToAdd.clear();
productAssetIdsToAdd = AZStd::move(m_productAssetIdsToAdd);
m_productAssetIdsToAdd.clear();
}
for (const AZ::s64& fileId : fileIdsToAdd)
{
m_databaseConnection->QueryFileByFileID(fileId,
[&](FileDatabaseEntry& file)
{
return m_databaseConnection->QueryScanFolderByScanFolderID(file.m_scanFolderPK,
[&](ScanFolderDatabaseEntry& scanFolder)
{
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (!scanFolder.m_isRoot)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew AddFileChange(file));
}
return true;
});
});
}
for (const AZ::Data::AssetId& assetId : productAssetIdsToAdd)
{
if (AZStd::find(sourceUuidsToAdd.begin(), sourceUuidsToAdd.end(), assetId.m_guid) == sourceUuidsToAdd.end())
{
sourceUuidsToAdd.push_back(assetId.m_guid);
}
m_databaseConnection->QueryProductBySourceGuidSubID(assetId.m_guid, assetId.m_subId,
[&](ProductDatabaseEntry& product)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew AddProductChange({ assetId.m_guid, product }));
return true;
});
}
for (const AZ::Uuid& sourceUuid : sourceUuidsToAdd)
{
m_databaseConnection->QuerySourceBySourceGuid(sourceUuid,
[&](SourceDatabaseEntry& source)
{
return m_databaseConnection->QueryFileByFileNameScanFolderID(source.m_sourceName.c_str(), source.m_scanFolderPK,
[&](FileDatabaseEntry& file)
{
return m_databaseConnection->QueryScanFolderByScanFolderID(file.m_scanFolderPK,
[&](ScanFolderDatabaseEntry& scanFolder)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (!scanFolder.m_isRoot)
{
m_changes.push_back(new AddSourceChange({ file.m_fileID, source }));
}
else
{
// if products belonging to entry in root folder are considered, remove them from changes
m_changes.erase(AZStd::remove_if(m_changes.begin(), m_changes.end(),
[sourceUuid](AssetEntryChange* change)
{
auto addProductChange = azrtti_cast<AddProductChange*>(change);
if (addProductChange && addProductChange->GetUuid() == sourceUuid)
{
delete change;
return true;
}
return false;
}),
m_changes.end());
}
return true;
});
});
});
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework