Browse Source

Made model load Async (thanks claude)

master
Tillman Staffen 1 month ago
parent
commit
33e4088802
  1. 168
      Unreal/Plugins/RuntimeMetaHumanLipSync_5.6/Source/RuntimeMetaHumanLipSync/Private/RealisticMetaHumanLipSyncGenerator.cpp

168
Unreal/Plugins/RuntimeMetaHumanLipSync_5.6/Source/RuntimeMetaHumanLipSync/Private/RealisticMetaHumanLipSyncGenerator.cpp

@ -121,14 +121,8 @@ URealisticMetaHumanLipSyncGenerator* URealisticMetaHumanLipSyncGenerator::Create
{ {
URealisticMetaHumanLipSyncGenerator* Generator = NewObject<URealisticMetaHumanLipSyncGenerator>(); URealisticMetaHumanLipSyncGenerator* Generator = NewObject<URealisticMetaHumanLipSyncGenerator>();
#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID
if (!Generator->OrtEnvironment) Generator->GeneratorConfig = Config;
{ Generator->InitializeModelAsync();
Generator->OrtEnvironment = MakeShared<Ort::Env>(ORT_LOGGING_LEVEL_WARNING, "MetaHumanLipSyncModel");
Generator->GeneratorConfig = Config;
// Start async model loading
Generator->InitializeModelAsync();
}
#endif #endif
return Generator; return Generator;
} }
@ -137,18 +131,10 @@ URealisticMetaHumanLipSyncGenerator* URealisticMetaHumanLipSyncGenerator::Create
{ {
URealisticMetaHumanLipSyncGenerator* Generator = NewObject<URealisticMetaHumanLipSyncGenerator>(); URealisticMetaHumanLipSyncGenerator* Generator = NewObject<URealisticMetaHumanLipSyncGenerator>();
#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID #if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID
if (!Generator->OrtEnvironment) Generator->GeneratorConfigWithMood = Config;
{ Generator->CurrentLookaheadMs = Config.LookaheadMs;
Generator->OrtEnvironment = MakeShared<Ort::Env>(ORT_LOGGING_LEVEL_WARNING, "MetaHumanLipSyncMoodModel"); Generator->CurrentOutputType = Config.OutputType;
Generator->GeneratorConfigWithMood = Config; Generator->InitializeModelAsync();
// Apply configuration settings
Generator->CurrentLookaheadMs = Config.LookaheadMs;
Generator->CurrentOutputType = Config.OutputType;
// Start async model loading
Generator->InitializeModelAsync();
}
#endif #endif
return Generator; return Generator;
} }
@ -1167,29 +1153,7 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
ModelLoadState = ELipSyncModelLoadState::Loading; ModelLoadState = ELipSyncModelLoadState::Loading;
CurrentModelKey = GetModelCacheKey(); CurrentModelKey = GetModelCacheKey();
// Check if model is already cached first // Get the asset path for async loading (cheap, game-thread safe)
if (ModelCache.IsValid())
{
FScopeLock Lock(&ModelCache->CacheMutex);
if (TSharedPtr<FLipSyncModelCache::FCachedModelData>* ExistingModel = ModelCache->CachedModels.Find(CurrentModelKey))
{
if (ExistingModel->IsValid() && (*ExistingModel)->bIsValid)
{
(*ExistingModel)->ReferenceCount++;
bool bInitSuccess = InitializeOnnxSessionFromCache(*ExistingModel);
ModelLoadState = bInitSuccess ? ELipSyncModelLoadState::Loaded : ELipSyncModelLoadState::Failed;
OnModelLoaded.Broadcast(bInitSuccess);
if (OnComplete)
{
OnComplete(bInitSuccess);
}
return;
}
}
}
// Get the asset path for async loading
const FRuntimeMetaHumanLipSyncModule* RuntimeMetaHumanLipSyncModulePtr = static_cast<FRuntimeMetaHumanLipSyncModule*>(FModuleManager::Get().GetModule("RuntimeMetaHumanLipSync")); const FRuntimeMetaHumanLipSyncModule* RuntimeMetaHumanLipSyncModulePtr = static_cast<FRuntimeMetaHumanLipSyncModule*>(FModuleManager::Get().GetModule("RuntimeMetaHumanLipSync"));
if (!RuntimeMetaHumanLipSyncModulePtr) if (!RuntimeMetaHumanLipSyncModulePtr)
{ {
@ -1218,8 +1182,61 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
return; return;
} }
// Use StreamableManager for async asset loading // Fix B: Check cache hit — grab a reference under the lock, then release the lock before going async
TSharedPtr<FLipSyncModelCache::FCachedModelData> CachedHit;
if (ModelCache.IsValid())
{
FScopeLock Lock(&ModelCache->CacheMutex);
if (TSharedPtr<FLipSyncModelCache::FCachedModelData>* ExistingModel = ModelCache->CachedModels.Find(CurrentModelKey))
{
if (ExistingModel->IsValid() && (*ExistingModel)->bIsValid)
{
(*ExistingModel)->ReferenceCount++;
CachedHit = *ExistingModel;
}
}
}
TWeakObjectPtr<URealisticMetaHumanLipSyncGenerator> WeakThis(this); TWeakObjectPtr<URealisticMetaHumanLipSyncGenerator> WeakThis(this);
if (CachedHit.IsValid())
{
// Fix B: Cache hit — create Ort::Env + Ort::Session on a background thread, not the game thread
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [WeakThis, CachedHit, OnComplete]()
{
URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get();
if (!Generator)
{
return;
}
#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID
const bool bIsMood = Generator->GeneratorConfigWithMood.IsSet();
Generator->OrtEnvironment = MakeShared<Ort::Env>(ORT_LOGGING_LEVEL_WARNING,
bIsMood ? "MetaHumanLipSyncMoodModel" : "MetaHumanLipSyncModel");
#endif
bool bInitSuccess = Generator->InitializeOnnxSessionFromCache(CachedHit);
// Only marshal the lightweight state update back to the game thread
AsyncTask(ENamedThreads::GameThread, [WeakThis, bInitSuccess, OnComplete]()
{
URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get();
if (Generator)
{
Generator->ModelLoadState = bInitSuccess ? ELipSyncModelLoadState::Loaded : ELipSyncModelLoadState::Failed;
if (bInitSuccess)
{
UE_LOG(LogRuntimeMetaHumanLipSync, Log, TEXT("Successfully loaded lip sync model from cache asynchronously"));
}
Generator->OnModelLoaded.Broadcast(bInitSuccess);
if (OnComplete) OnComplete(bInitSuccess);
}
});
});
return;
}
// Fix A + C: Cache miss — use StreamableManager for async asset loading, then create ORT objects on background thread
UAssetManager::GetStreamableManager().RequestAsyncLoad( UAssetManager::GetStreamableManager().RequestAsyncLoad(
AssetPath, AssetPath,
[WeakThis, OnComplete]() [WeakThis, OnComplete]()
@ -1230,7 +1247,7 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
return; return;
} }
// Asset is now loaded, extract model data on background thread // Asset is now loaded — extract model data and create ORT session entirely on background thread
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [WeakThis, OnComplete]() AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [WeakThis, OnComplete]()
{ {
URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get(); URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get();
@ -1239,11 +1256,11 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
return; return;
} }
bool bSuccess = false; TSharedPtr<FLipSyncModelCache::FCachedModelData> CachedModel;
if (Generator->ModelCache.IsValid()) if (Generator->ModelCache.IsValid())
{ {
auto CachedModel = Generator->ModelCache->GetOrLoadModel(Generator->CurrentModelKey, CachedModel = Generator->ModelCache->GetOrLoadModel(Generator->CurrentModelKey,
[WeakThis](TSharedPtr<FLipSyncModelCache::FCachedModelData>& ModelData) -> bool [WeakThis](TSharedPtr<FLipSyncModelCache::FCachedModelData>& ModelData) -> bool
{ {
URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get(); URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get();
@ -1253,35 +1270,36 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
} }
return Generator->LoadModelDataFromLoadedAsset(ModelData); return Generator->LoadModelDataFromLoadedAsset(ModelData);
}); });
}
if (CachedModel.IsValid() && CachedModel->bIsValid)
if (CachedModel.IsValid() && CachedModel->bIsValid)
{
// Fix A + C: Construct Ort::Env and Ort::Session here on the background thread
#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX || PLATFORM_IOS || PLATFORM_ANDROID
const bool bIsMood = Generator->GeneratorConfigWithMood.IsSet();
Generator->OrtEnvironment = MakeShared<Ort::Env>(ORT_LOGGING_LEVEL_WARNING,
bIsMood ? "MetaHumanLipSyncMoodModel" : "MetaHumanLipSyncModel");
#endif
bool bInitSuccess = Generator->InitializeOnnxSessionFromCache(CachedModel);
// Only marshal the lightweight state update back to the game thread
AsyncTask(ENamedThreads::GameThread, [WeakThis, bInitSuccess, OnComplete]()
{ {
// Initialize ONNX session from cached data back on game thread URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get();
AsyncTask(ENamedThreads::GameThread, [WeakThis, CachedModel, OnComplete]() if (Generator)
{ {
URealisticMetaHumanLipSyncGenerator* Generator = WeakThis.Get(); Generator->ModelLoadState = bInitSuccess ? ELipSyncModelLoadState::Loaded : ELipSyncModelLoadState::Failed;
if (Generator) if (bInitSuccess)
{ {
bool bInitSuccess = Generator->InitializeOnnxSessionFromCache(CachedModel); UE_LOG(LogRuntimeMetaHumanLipSync, Log, TEXT("Successfully loaded lip sync model asynchronously"));
Generator->ModelLoadState = bInitSuccess ? ELipSyncModelLoadState::Loaded : ELipSyncModelLoadState::Failed;
if (bInitSuccess)
{
UE_LOG(LogRuntimeMetaHumanLipSync, Log, TEXT("Successfully loaded lip sync model asynchronously"));
}
Generator->OnModelLoaded.Broadcast(bInitSuccess);
if (OnComplete)
{
OnComplete(bInitSuccess);
}
} }
}); Generator->OnModelLoaded.Broadcast(bInitSuccess);
return; if (OnComplete) OnComplete(bInitSuccess);
} }
});
return;
} }
// Failed to load // Failed to load
AsyncTask(ENamedThreads::GameThread, [WeakThis, OnComplete]() AsyncTask(ENamedThreads::GameThread, [WeakThis, OnComplete]()
{ {
@ -1290,11 +1308,7 @@ void URealisticMetaHumanLipSyncGenerator::InitializeModelAsync(TFunction<void(bo
{ {
Generator->ModelLoadState = ELipSyncModelLoadState::Failed; Generator->ModelLoadState = ELipSyncModelLoadState::Failed;
Generator->OnModelLoaded.Broadcast(false); Generator->OnModelLoaded.Broadcast(false);
if (OnComplete) OnComplete(false);
if (OnComplete)
{
OnComplete(false);
}
} }
}); });
}); });

Loading…
Cancel
Save