387 lines
14 KiB
C++
387 lines
14 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
#include "OculusXRSpaceWarp.h"
|
|
#include "IOpenXRHMD.h"
|
|
#include "IOpenXRHMDModule.h"
|
|
#include "IXRTrackingSystem.h"
|
|
#include "OculusXROpenXRUtilities.h"
|
|
#include "OpenXRCore.h"
|
|
#include "OpenXRHMD_Swapchain.h"
|
|
#include "StereoRenderUtils.h"
|
|
|
|
#if defined(WITH_OCULUS_BRANCH)
|
|
|
|
DEFINE_LOG_CATEGORY(LogOculusSpaceWarpExtensionPlugin);
|
|
|
|
static const int64 VELOCITY_SWAPCHAIN_WAIT_TIMEOUT = 100000000ll; // 100ms in nanoseconds.
|
|
|
|
namespace OculusXR
|
|
{
|
|
FSpaceWarpExtensionPlugin::FSpaceWarpExtensionPlugin()
|
|
{
|
|
}
|
|
|
|
bool FSpaceWarpExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
UE_LOG(LogOculusSpaceWarpExtensionPlugin, Warning, TEXT("FSpaceWarpExtensionPlugin::GetRequiredExtensions"));
|
|
|
|
static const auto CVarSupportMobileSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.SupportMobileSpaceWarp"));
|
|
if (CVarSupportMobileSpaceWarp && (CVarSupportMobileSpaceWarp->GetValueOnAnyThread() != 0))
|
|
{
|
|
OutExtensions.Add(XR_FB_SPACE_WARP_EXTENSION_NAME);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void* FSpaceWarpExtensionPlugin::OnEnumerateViewConfigurationViews(XrInstance InInstance, XrSystemId InSystem, XrViewConfigurationType InViewConfigurationType, uint32_t InViewIndex, void* InNext)
|
|
{
|
|
SelectedViewConfigurationType = InViewConfigurationType;
|
|
return InNext;
|
|
}
|
|
|
|
const void* FSpaceWarpExtensionPlugin::OnCreateInstance(IOpenXRHMDModule* InModule, const void* InNext)
|
|
{
|
|
static const auto CVarSupportMobileSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.SupportMobileSpaceWarp"));
|
|
if (InModule && CVarSupportMobileSpaceWarp && (CVarSupportMobileSpaceWarp->GetValueOnAnyThread() != 0))
|
|
{
|
|
bSpaceWarpExtensionEnabled = InModule->IsExtensionEnabled(XR_FB_SPACE_WARP_EXTENSION_NAME);
|
|
}
|
|
|
|
return InNext;
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
|
{
|
|
if (!bSpaceWarpExtensionEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
XrInstanceProperties InstanceProperties = { XR_TYPE_INSTANCE_PROPERTIES, nullptr };
|
|
XR_ENSURE(xrGetInstanceProperties(InInstance, &InstanceProperties));
|
|
InstanceProperties.runtimeName[XR_MAX_RUNTIME_NAME_SIZE - 1] = 0; // Ensure the name is null terminated.
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::PostCreateSession(XrSession InSession)
|
|
{
|
|
if (!bSpaceWarpExtensionEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
XrSystemProperties SystemProperties;
|
|
SystemProperties = XrSystemProperties{ XR_TYPE_SYSTEM_PROPERTIES, &SpaceWarpSystemProperties };
|
|
XR_ENSURE(xrGetSystemProperties(IOpenXRHMDModule::Get().GetInstance(), IOpenXRHMDModule::Get().GetSystemId(), &SystemProperties));
|
|
|
|
SpaceWarpViewExtension = FSceneViewExtensions::NewExtension<FSpaceWarpViewExtension>(this);
|
|
|
|
UE::StereoRenderUtils::FStereoShaderAspects Aspects(GMaxRHIShaderPlatform);
|
|
bIsMobileMultiViewEnabled = Aspects.IsMobileMultiViewEnabled();
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::OnBeginRendering_RenderThread(XrSession InSession)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (!PipelinedVelocityState_RenderThread.bEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnBeginRendering_RenderThread, FColor::Red);
|
|
|
|
const FXRSwapChainPtr& VelocitySwapchain = PipelinedVelocityState_RenderThread.VelocitySwapchain;
|
|
const FXRSwapChainPtr& VelocityDepthSwapchain = PipelinedVelocityState_RenderThread.VelocityDepthSwapchain;
|
|
if (VelocitySwapchain)
|
|
{
|
|
VelocitySwapchain->IncrementSwapChainIndex_RHIThread();
|
|
if (VelocityDepthSwapchain)
|
|
{
|
|
VelocityDepthSwapchain->IncrementSwapChainIndex_RHIThread();
|
|
}
|
|
}
|
|
}
|
|
|
|
const void* FSpaceWarpExtensionPlugin::OnBeginProjectionView(XrSession InSession, int32 InLayerIndex, int32 InViewIndex, const void* InNext)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
IXRTrackingSystem* XRTrackingSystem = GetOpenXRTrackingSystem();
|
|
if (!XRTrackingSystem || !PipelinedVelocityState_RenderThread.bEnabled)
|
|
{
|
|
return InNext;
|
|
}
|
|
|
|
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnBeginProjectionView, FColor::Red);
|
|
|
|
TSharedPtr<XrCompositionLayerSpaceWarpInfoFB> SpaceWarpLayerInfo = MakeShared<XrCompositionLayerSpaceWarpInfoFB>();
|
|
|
|
FTransform TrackingToWorld = XRTrackingSystem->GetTrackingToWorldTransform();
|
|
FTransform TrackingSpaceDeltaPose = TrackingToWorld * LastTrackingToWorld.Inverse();
|
|
LastTrackingToWorld = TrackingToWorld;
|
|
FTransform BaseTransform = FTransform(XRTrackingSystem->GetBaseOrientation(), XRTrackingSystem->GetBasePosition());
|
|
TrackingSpaceDeltaPose = BaseTransform.Inverse() * TrackingSpaceDeltaPose * BaseTransform;
|
|
|
|
SpaceWarpLayerInfo->type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB;
|
|
SpaceWarpLayerInfo->next = InNext;
|
|
SpaceWarpLayerInfo->layerFlags = 0;
|
|
SpaceWarpLayerInfo->appSpaceDeltaPose = ToXrPose(TrackingSpaceDeltaPose);
|
|
SpaceWarpLayerInfo->farZ = GNearClippingPlane / 100.f;
|
|
SpaceWarpLayerInfo->nearZ = INFINITY;
|
|
SpaceWarpLayerInfo->minDepth = 0.0f;
|
|
SpaceWarpLayerInfo->maxDepth = 1.0f;
|
|
|
|
FIntPoint TextureSize;
|
|
if (GetRecommendedVelocityTextureSize_RenderThread(TextureSize) && PipelinedVelocityState_RenderThread.VelocitySwapchain.IsValid())
|
|
{
|
|
FIntPoint TextureOffset(0, 0);
|
|
FIntRect ImageRect(TextureOffset, TextureOffset + TextureSize);
|
|
|
|
TSharedPtr<XrSwapchainSubImage> VelocityImage = MakeShared<XrSwapchainSubImage>();
|
|
VelocityImage->swapchain = static_cast<FOpenXRSwapchain*>(PipelinedVelocityState_RenderThread.VelocitySwapchain.Get())->GetHandle();
|
|
VelocityImage->imageArrayIndex = (bIsMobileMultiViewEnabled && InViewIndex < 2) ? InViewIndex : 0;
|
|
VelocityImage->imageRect = ToXrRect(ImageRect);
|
|
SpaceWarpLayerInfo->motionVectorSubImage = *VelocityImage;
|
|
|
|
TSharedPtr<XrSwapchainSubImage> VelocityDepthImage = nullptr;
|
|
if (PipelinedVelocityState_RenderThread.VelocityDepthSwapchain.IsValid())
|
|
{
|
|
VelocityDepthImage = MakeShared<XrSwapchainSubImage>();
|
|
VelocityDepthImage->swapchain = static_cast<FOpenXRSwapchain*>(PipelinedVelocityState_RenderThread.VelocityDepthSwapchain.Get())->GetHandle();
|
|
VelocityDepthImage->imageArrayIndex = (bIsMobileMultiViewEnabled && InViewIndex < 2) ? InViewIndex : 0;
|
|
VelocityDepthImage->imageRect = ToXrRect(ImageRect);
|
|
SpaceWarpLayerInfo->depthSubImage = *VelocityDepthImage;
|
|
}
|
|
|
|
// The SpaceWarpInfo that we return from here gets used when the layer is submitted on xrEndFrame on the RHI Thread,
|
|
// so we need to keep the XrSwapchainSubImage objects on the RHI Thread until PostEndFrame_RHIThread()
|
|
GetImmediateCommandList_ForRenderCommand().EnqueueLambda([this, SpaceWarpLayerInfo, VelocityImage, VelocityDepthImage, InViewIndex](FRHICommandList& RHICmdList) {
|
|
if (SpaceWarpLayerInfo_RHIThread.IsValidIndex(InViewIndex))
|
|
{
|
|
SpaceWarpLayerInfo_RHIThread[InViewIndex] = SpaceWarpLayerInfo;
|
|
}
|
|
|
|
if (VelocityImages_RHIThread.IsValidIndex(InViewIndex) && VelocityImage.IsValid())
|
|
{
|
|
VelocityImages_RHIThread[InViewIndex] = VelocityImage;
|
|
}
|
|
|
|
if (VelocityDepthImages_RHIThread.IsValidIndex(InViewIndex) && VelocityDepthImage.IsValid())
|
|
{
|
|
VelocityDepthImages_RHIThread[InViewIndex] = VelocityDepthImage;
|
|
}
|
|
});
|
|
|
|
return SpaceWarpLayerInfo.Get();
|
|
}
|
|
return InNext;
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::PostBeginFrame_RHIThread(XrTime PredictedDisplayTime)
|
|
{
|
|
check(IsRunningRHIInSeparateThread() ? IsInRHIThread() : IsInRenderingThread());
|
|
|
|
if (!PipelinedVelocityState_RHIThread.bEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_PostBeginFrame_RHIThread, FColor::Red);
|
|
|
|
// We need a new swapchain image unless we've already acquired one for rendering
|
|
if (PipelinedVelocityState_RHIThread.VelocitySwapchain.IsValid())
|
|
{
|
|
PipelinedVelocityState_RHIThread.VelocitySwapchain->WaitCurrentImage_RHIThread(VELOCITY_SWAPCHAIN_WAIT_TIMEOUT);
|
|
if (PipelinedVelocityState_RHIThread.VelocityDepthSwapchain.IsValid())
|
|
{
|
|
PipelinedVelocityState_RHIThread.VelocityDepthSwapchain->WaitCurrentImage_RHIThread(VELOCITY_SWAPCHAIN_WAIT_TIMEOUT);
|
|
}
|
|
}
|
|
}
|
|
|
|
const void* FSpaceWarpExtensionPlugin::OnEndFrame(XrSession InSession, XrTime DisplayTime, const void* InNext)
|
|
{
|
|
check(IsRunningRHIInSeparateThread() ? IsInRHIThread() : IsInRenderingThread());
|
|
|
|
if (!PipelinedVelocityState_RHIThread.bEnabled)
|
|
{
|
|
return InNext;
|
|
}
|
|
|
|
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnEndFrame, FColor::Red);
|
|
|
|
if (PipelinedVelocityState_RHIThread.VelocitySwapchain.IsValid())
|
|
{
|
|
PipelinedVelocityState_RHIThread.VelocitySwapchain->ReleaseCurrentImage_RHIThread(nullptr);
|
|
if (PipelinedVelocityState_RHIThread.VelocityDepthSwapchain.IsValid())
|
|
{
|
|
PipelinedVelocityState_RHIThread.VelocityDepthSwapchain->ReleaseCurrentImage_RHIThread(nullptr);
|
|
}
|
|
}
|
|
|
|
return InNext;
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::PostEndFrame_RHIThread()
|
|
{
|
|
SpaceWarpLayerInfo_RHIThread.Reset();
|
|
VelocityImages_RHIThread.Reset();
|
|
VelocityDepthImages_RHIThread.Reset();
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::AllocateRenderTargetTextures_RenderThread()
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (!bSpaceWarpExtensionEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_AllocateRenderTargetTextures_RenderThread, FColor::Red);
|
|
|
|
FIntPoint VelocitySize;
|
|
if (GetRecommendedVelocityTextureSize_RenderThread(VelocitySize))
|
|
{
|
|
IOpenXRHMD* OpenXRHMD = nullptr;
|
|
if (GEngine && GEngine->XRSystem.IsValid())
|
|
{
|
|
OpenXRHMD = GEngine->XRSystem.Get()->GetIOpenXRHMD();
|
|
if (OpenXRHMD)
|
|
{
|
|
uint8 UnusedActualFormat = 0;
|
|
const FOpenXRSwapchainProperties VelocitySwapchainProperties = {
|
|
TEXT("VelocitySwapchain"),
|
|
PF_FloatRGBA,
|
|
static_cast<uint32>(VelocitySize.X),
|
|
static_cast<uint32>(VelocitySize.Y),
|
|
static_cast<uint32>(bIsMobileMultiViewEnabled ? 2 : 1),
|
|
1,
|
|
1,
|
|
// BEGIN META SECTION - Add XR Cubemap Support
|
|
(bIsMobileMultiViewEnabled) ? ETextureDimension::Texture2DArray : ETextureDimension::Texture2D,
|
|
// END META SECTION - Add XR Cubemap Support
|
|
TexCreate_RenderTargetable | TexCreate_ResolveTargetable | TexCreate_ShaderResource | TexCreate_InputAttachmentRead | TexCreate_Dynamic,
|
|
FClearValueBinding::Transparent,
|
|
TexCreate_None
|
|
};
|
|
|
|
OpenXRHMD->AllocateSwapchainTextures_RenderThread(
|
|
VelocitySwapchainProperties,
|
|
PipelinedVelocityState_RenderThread.VelocitySwapchain,
|
|
UnusedActualFormat);
|
|
|
|
const FOpenXRSwapchainProperties VelocityDepthSwapchainProperties = {
|
|
TEXT("VelocityDepthSwapchain"),
|
|
PF_DepthStencil,
|
|
static_cast<uint32>(VelocitySize.X),
|
|
static_cast<uint32>(VelocitySize.Y),
|
|
static_cast<uint32>(bIsMobileMultiViewEnabled ? 2 : 1),
|
|
1,
|
|
1,
|
|
// BEGIN META SECTION - Add XR Cubemap Support
|
|
(bIsMobileMultiViewEnabled) ? ETextureDimension::Texture2DArray : ETextureDimension::Texture2D,
|
|
// END META SECTION - Add XR Cubemap Support
|
|
TexCreate_DepthStencilTargetable | TexCreate_ShaderResource | TexCreate_InputAttachmentRead | TexCreate_Dynamic,
|
|
FClearValueBinding::DepthZero,
|
|
TexCreate_None
|
|
};
|
|
OpenXRHMD->AllocateSwapchainTextures_RenderThread(
|
|
VelocityDepthSwapchainProperties,
|
|
PipelinedVelocityState_RenderThread.VelocityDepthSwapchain,
|
|
UnusedActualFormat);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FSpaceWarpExtensionPlugin::GetRecommendedVelocityTextureSize_RenderThread(FIntPoint& OutTextureSize)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (!bSpaceWarpExtensionEnabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OutTextureSize = FIntPoint(SpaceWarpSystemProperties.recommendedMotionVectorImageRectWidth, SpaceWarpSystemProperties.recommendedMotionVectorImageRectHeight);
|
|
|
|
return true;
|
|
}
|
|
|
|
FXRSwapChainPtr FSpaceWarpExtensionPlugin::GetVelocitySwapchain_RenderThread()
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (!PipelinedVelocityState_RenderThread.bEnabled)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return PipelinedVelocityState_RenderThread.VelocitySwapchain;
|
|
}
|
|
|
|
FXRSwapChainPtr FSpaceWarpExtensionPlugin::GetVelocityDepthSwapchain_RenderThread()
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (!PipelinedVelocityState_RenderThread.bEnabled)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return PipelinedVelocityState_RenderThread.VelocityDepthSwapchain;
|
|
}
|
|
|
|
bool FSpaceWarpExtensionPlugin::IsSpaceWarpEnabled() const
|
|
{
|
|
if (bSpaceWarpExtensionEnabled)
|
|
{
|
|
static const auto CVarOculusEnableSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.Oculus.SpaceWarp.Enable"));
|
|
return CVarOculusEnableSpaceWarp && (CVarOculusEnableSpaceWarp->GetValueOnAnyThread() != 0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::FSpaceWarpViewExtension(const FAutoRegister& AutoRegister, FSpaceWarpExtensionPlugin* InPlugin)
|
|
: FHMDSceneViewExtension(AutoRegister)
|
|
, SpaceWarpExtensionPlugin(InPlugin)
|
|
{
|
|
check(SpaceWarpExtensionPlugin);
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::SetupViewFamily(FSceneViewFamily& InViewFamily)
|
|
{
|
|
check(IsInGameThread())
|
|
InViewFamily.bRenderStereoVelocity = SpaceWarpExtensionPlugin->IsSpaceWarpEnabled();
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView)
|
|
{
|
|
}
|
|
|
|
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::BeginRenderViewFamily(FSceneViewFamily& InViewFamily)
|
|
{
|
|
bool bEnabled = SpaceWarpExtensionPlugin->IsSpaceWarpEnabled();
|
|
|
|
ENQUEUE_RENDER_COMMAND(FSpaceWarpViewExtension_BeginRenderViewFamily)
|
|
([this, bEnabled](FRHICommandList& RHICmdList) {
|
|
SpaceWarpExtensionPlugin->PipelinedVelocityState_RenderThread.bEnabled = bEnabled;
|
|
|
|
FPipelinedVelocityState VelocityState = SpaceWarpExtensionPlugin->PipelinedVelocityState_RenderThread;
|
|
RHICmdList.EnqueueLambda([this, VelocityState, bEnabled](FRHICommandList& RHICmdList) {
|
|
SpaceWarpExtensionPlugin->PipelinedVelocityState_RHIThread = VelocityState;
|
|
|
|
uint32_t ViewConfigCount = 0;
|
|
XR_ENSURE(xrEnumerateViewConfigurationViews(IOpenXRHMDModule::Get().GetInstance(), IOpenXRHMDModule::Get().GetSystemId(), SpaceWarpExtensionPlugin->SelectedViewConfigurationType, 0, &ViewConfigCount, nullptr));
|
|
SpaceWarpExtensionPlugin->SpaceWarpLayerInfo_RHIThread.SetNum(ViewConfigCount);
|
|
SpaceWarpExtensionPlugin->VelocityImages_RHIThread.SetNum(ViewConfigCount);
|
|
SpaceWarpExtensionPlugin->VelocityDepthImages_RHIThread.SetNum(ViewConfigCount);
|
|
});
|
|
});
|
|
}
|
|
} // namespace OculusXR
|
|
|
|
#endif // defined(WITH_OCULUS_BRANCH)
|