VR2024_WizardVR/TheProject/VRWizardProject/Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDModule.cpp

510 lines
14 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "Containers/StringConv.h"
#include "Misc/EngineVersion.h"
#include "Misc/Paths.h"
#if PLATFORM_ANDROID
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#include "Interfaces/IPluginManager.h"
#include "ShaderCore.h"
#include "OculusXRTelemetry.h"
#if PLATFORM_WINDOWS
#include "OculusXRSimulator.h"
#include "OculusXRSyntheticEnvironmentServer.h"
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
namespace
{
void __cdecl OvrpLogCallback2(ovrpLogLevel InLevel, const char* Message, int Length)
{
ELogVerbosity::Type OutLevel;
switch (InLevel)
{
case ovrpLogLevel_Debug:
OutLevel = ELogVerbosity::Log;
break;
case ovrpLogLevel_Info:
OutLevel = ELogVerbosity::Display;
break;
case ovrpLogLevel_Error:
OutLevel = ELogVerbosity::Error;
break;
default:
OutLevel = ELogVerbosity::NoLogging;
}
const FString MessageStr(Length, Message);
GLog->CategorizedLogf(TEXT("LogOVRPlugin"), OutLevel, TEXT("%s"), *MessageStr);
}
} // namespace
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
OculusPluginWrapper FOculusXRHMDModule::PluginWrapper{};
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusPluginWrapper& FOculusXRHMDModule::GetPluginWrapper()
{
return PluginWrapper;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
FOculusXRHMDModule::FOculusXRHMDModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
bPreInit = false;
bPreInitCalled = false;
OVRPluginHandle = nullptr;
GraphicsAdapterLuid = 0;
#endif
}
void FOculusXRHMDModule::StartupModule()
{
IHeadMountedDisplayModule::StartupModule();
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/Plugin/OculusXR"), PluginShaderDir);
}
void FOculusXRHMDModule::ShutdownModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (PluginWrapper.IsInitialized())
{
OculusXRTelemetry::FTelemetryBackend::OnEditorShutdown();
PluginWrapper.Shutdown2();
OculusPluginWrapper::DestroyOculusPluginWrapper(&PluginWrapper);
}
if (OVRPluginHandle)
{
FPlatformProcess::FreeDllHandle(OVRPluginHandle);
OVRPluginHandle = nullptr;
}
#endif
}
#if PLATFORM_ANDROID
extern bool AndroidThunkCpp_IsOculusMobileApplication();
#endif
FString FOculusXRHMDModule::GetModuleKeyName() const
{
return FString(TEXT("OculusXRHMD"));
}
void FOculusXRHMDModule::GetModuleAliases(TArray<FString>& AliasesOut) const
{
// Pre-OculusXR rename (5.0.3 v44)
AliasesOut.Add(TEXT("OculusHMD"));
}
bool FOculusXRHMDModule::PreInit()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (!bPreInitCalled)
{
bPreInit = false;
#if PLATFORM_ANDROID
bPreInitCalled = true;
if (!AndroidThunkCpp_IsOculusMobileApplication())
{
UE_LOG(LogHMD, Log, TEXT("App is not packaged for Oculus Mobile"));
return false;
}
#endif
// Init module if app can render
if (FApp::CanEverRender())
{
// Load OVRPlugin
OVRPluginHandle = GetOVRPluginHandle();
if (!OVRPluginHandle)
{
UE_LOG(LogHMD, Log, TEXT("Failed loading OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
return false;
}
if (!OculusPluginWrapper::InitializeOculusPluginWrapper(&PluginWrapper))
{
UE_LOG(LogHMD, Log, TEXT("Failed InitializeOculusPluginWrapper"));
return false;
}
// Initialize OVRPlugin
ovrpRenderAPIType PreinitApiType = ovrpRenderAPI_None;
#if PLATFORM_ANDROID
void* Activity = (void*)FAndroidApplication::GetGameActivityThis();
PreinitApiType = ovrpRenderAPI_Vulkan;
#else
void* Activity = nullptr;
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
PluginWrapper.SetLogCallback2(OvrpLogCallback2);
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
if (OVRP_FAILURE(PluginWrapper.PreInitialize5(Activity, PreinitApiType, ovrpPreinitializeFlags::ovrpPreinitializeFlag_None)))
{
UE_LOG(LogHMD, Log, TEXT("Failed initializing OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
#if WITH_EDITOR && PLATFORM_WINDOWS
// In the editor, we want to allow the headset to connect after the editor has booted.
// To do this, we must have PreInit() return true, to prevent the HMD module from being unloaded.
return GIsEditor;
#else
return false;
#endif
}
#if PLATFORM_WINDOWS
bPreInitCalled = true;
const LUID* DisplayAdapterId;
if (OVRP_SUCCESS(PluginWrapper.GetDisplayAdapterId2((const void**)&DisplayAdapterId)) && DisplayAdapterId)
{
SetGraphicsAdapterLuid(*(const uint64*)DisplayAdapterId);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD display adapter"));
}
const WCHAR* AudioInDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioInDeviceId2((const void**)&AudioInDeviceId)) && AudioInDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio input device"));
}
const WCHAR* AudioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&AudioOutDeviceId)) && AudioOutDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio output device"));
}
#endif
float ModulePriority;
if (!GConfig->GetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni))
{
// if user doesn't set priority set it for them to allow this hmd to be used if enabled
ModulePriority = 45.0f;
GConfig->SetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni);
}
UE_LOG(LogHMD, Log, TEXT("FOculusXRHMDModule PreInit successfully"));
bPreInit = true;
}
}
return bPreInit;
#else
return false;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool FOculusXRHMDModule::IsHMDConnected()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
if (FApp::CanEverRender() && HMDSettings->XrApi != EOculusXRXrApi::NativeOpenXR)
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
uint64 FOculusXRHMDModule::GetGraphicsAdapterLuid()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
if (!GraphicsAdapterLuid)
{
int GraphicsAdapter;
if (GConfig->GetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), GraphicsAdapter, GEngineIni) && GraphicsAdapter >= 0)
{
TRefCountPtr<IDXGIFactory> DXGIFactory;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())) && SUCCEEDED(DXGIFactory->EnumAdapters(GraphicsAdapter, DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
FMemory::Memcpy(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid));
}
}
}
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return GraphicsAdapterLuid;
#else
return 0;
#endif
}
FString FOculusXRHMDModule::GetAudioInputDevice()
{
FString AudioInputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInputDevice, GEngineIni);
#endif
return AudioInputDevice;
}
FString FOculusXRHMDModule::GetAudioOutputDevice()
{
FString AudioOutputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#if PLATFORM_WINDOWS
if (bPreInit)
{
if (FApp::CanEverRender())
{
const WCHAR* audioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&audioOutDeviceId)) && audioOutDeviceId)
{
AudioOutputDevice = audioOutDeviceId;
}
}
}
#else
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutputDevice, GEngineIni);
#endif
#endif
return AudioOutputDevice;
}
TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> FOculusXRHMDModule::CreateTrackingSystem()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit || (GIsEditor && PLATFORM_WINDOWS))
{
//If -HMDSimulator is used as the command option to launch UE, use simulator runtime instead of the physical HMD runtime (like PC-Link).
if (FParse::Param(FCommandLine::Get(), TEXT("HMDSimulator")) && GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MetaXRJsonPath.FilePath.Len())
{
if (!IsSimulatorActivated())
{
ToggleOpenXRRuntime();
}
}
OculusXRHMD::FOculusXRHMDPtr OculusXRHMD = FSceneViewExtensions::NewExtension<OculusXRHMD::FOculusXRHMD>();
if (OculusXRHMD->Startup())
{
HeadMountedDisplay = OculusXRHMD;
return OculusXRHMD;
}
}
HeadMountedDisplay = nullptr;
#endif
return nullptr;
}
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> FOculusXRHMDModule::GetVulkanExtensions()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit)
{
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FVulkanExtensions);
}
}
#if WITH_EDITOR && PLATFORM_WINDOWS
else if (GIsEditor)
{
// OpenXR has no ability to query for possible vulkan extensions without connecting a HMD.
// This is a problem, because we need to create our VkInstance and VkDevice to render in 2D and there's no HMD.
// For now, as a workaround, we hardcode the extensions that Oculus's OpenXR implementation needs.
// Eventually, one of three things has to happen for a proper fix:
//
// 1. OculusXRHMD (or, better, OVRPlugin) maintains a separate VkInstance that has the right extensions,
// and uses the vk_external extensions to transfer data between them when needed.
// 2. OpenXR changes to allow querying instance and device extensions without an active HMD.
// It may still require a physical device handle to list device extensions.
// 3. Oculus's Link implementation for OpenXR changes to allow an XrSystemId to be created before a headset
// is connected (possibly as an opt-in OpenXR extension for backwards compatibility).
//
// (2) or (3) are preferable, but if OpenXR is held constant we will have to do (1).
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FEditorVulkanExtensions);
}
}
#endif
return VulkanExtensions;
#endif
return nullptr;
}
FString FOculusXRHMDModule::GetDeviceSystemName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpSystemHeadset SystemHeadset;
if (PluginWrapper.IsInitialized() && OVRP_SUCCESS(PluginWrapper.GetSystemHeadsetType2(&SystemHeadset)))
{
switch (SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return FString("Oculus Quest");
case ovrpSystemHeadset_Oculus_Quest_2:
default:
return FString("Oculus Quest2");
#ifdef WITH_OCULUS_BRANCH
case ovrpSystemHeadset_Meta_Quest_Pro:
return FString("Meta Quest Pro");
case ovrpSystemHeadset_Meta_Quest_3:
return FString("Meta Quest 3");
#endif // WITH_OCULUS_BRANCH
}
}
return FString();
#else
return FString();
#endif
}
bool FOculusXRHMDModule::IsStandaloneStereoOnlyDevice()
{
#if PLATFORM_ANDROID
return FAndroidMisc::GetDeviceMake() == FString("Oculus");
#else
return false;
#endif
}
bool FOculusXRHMDModule::IsSimulatorActivated()
{
#if PLATFORM_WINDOWS
return FMetaXRSimulator::IsSimulatorActivated();
#else
return false;
#endif
}
void FOculusXRHMDModule::ToggleOpenXRRuntime()
{
#if PLATFORM_WINDOWS
FMetaXRSimulator::ToggleOpenXRRuntime();
#endif
}
void FOculusXRHMDModule::LaunchEnvironment(FString EnvironmentName)
{
#if PLATFORM_WINDOWS
FMetaXRSES::LaunchEnvironment(EnvironmentName);
#endif
}
void FOculusXRHMDModule::StopServer()
{
#if PLATFORM_WINDOWS
FMetaXRSES::StopServer();
#endif
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* FOculusXRHMDModule::GetOVRPluginHandle()
{
void* OVRPluginHandle = nullptr;
#if PLATFORM_WINDOWS
FString XrApi;
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD") || !GConfig->GetString(TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings"), TEXT("XrApi"), XrApi, GEngineIni) || XrApi.Equals(FString("OVRPluginOpenXR")))
{
FString BinariesPath = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/Win64"));
FPlatformProcess::PushDllDirectory(*BinariesPath);
OVRPluginHandle = FPlatformProcess::GetDllHandle(*(BinariesPath / "OpenXR/OVRPlugin.dll"));
FPlatformProcess::PopDllDirectory(*BinariesPath);
}
#elif PLATFORM_ANDROID
OVRPluginHandle = FPlatformProcess::GetDllHandle(TEXT("libOVRPlugin.so"));
#endif // PLATFORM_ANDROID
return OVRPluginHandle;
}
bool FOculusXRHMDModule::PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const
{
OculusXRHMD::CheckInGameThread();
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(HeadMountedDisplay.Pin().Get());
if (OculusXRHMD)
{
ovrpPosef InPose;
InPose.Orientation = OculusXRHMD::ToOvrpQuatf(InOrientation);
InPose.Position = OculusXRHMD::ToOvrpVector3f(InPosition);
OculusXRHMD::FPose OutPose;
if (OculusXRHMD->ConvertPose(InPose, OutPose))
{
OutOrientation = OutPose.Orientation;
OutPosition = OutPose.Position;
return true;
}
}
return false;
}
void FOculusXRHMDModule::SetGraphicsAdapterLuid(uint64 InLuid)
{
GraphicsAdapterLuid = InLuid;
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
TRefCountPtr<IDXGIFactory> DXGIFactory;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())))
{
for (int32 adapterIndex = 0;; adapterIndex++)
{
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (FAILED(DXGIFactory->EnumAdapters(adapterIndex, DXGIAdapter.GetInitReference())) || FAILED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
break;
}
if (!FMemory::Memcmp(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid)))
{
// Remember this adapterIndex so we use the right adapter, even when we startup without HMD connected
GConfig->SetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), adapterIndex, GEngineIni);
break;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRHMDModule, OculusXRHMD)