Files
2025-05-18 13:04:45 +08:00

173 lines
4.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MixManager.h"
#include "MixInterface.h"
#include "MixSettings.h"
#include "Job/JobBatch.h"
#include "TextureGraphEngine.h"
#include "Job/Scheduler.h"
DEFINE_LOG_CATEGORY(LogMixManager);
//////////////////////////////////////////////////////////////////////////
bool MixInterface_ComparePriority::operator()(const MixInvalidateInfo& LHS, const MixInvalidateInfo& RHS)
{
if (!LHS.MixObj || !RHS.MixObj)
return false;
/// This is the most immediate priority
if (LHS.Priority != RHS.Priority)
return LHS.Priority > RHS.Priority;
/// Fallback to mix priority if immediate priority isn't satisfied
return !LHS.MixObj->IsHigherPriorityThan(RHS.MixObj);
}
MixManager::MixManager()
{
}
MixManager::~MixManager()
{
Queue.clear();
}
void MixManager::FlushMix(UMixInterface* MixObj)
{
check(IsInGameThread());
MixesToFlush.push_back(MixObj);
}
void MixManager::Update(float Delta)
{
/// Mix manager has been suspended for the time being.
/// Do not update any mixes
if (bIsSuspended)
return;
std::vector<MixInvalidateInfo> MixInfos = Queue.to_vector_and_clear();
if (!MixInfos.size())
{
// if no invalidations queued, we can clear out MixesToFlush as well,
// this avoids a bug where the next time this same mix is opened it is immediately flushed and doesn't trigger an update
MixesToFlush.clear();
return;
}
if (!MixesToFlush.empty())
{
for (UMixInterface* MixToFlush : MixesToFlush)
{
for (auto Iter = MixInfos.begin(); Iter != MixInfos.end();)
{
MixInvalidateInfo& MixInfo = *Iter;
UMixInterface* MixObj = MixInfo.MixObj;
if (MixObj == MixToFlush)
Iter = MixInfos.erase(Iter);
else
Iter++;
}
}
MixesToFlush.clear();
}
std::vector<MixInvalidateInfo> BucketedMixInfos;
// First merge invalidation touching on the same mix
// High priority mixInfo are last in list; this is by design of the queue
// We are reversing that order as we produce the bucketed list
for (auto Iter = MixInfos.rbegin(); Iter != MixInfos.rend(); ++Iter)
{
MixInvalidateInfo& MixInfo = *Iter;
UMixInterface* MixObj = MixInfo.MixObj;
auto RIter = std::find_if(BucketedMixInfos.begin(), BucketedMixInfos.end(),
[MixObj](const MixInvalidateInfo& Info)
{
return MixObj == Info.MixObj;
});
if (RIter == BucketedMixInfos.end()) // New mix encoutered
{
BucketedMixInfos.emplace_back(MixInfo); // Push the new mixInfo at the index
}
else
{
int32 Index = RIter - BucketedMixInfos.begin();
// Merge the new mixInfo invalidation over the one already present
BucketedMixInfos[Index].InvalidationDetails.Merge(MixInfo.InvalidationDetails);
// The first priority bucketed should be the highest, no need to worry about priority anymore
}
}
// Second, execute the bucketedMixInfos commands
for (auto& MixInfo : BucketedMixInfos)
{
UE_LOG(LogMixManager, Log, TEXT("Rendering mix: %s [Invalidation FrameId: %llu, Update FrameId: %llu, Discard: %s"),
*MixInfo.MixObj->GetName(), MixInfo.MixObj->GetInvalidationFrameId(), MixInfo.MixObj->GetUpdateFrameId(),
(MixInfo.InvalidationDetails.IsDiscard() ? TEXT("Yes") : TEXT("No")));
UMixSettings* MixSettings = MixInfo.MixObj->GetSettings();
FInvalidationDetails& Details = MixInfo.InvalidationDetails;
if (!Details.Mix.IsValid())
Details.Mix = MixInfo.MixObj;
JobBatchPtr Batch = JobBatch::Create(Details);
MixUpdateCyclePtr Cycle = Batch->GetCycle();
// Clear the existing errors for the mix before updating.
// Update will generate new errors.
TextureGraphEngine::GetErrorReporter(MixInfo.MixObj)->Clear();
MixInfo.MixObj->Update(Cycle);
TextureGraphEngine::GetScheduler()->AddBatch(Batch);
}
}
void MixManager::Suspend()
{
bIsSuspended = true;
}
void MixManager::Resume()
{
bIsSuspended = false;
}
void MixManager::Exit()
{
UE_LOG(LogMixManager, Log, TEXT("Begin MixManager::Suspend"));
FRenderCommandFence SuspendFence;
SuspendFence.BeginFence();
SuspendFence.Wait();
Suspend();
UE_LOG(LogMixManager, Log, TEXT("End MixManager::Suspend"));
}
void MixManager::InvalidateMix(UMixInterface* MixObj, const FInvalidationDetails &Details, int32 Priority /* = (int32)E_Priority::kNormal */)
{
check(IsInGameThread());
/// Mix manager has been suspended for the time being.
/// We dont want to add it in invalidation queue.
if(bIsSuspended)
return;
Queue.add(MixInvalidateInfo
{
MixObj,
Details,
Priority
});
}