/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include static AZStd::atomic_bool forceStop{ false }; void TestCreateAssets(const AZ::StringSet& someStrings) { bool loadBlocking = false; for (auto& thisString : someStrings) { if (thisString == "block") { loadBlocking = true; } } auto runJob = AZ::CreateJobFunction([loadBlocking]() { AZStd::vector> assetList; AZ::Data::AssetCatalogRequests::AssetEnumerationCB collectAssetsCb = [&](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info) { if (AZ::Data::AssetManager::Instance().GetHandler(info.m_assetType)) { auto somePair = AZStd::make_pair(id, info); assetList.push_back(somePair); } }; AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, collectAssetsCb, nullptr); auto start = AZStd::chrono::system_clock::now(); AZStd::vector> foundAssets; for (const auto& thisElement : assetList) { foundAssets.emplace_back(AZ::Data::AssetManager::Instance().GetAsset(thisElement.first, thisElement.second.m_assetType, true, nullptr, loadBlocking)); } size_t totalAssets; AZStd::chrono::milliseconds maxWait{ 20000 }; AZStd::chrono::milliseconds runMS{ 0 }; AZStd::vector> loadedAssets; totalAssets = foundAssets.size(); while (!foundAssets.empty() && runMS < maxWait) { AZStd::vector> stillRemaining; for (auto& foundAsset : foundAssets) { if (!foundAsset || !foundAsset.GetId().IsValid()) { AZ_TracePrintf("TestCreateAssets", "Asset %s type %s (%s) was not found\n", foundAsset.GetId().ToString().c_str(), foundAsset.GetType().ToString().c_str(), foundAsset.GetHint().c_str()); continue; } if (foundAsset.IsReady()) { loadedAssets.emplace_back(AZStd::move(foundAsset)); } else { stillRemaining.emplace_back(AZStd::move(foundAsset)); } } foundAssets = AZStd::move(stillRemaining); runMS = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - start); if (loadedAssets.size() == totalAssets) { AZ_TracePrintf("TestCreateAssets", "All assets (%d) ready after %ld ms\n", loadedAssets.size(), runMS); return; } AZ_TracePrintf("TestCreateAssets", "%d / %d ready after %ld ms\n", loadedAssets.size(), totalAssets, runMS); AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); } }, true); runJob->Start(); } AZ_CONSOLEFREEFUNC(TestCreateAssets, AZ::ConsoleFunctorFlags::Null, "Time the creation of all assets in the catalog"); void TestChangeAssets(const AZ::StringSet& someStrings) { AZStd::chrono::milliseconds runTime{ 60000 }; // Total test run time in milliseconds AZStd::chrono::milliseconds changeFreq{ 1000 }; // How often to perform the update cycle unsigned long changePercent = 10; // what percent of the assets should we simulate receiving AssetChanged messages for unsigned long holdPercent = 20; // What percent of assets should we get a reference to and hold each update unsigned long loadBlocking = 0; // What percent of root asset loads should be requested as blocking AZ::u64 seedValue = 1234; for (auto thisIndex = 0; thisIndex < someStrings.size(); ++thisIndex) { auto& thisString = someStrings[thisIndex]; if (thisString == "stop") { AZ_TracePrintf("TestChangeAssets", "Stopping tests.\n"); forceStop = true; } else if (thisString == "start") { AZ_TracePrintf("TestChangeAssets", "Enabling tests.\n"); forceStop = false; } else if (thisIndex < someStrings.size() - 1) { if (thisString == "runtime") { runTime = AZStd::chrono::milliseconds(stoull(someStrings[++thisIndex])); } else if (thisString == "frequency") { changeFreq = AZStd::chrono::milliseconds(stoull(someStrings[++thisIndex])); } else if (thisString == "change") { changePercent = stoul(someStrings[++thisIndex]); } else if (thisString == "hold") { holdPercent = stoul(someStrings[++thisIndex]); } else if (thisString == "loadblocking") { loadBlocking = stoul(someStrings[++thisIndex]);; } else if (thisString == "seed") { seedValue = stoull(someStrings[++thisIndex]); } } } AZ_TracePrintf("TestChangeAssets", "%s: Running for %llu ms freq %llu holding %u percent changing %u percent blocking %u percent seed value %llu\n", forceStop ? "STOPPED" : "START", runTime, changeFreq, holdPercent, changePercent, loadBlocking,seedValue); auto runJob = AZ::CreateJobFunction([runTime, changeFreq, changePercent, holdPercent, loadBlocking, seedValue]() { AZStd::vector> assetList; AZ::Data::AssetCatalogRequests::AssetEnumerationCB collectAssetsCb = [&](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info) { if (AZ::Data::AssetManager::Instance().GetHandler(info.m_assetType)) { AZ::Outcome, AZStd::string> result; AZ::Data::AssetCatalogRequestBus::BroadcastResult(result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, id); AzFramework::AssetSystem::AssetNotificationMessage message; message.m_assetId = id; message.m_data = info.m_relativePath; message.m_sizeBytes = info.m_sizeBytes; message.m_assetType = info.m_assetType; // Assets with no dependencies won't return a successful response if (result.IsSuccess()) { message.m_dependencies = result.TakeValue(); } auto somePair = AZStd::make_pair(id, message); assetList.push_back(somePair); } }; AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, collectAssetsCb, nullptr); auto start = AZStd::chrono::system_clock::now(); AZStd::chrono::milliseconds runMs{ 0 }; AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface::Get(); if (!notificationInterface) { AZ_Warning("TestChangeAssets", false, "Couldn't get notification interface to send change messages\n"); return; } AZ::SimpleLcgRandom randomizer(seedValue); int lastTick = 0; AZStd::vector> heldAssets; AZStd::size_t heldCount{ 0 }; AZ::u64 changeCount{ 0 }; AZ::u64 blockCount{ 0 }; AZ_TracePrintf("TestChangeAssets", "Beginning run with %zu assets\n", assetList.size()); while (!forceStop && runMs < runTime) { runMs = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - start); int thisTick = aznumeric_cast(runMs / changeFreq) + 1; if (thisTick > lastTick) { heldAssets.clear(); lastTick = thisTick; AZ::u64 thisChangeCount{ 0 }; AZ::u64 thisBlockCount{ 0 }; for (const auto& thisElement : assetList) { bool blockLoad = (randomizer.GetRandom() % 100 < loadBlocking); if (randomizer.GetRandom() % 100 < holdPercent) { heldAssets.emplace_back(AZ::Data::AssetManager::Instance().GetAsset(thisElement.first, thisElement.second.m_assetType, true, nullptr, blockLoad)); if (blockLoad) { ++thisBlockCount; } } if (randomizer.GetRandom() % 100 < changePercent) { ++thisChangeCount; notificationInterface->AssetChanged(thisElement.second); } } AZ_TracePrintf("TestChangeAssets", "On Tick %d held %zu assets, block loaded %llu and queued changes for %llu\n", lastTick, heldAssets.size(), thisBlockCount, thisChangeCount); heldCount += heldAssets.size(); blockCount += thisBlockCount; changeCount += thisChangeCount; AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(300)); } } AZ_TracePrintf("TestChangeAssets", "DONE: After %llu ms held %zu assets, block loaded %llu and queued changes for %llu\n", runMs, heldCount, blockCount, changeCount); }, true); runJob->Start(); } AZ_CONSOLEFREEFUNC(TestChangeAssets, AZ::ConsoleFunctorFlags::Null, "Perform series of randomized asset change updates to stress asset reload systems"); void TestCreateContainers(const AZ::StringSet& someStrings) { auto runJob = AZ::CreateJobFunction([]() { AZStd::vector> assetList; AZ::Data::AssetCatalogRequests::AssetEnumerationCB collectAssetsCb = [&](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info) { if (AZ::Data::AssetManager::Instance().GetHandler(info.m_assetType)) { auto somePair = AZStd::make_pair(id, info); assetList.push_back(somePair); } }; AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, collectAssetsCb, nullptr); auto start = AZStd::chrono::system_clock::now(); AZStd::vector> loadingContainers; for (const auto& thisElement : assetList) { loadingContainers.emplace_back(AZ::Data::AssetManager::Instance().GetAssetContainer(thisElement.first, thisElement.second.m_assetType)); } AZStd::chrono::milliseconds maxWait{ 5000 }; AZStd::chrono::milliseconds runMS{ 0 }; AZStd::vector> readyContainers; size_t totalContainers = loadingContainers.size(); while(readyContainers.size() < totalContainers && runMS < maxWait) { auto containerIter = loadingContainers.begin(); while (containerIter != loadingContainers.end()) { if ((*containerIter)->IsReady()) { readyContainers.push_back(*containerIter); containerIter = loadingContainers.erase(containerIter); } else { ++containerIter; } } runMS = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - start); if (readyContainers.size() == totalContainers) { AZ_TracePrintf("TestCreateContainers", "All assets (%d) ready after %ld ms\n", readyContainers.size(), runMS); return; } AZ_TracePrintf("TestCreateContainers", "%d / %d ready after %ld ms\n", readyContainers.size(), totalContainers, runMS); AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); } if (loadingContainers.size()) { AZ_TracePrintf("TestCreateContainers", "Failed to load %d / %d containers\n", loadingContainers.size(), totalContainers); for (auto& thisContainer : loadingContainers) { AZ_TracePrintf("TestCreateContainers", "Couldn't load container for %s\n", thisContainer->GetAsset().ToString().c_str()); } } }, true); runJob->Start(); } AZ_CONSOLEFREEFUNC(TestCreateContainers, AZ::ConsoleFunctorFlags::Null, "Time the creation of all assets in the catalog as containers"); void TestSingleContainer(const AZ::StringSet& someStrings) { if (someStrings.empty()) { AZ_Warning("TestSingleContainer", false, "Need a valid id to test against"); return; } auto uuidString = someStrings[0]; AZ::u32 subId{0}; auto subidPos = uuidString.rfind(':'); if (subidPos != AZStd::string::npos) { auto subidstr = uuidString.substr(subidPos + 1); subId = atoi(subidstr.c_str()); uuidString = uuidString.substr(0, subidPos); } AZ::Data::AssetId assetId(AZ::Uuid::CreateStringPermissive(uuidString.c_str()), subId); auto runJob = AZ::CreateJobFunction([assetId]() { AZStd::vector> assetList; AZ::Data::AssetInfo assetInfo; AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId); if (!assetInfo.m_assetId.IsValid()) { AZ_Warning("TestSingleContainer", false, "Couldn't get asset info for %s", assetId.ToString().c_str()); } auto start = AZStd::chrono::system_clock::now(); auto thisContainer = AZ::Data::AssetManager::Instance().GetAssetContainer(assetId, assetInfo.m_assetType); AZStd::chrono::milliseconds maxWait{ 2000 }; AZStd::chrono::milliseconds runMS{ 0 }; while(thisContainer->IsLoading() && runMS < maxWait) { runMS = AZStd::chrono::duration_cast(AZStd::chrono::system_clock::now() - start); AZ_TracePrintf("TestCreateContainers", "Container root ready is %d and has %zu waiting dependencies after %ld ms\n", thisContainer->GetAsset()->IsReady(), thisContainer->GetNumWaitingDependencies(), runMS); AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); } if (thisContainer->IsReady()) { AZ_TracePrintf("TestCreateContainers", "Container for %s loaded\n", assetId.ToString().c_str()); } else { AZ_TracePrintf("TestCreateContainers", "Failed to load container for %s\n", assetId.ToString().c_str()); thisContainer->ListWaitingAssets(); } }, true); runJob->Start(); } AZ_CONSOLEFREEFUNC(TestSingleContainer, AZ::ConsoleFunctorFlags::Null, "Test the creation of a single container"); void ShowAsset(const AZ::StringSet& someStrings) { if (someStrings.empty()) { AZ_Warning("TestSingleContainer", false, "Need a valid id to show"); return; } auto uuidString = someStrings[0]; AZ::u32 subId{ 0 }; auto subidPos = uuidString.rfind(':'); if (subidPos != AZStd::string::npos) { auto subidstr = uuidString.substr(subidPos + 1); subId = atoi(subidstr.c_str()); uuidString = uuidString.substr(0, subidPos); } AZ::Data::AssetId assetId(AZ::Uuid::CreateStringPermissive(uuidString.c_str()), subId); AZ::Data::AssetInfo assetInfo; AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId); if (!assetInfo.m_assetId.IsValid()) { AZ_Warning("TestSingleContainer", false, "Couldn't get asset info for %s", assetId.ToString().c_str()); return; } AZ_TracePrintf("ShowAsset", "Asset %s : Type %s Size %zu RelativePath %s\n", assetId.ToString().c_str(), assetInfo.m_assetType.ToString().c_str(), assetInfo.m_sizeBytes, assetInfo.m_relativePath.c_str()); } AZ_CONSOLEFREEFUNC(ShowAsset, AZ::ConsoleFunctorFlags::Null, "Print info on a single asset");