293 lines
8.7 KiB
C++
293 lines
8.7 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
#include "OculusXRRuleProcessorSubsystem.h"
|
|
|
|
#include "LightComponentBase.h"
|
|
#include "OculusXRProjectSetupToolModule.h"
|
|
#include "OculusXRPSTEvents.h"
|
|
#include "OculusXRPSTSettings.h"
|
|
#include "OculusXRPSTUtils.h"
|
|
#include "OculusXRTelemetry.h"
|
|
#include "Developer/LauncherServices/Public/ILauncherServicesModule.h"
|
|
#include "Rules/OculusXRAnchorsRules.h"
|
|
#include "Rules/OculusXRCompatibilityRules.h"
|
|
#include "Rules/OculusXRMovementRules.h"
|
|
#include "Rules/OculusXRPassthroughRules.h"
|
|
#include "Rules/OculusXRRenderingRules.h"
|
|
#include "Rules/OculusXRPluginRules.h"
|
|
|
|
/**
|
|
* Initialize the subsystem. USubsystem override
|
|
*/
|
|
void UOculusXRRuleProcessorSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
|
|
PopulateDynamicLights();
|
|
|
|
// Register rules
|
|
RegisterRules(OculusXRRenderingRules::RenderingRules_Table);
|
|
RegisterRules(OculusXRPluginRules::PluginRules_Table);
|
|
RegisterRules(OculusXRCompatibilityRules::CompatibilityRules_Table);
|
|
RegisterRules(OculusXRPassthroughRules::PassthroughRules_Table);
|
|
RegisterRules(OculusXRMovementRules::MovementRules_Table);
|
|
RegisterRules(OculusXRAnchorsRules::AnchorRules_Table);
|
|
|
|
// Register on Launcher Callback
|
|
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<ILauncherServicesModule>(
|
|
"LauncherServices");
|
|
LauncherCallbackHandle = ProjectLauncherServicesModule.OnCreateLauncherDelegate.AddUObject(
|
|
this, &UOculusXRRuleProcessorSubsystem::OnLauncherCreated);
|
|
// Show errors after play in editor is over.
|
|
FEditorDelegates::PrePIEEnded.AddUObject(this, &UOculusXRRuleProcessorSubsystem::OnPIEEnded);
|
|
|
|
// Update if rules are ignored. Note: At time of the rules construction it is early to fetch settings
|
|
const auto& IgnoredRules = GetMutableDefault<UOculusXRPSTSettings>()->IgnoredRules;
|
|
for (const auto& Rule : Rules)
|
|
{
|
|
Rule->SetIgnoreRule(IgnoredRules.Contains(Rule->GetId()), false);
|
|
}
|
|
|
|
SendSummaryEvent();
|
|
}
|
|
|
|
/**
|
|
* De-initializes the subsystem. USubsystem override
|
|
*/
|
|
void UOculusXRRuleProcessorSubsystem::Deinitialize()
|
|
{
|
|
SendSummaryEvent();
|
|
Super::Deinitialize();
|
|
if (LauncherCallbackHandle.IsValid())
|
|
{
|
|
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<
|
|
ILauncherServicesModule>(
|
|
"LauncherServices");
|
|
ProjectLauncherServicesModule.OnCreateLauncherDelegate.Remove(LauncherCallbackHandle);
|
|
LauncherCallbackHandle.Reset();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a rule
|
|
*/
|
|
bool UOculusXRRuleProcessorSubsystem::RegisterRule(const SetupRulePtr& Rule)
|
|
{
|
|
if (Rule == nullptr)
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Error, TEXT("RegisterRule: Cannot register nullptr"));
|
|
return false;
|
|
}
|
|
bool bIsAlreadyRegistred = false;
|
|
Rules.Add(Rule, &bIsAlreadyRegistred);
|
|
if (bIsAlreadyRegistred)
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Warning, TEXT("RegisterRule: rule with id <%s> has already been registered"),
|
|
*(Rule->GetId().ToString()));
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogProjectSetupTool, Display, TEXT("RegisterRule: added rule with id <%s>"), *(Rule->GetId().ToString()));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unregister a rule
|
|
*/
|
|
bool UOculusXRRuleProcessorSubsystem::UnregisterRule(const SetupRulePtr& Rule)
|
|
{
|
|
if (Rule == nullptr)
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Error, TEXT("RegisterRule: Cannot deregister nullptr"));
|
|
return false;
|
|
}
|
|
const auto Id = Rule->GetId();
|
|
if (!Rules.Contains(Id))
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Warning, TEXT("UnregisterRule: rule with id <%s> has not been registered"),
|
|
*Id.ToString());
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogProjectSetupTool, Display, TEXT("UnregisterRule: removed rule with id <%s>"), *Id.ToString());
|
|
|
|
Rules.Remove(Id);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unregister all rules
|
|
*/
|
|
void UOculusXRRuleProcessorSubsystem::UnregisterAllRules()
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Display, TEXT("UnregisterRule: removed all rules"));
|
|
|
|
Rules.Empty();
|
|
}
|
|
|
|
/**
|
|
* Fetch all rules
|
|
*/
|
|
const TSet<SetupRulePtr, FSetupRuleKeyFunc>& UOculusXRRuleProcessorSubsystem::GetRules() const
|
|
{
|
|
return Rules;
|
|
}
|
|
|
|
/**
|
|
* Fetch rule with given `Id`
|
|
*/
|
|
SetupRulePtr UOculusXRRuleProcessorSubsystem::GetRule(const FName& Id) const
|
|
{
|
|
const auto Found = Rules.Find(Id);
|
|
if (Found == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return *Found;
|
|
}
|
|
|
|
bool UOculusXRRuleProcessorSubsystem::DynamicLightsExistInProject() const
|
|
{
|
|
return DynamicLights.Num() > 0;
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::SendSummaryEvent()
|
|
{
|
|
SendSummaryEvent(ESetupRulePlatform::MetaLink);
|
|
SendSummaryEvent(ESetupRulePlatform::MetaQuest_2);
|
|
SendSummaryEvent(ESetupRulePlatform::MetaQuest_3);
|
|
SendSummaryEvent(ESetupRulePlatform::MetaQuest_Pro);
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::SendSummaryEvent(ESetupRulePlatform Platform) const
|
|
{
|
|
const auto& Status = UnAppliedRulesStatus(Platform);
|
|
const char* Level = Status.PendingRequiredRulesCount > 0 ? "Critical" : "Recommended";
|
|
const char* Value = TCHAR_TO_ANSI(*FString::FromInt(Status.PendingRequiredRulesCount > 0 ? Status.PendingRequiredRulesCount : Status.PendingRecommendedRulesCount));
|
|
const char* Total = TCHAR_TO_ANSI(
|
|
*FString::FromInt(Status.PendingRequiredRulesCount + Status.PendingRecommendedRulesCount));
|
|
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolSummary> SummaryEvent;
|
|
const auto& CriticalAnnotated = SummaryEvent
|
|
.AddAnnotation(OculusXRTelemetry::Annotations::Level, Level)
|
|
.AddAnnotation(OculusXRTelemetry::Annotations::Value, Value)
|
|
.AddAnnotation(OculusXRTelemetry::Annotations::Count, Total)
|
|
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(Platform));
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::Refresh()
|
|
{
|
|
PopulateDynamicLights();
|
|
SendSummaryEvent();
|
|
}
|
|
|
|
UOculusXRRuleProcessorSubsystem::RuleStatus UOculusXRRuleProcessorSubsystem::UnAppliedRulesStatus(
|
|
ESetupRulePlatform Platform) const
|
|
{
|
|
RuleStatus Status{};
|
|
for (const auto& Rule : Rules)
|
|
{
|
|
if (Rule->IsApplied())
|
|
{
|
|
continue;
|
|
}
|
|
if (OculusXRPSTUtils::ShouldRuleBeSkipped(Rule, Platform, { ESetupRuleSeverity::Critical, ESetupRuleSeverity::Performance, ESetupRuleSeverity::Warning }))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Rule->GetSeverity() == ESetupRuleSeverity::Critical)
|
|
{
|
|
++Status.PendingRequiredRulesCount;
|
|
}
|
|
else
|
|
{
|
|
++Status.PendingRecommendedRulesCount;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::PopulateDynamicLights()
|
|
{
|
|
DynamicLights.Empty();
|
|
|
|
for (TObjectIterator<ULightComponentBase> LightItr; LightItr; ++LightItr)
|
|
{
|
|
const AActor* owner = LightItr->GetOwner();
|
|
if (owner != nullptr && (owner->IsRootComponentStationary() || owner->IsRootComponentMovable()) && !owner->IsHiddenEd() && LightItr->IsVisible() && owner->IsEditable() && owner->IsSelectable() && LightItr->GetWorld() == GEditor->GetEditorWorldContext().World())
|
|
{
|
|
DynamicLights.Add(LightItr->GetFullGroupName(false), TWeakObjectPtr<ULightComponentBase>(*LightItr));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::RegisterRules(const TArray<SetupRulePtr>& InRules)
|
|
{
|
|
for (const auto& Rule : InRules)
|
|
{
|
|
if (!RegisterRule(Rule))
|
|
{
|
|
UE_LOG(LogProjectSetupTool, Error, TEXT("Cannot register rule <%s>"), *Rule->GetId().ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::OnLauncherCreated(ILauncherRef Launcher)
|
|
{
|
|
// Add callback for when launcher worker is started
|
|
Launcher->FLauncherWorkerStartedDelegate.AddUObject(
|
|
this, &UOculusXRRuleProcessorSubsystem::OnLauncherWorkerStarted);
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker,
|
|
ILauncherProfileRef Profile)
|
|
{
|
|
if (!GetMutableDefault<UOculusXRPSTSettings>()->bStopBuildOnUnAppliedCriticalItems)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const TArray<FString> Platforms = Profile.Get().GetCookedPlatforms();
|
|
ESetupRulePlatform RulePlatforms = ESetupRulePlatform::None;
|
|
if (Platforms.Contains("Android_ASTC"))
|
|
{
|
|
RulePlatforms |= MetaQuest_All;
|
|
}
|
|
|
|
if (Platforms.Contains("Windows"))
|
|
{
|
|
RulePlatforms |= ESetupRulePlatform::MetaLink;
|
|
}
|
|
|
|
const auto& UnAppliedRules = UnAppliedRulesForPlatform(RulePlatforms, { ESetupRuleSeverity::Critical });
|
|
|
|
OculusXRPSTUtils::LogErrorForUnAppliedRules(UnAppliedRules);
|
|
if (!UnAppliedRules.IsEmpty())
|
|
{
|
|
LauncherWorker->Cancel();
|
|
}
|
|
}
|
|
|
|
void UOculusXRRuleProcessorSubsystem::OnPIEEnded(bool bIsSimulating)
|
|
{
|
|
const auto& UnAppliedRules = UnAppliedRulesForPlatform(ESetupRulePlatform::MetaLink, { ESetupRuleSeverity::Critical });
|
|
|
|
OculusXRPSTUtils::LogErrorForUnAppliedRules(UnAppliedRules);
|
|
}
|
|
|
|
TArray<SetupRulePtr> UOculusXRRuleProcessorSubsystem::UnAppliedRulesForPlatform(ESetupRulePlatform Platform, const TSet<ESetupRuleSeverity>& Severities) const
|
|
{
|
|
TArray<SetupRulePtr> UnAppliedRules = {};
|
|
for (const auto Rule : Rules)
|
|
{
|
|
if (!OculusXRPSTUtils::ShouldRuleBeSkipped(Rule, Platform, Severities) && !Rule->IsApplied())
|
|
{
|
|
UnAppliedRules.Add(Rule);
|
|
}
|
|
}
|
|
|
|
return UnAppliedRules;
|
|
}
|