October3d55/M/PICOXRPlugin/Source/PICOXRHMD/Private/PXR_StereoLayer.cpp

1313 lines
47 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright PICO Technology Co., Ltd. All rights reserved.
// This plugin incorporates portions of the Unreal® Engine. Unreal® is a trademark or registered trademark of Epic Games, Inc. in the United States of America and elsewhere.
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PXR_StereoLayer.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "PXR_HMDPrivateRHI.h"
#include "HardwareInfo.h"
#include "IXRTrackingSystem.h"
#include "PXR_HMD.h"
#include "PXR_Utils.h"
#include "GameFramework/PlayerController.h"
#include "PXR_Log.h"
#include "Materials/Material.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "XRThreadUtils.h"
#include "PXR_GameFrame.h"
#include "PXR_StereoLayersFlagsSupplier.h"
#ifdef PICO_CUSTOM_ENGINE
void GetTrackingSpaceDeltaPose(const FGameSettings* Setting, const FPXRGameFrame* Frame, FTransform& TrackingSpaceDeltaPose)
{
TrackingSpaceDeltaPose = Frame->TrackingToWorld * Frame->LastTrackingToWorld.Inverse();
PXR_LOGV(PxrUnreal, "AppSpaceWarp GetTrackingSpaceDeltaPose TrackingToWorld:%s LastTrackingToWorld.Inverse():%s"
,PLATFORM_CHAR(*Frame->TrackingToWorld.ToString())
,PLATFORM_CHAR(*Frame->LastTrackingToWorld.Inverse().ToString()));
PXR_LOGV(PxrUnreal, "AppSpaceWarp GetTrackingSpaceDeltaPose TrackingSpaceDeltaPose 1:%s"
,PLATFORM_CHAR(*TrackingSpaceDeltaPose.ToString()));
FTransform SettingBasePose = FTransform(Setting->BaseOrientation, Setting->BaseOffset);
PXR_LOGV(PxrUnreal, "AppSpaceWarp GetTrackingSpaceDeltaPose SettingBasePose Orientation:X:%f,Y:%f,Z:%f,W:%f"
, Setting->BaseOrientation.X
, Setting->BaseOrientation.Y
, Setting->BaseOrientation.Z
, Setting->BaseOrientation.W);
TrackingSpaceDeltaPose = SettingBasePose.Inverse() * TrackingSpaceDeltaPose * SettingBasePose;
PXR_LOGV(PxrUnreal, "AppSpaceWarp TrackingSpaceDeltaPose 2:%s",PLATFORM_CHAR(*TrackingSpaceDeltaPose.ToString()));
}
#endif
FPxrLayer::FPxrLayer(uint32 ID, uint32 InPxrLayerId, FDelayDeleteLayerManager* InDelayDeletion) :
ID(ID),
PxrLayerId(InPxrLayerId),
DelayDeletion(InDelayDeletion)
{
}
FPxrLayer::~FPxrLayer()
{
if (IsInGameThread())
{
ExecuteOnRenderThread([ID = this->ID, PxrLayerId = this->PxrLayerId, DelayDeletion = this->DelayDeletion]()
{
DelayDeletion->AddPxrLayerToDeferredDeletionQueue(ID, PxrLayerId);
});
}
else
{
DelayDeletion->AddPxrLayerToDeferredDeletionQueue(ID, PxrLayerId);
}
}
uint64_t OverlayImages[2] = {};
uint64_t OverlayNativeImages[2][3] = {};
uint32 FPICOXRStereoLayer::PxrLayerIDCounter = 0;
FPICOXRStereoLayer::FPICOXRStereoLayer(FPICOXRHMD* InHMDDevice, uint32 InPXRLayerId, const IStereoLayers::FLayerDesc& InDesc)
: bSplashLayer(false)
, bSplashBlackProjectionLayer(false)
, bMRCLayer(false)
, bNeedsTexSrgbCreate(false)
#ifdef PICO_CUSTOM_ENGINE
, bEnableEyeTrackingFoveationRendering(false)
#endif
, HMDDevice(InHMDDevice)
, ID(InPXRLayerId)
, PxrLayerID(0)
, bTextureNeedUpdate(false)
, UnderlayMeshComponent(NULL)
, UnderlayActor(NULL)
, PxrLayer(nullptr)
, TrackingMode(PXR_TRACKING_MODE_POSITION_BIT)
{
PXR_LOGD(PxrUnreal, "FPICOXRStereoLayer with ID=%d", ID);
#if PLATFORM_ANDROID
FMemory::Memzero(PxrLayerCreateParam);
#endif
SetPXRLayerDesc(InDesc);
}
FPICOXRStereoLayer::FPICOXRStereoLayer(const FPICOXRStereoLayer& InPXRLayer)
: bSplashLayer(InPXRLayer.bSplashLayer)
, bSplashBlackProjectionLayer(InPXRLayer.bSplashBlackProjectionLayer)
, bMRCLayer(InPXRLayer.bMRCLayer)
, bNeedsTexSrgbCreate(InPXRLayer.bNeedsTexSrgbCreate)
#ifdef PICO_CUSTOM_ENGINE
, bEnableEyeTrackingFoveationRendering(InPXRLayer.bEnableEyeTrackingFoveationRendering)
#endif
, HMDDevice(InPXRLayer.HMDDevice)
, ID(InPXRLayer.ID)
, PxrLayerID(InPXRLayer.PxrLayerID)
, LayerDesc(InPXRLayer.LayerDesc)
, SwapChain(InPXRLayer.SwapChain)
, LeftSwapChain(InPXRLayer.LeftSwapChain)
, FoveationSwapChain(InPXRLayer.FoveationSwapChain)
#ifdef PICO_CUSTOM_ENGINE
, MotionVectorSwapChain(InPXRLayer.MotionVectorSwapChain)
, MotionVectorDepthSwapChain(InPXRLayer.MotionVectorDepthSwapChain)
#endif
, bTextureNeedUpdate(InPXRLayer.bTextureNeedUpdate)
, UnderlayMeshComponent(InPXRLayer.UnderlayMeshComponent)
, UnderlayActor(InPXRLayer.UnderlayActor)
, PxrLayer(InPXRLayer.PxrLayer)
, TrackingMode(InPXRLayer.TrackingMode)
{
FMemory::Memcpy(&PxrLayerCreateParam, &InPXRLayer.PxrLayerCreateParam, sizeof(PxrLayerCreateParam));
}
FPICOXRStereoLayer::~FPICOXRStereoLayer()
{
}
TSharedPtr<FPICOXRStereoLayer, ESPMode::ThreadSafe> FPICOXRStereoLayer::CloneMyself() const
{
return MakeShareable(new FPICOXRStereoLayer(*this));
}
void FPICOXRStereoLayer::SetPXRLayerDesc(const IStereoLayers::FLayerDesc& InDesc)
{
bool bRatioChanged = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO) != (InDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO);
if (LayerDesc.Texture != InDesc.Texture || LayerDesc.LeftTexture != InDesc.LeftTexture)
{
bTextureNeedUpdate = true;
}
LayerDesc = InDesc;
ManageUnderlayComponent(bRatioChanged);
}
void FPICOXRStereoLayer::ManageUnderlayComponent(bool bRatioChanged)
{
if (IsLayerSupportDepth())
{
if (bRatioChanged)
{
DestroyUnderlayMesh();
}
const FString UnderlayNameStr = FString::Printf(TEXT("PICOUnderlay_%d"), ID);
const FName UnderlayComponentName(*UnderlayNameStr);
if (UnderlayMeshComponent == NULL)
{
UWorld* World = NULL;
for (const FWorldContext& Context : GEngine->GetWorldContexts())
{
if (Context.WorldType == EWorldType::Game || Context.WorldType == EWorldType::PIE)
{
World = Context.World();
}
}
if (World == NULL)
{
return;
}
UnderlayActor = World->SpawnActor<AActor>();
UnderlayMeshComponent = NewObject<UProceduralMeshComponent>(UnderlayActor, UnderlayComponentName);
UnderlayMeshComponent->RegisterComponent();
TArray<FVector> VerticePos;
TArray<int32> TriangleIndics;
TArray<FVector> Normals;
TArray<FVector2D> TexUV;
TArray<FLinearColor> VertexColors;
TArray<FProcMeshTangent> Tangents;
CreateUnderlayMesh(VerticePos, TriangleIndics, TexUV);
UnderlayMeshComponent->CreateMeshSection_LinearColor(0, VerticePos, TriangleIndics, Normals, TexUV, VertexColors, Tangents, false);
if (HMDDevice && HMDDevice->GetContentResourceFinder())
{
UMaterialInstanceDynamic* DynamicMaterialInstance = UMaterialInstanceDynamic::Create(HMDDevice->GetContentResourceFinder()->StereoLayerDepthMat, NULL);
UnderlayMeshComponent->SetMaterial(0, DynamicMaterialInstance);
}
}
if (IsValid(UnderlayMeshComponent))
{
UnderlayMeshComponent->SetWorldTransform(LayerDesc.Transform);
}
else
{
PXR_LOGD(PxrUnreal, "UnderlayMeshComponent IsValid FalseDestroyUnderlayMesh");
DestroyUnderlayMesh();
}
}
else
{
DestroyUnderlayMesh();
}
return;
}
static void AddFaceIndices(const int v0, const int v1, const int v2, const int v3, TArray<int32>& Triangles, bool inverse)
{
if (inverse)
{
Triangles.Add(v0);
Triangles.Add(v2);
Triangles.Add(v1);
Triangles.Add(v0);
Triangles.Add(v3);
Triangles.Add(v2);
}
else
{
Triangles.Add(v0);
Triangles.Add(v1);
Triangles.Add(v2);
Triangles.Add(v0);
Triangles.Add(v2);
Triangles.Add(v3);
}
}
void FPICOXRStereoLayer::CreateUnderlayMesh(TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector2D>& UV0)
{
if (LayerDesc.HasShape<FQuadLayer>())
{
const float QuadScale = 0.99;
FIntPoint TexSize = LayerDesc.Texture.IsValid() ? LayerDesc.Texture->GetTexture2D()->GetSizeXY() : LayerDesc.LayerSize;
float AspectRatio = TexSize.X ? (float)TexSize.Y / (float)TexSize.X : 3.0f / 4.0f;
float QuadSizeX = LayerDesc.QuadSize.X;
float QuadSizeY = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO) ? LayerDesc.QuadSize.X * AspectRatio : LayerDesc.QuadSize.Y;
Vertices.Init(FVector::ZeroVector, 4);
Vertices[0] = FVector(0.0, -QuadSizeX / 2, -QuadSizeY / 2) * QuadScale;
Vertices[1] = FVector(0.0, QuadSizeX / 2, -QuadSizeY / 2) * QuadScale;
Vertices[2] = FVector(0.0, QuadSizeX / 2, QuadSizeY / 2) * QuadScale;
Vertices[3] = FVector(0.0, -QuadSizeX / 2, QuadSizeY / 2) * QuadScale;
UV0.Init(FVector2D::ZeroVector, 4);
UV0[0] = FVector2D(1, 0);
UV0[1] = FVector2D(1, 1);
UV0[2] = FVector2D(0, 0);
UV0[3] = FVector2D(0, 1);
Triangles.Reserve(6);
AddFaceIndices(0, 1, 2, 3, Triangles, false);
}
else if (LayerDesc.HasShape<FCylinderLayer>())
{
float Arc, Radius, Height;
const FCylinderLayer& CylinderProps = LayerDesc.GetShape<FCylinderLayer>();
Arc = CylinderProps.OverlayArc;
Radius = CylinderProps.Radius;
Height = CylinderProps.Height;
const float CylinderScale = 0.99;
FIntPoint TexSize = LayerDesc.Texture.IsValid() ? LayerDesc.Texture->GetTexture2D()->GetSizeXY() : LayerDesc.LayerSize;
float AspectRatio = TexSize.X ? (float)TexSize.Y / (float)TexSize.X : 3.0f / 4.0f;
float CylinderHeight = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO) ? Arc * AspectRatio : Height;
const FVector XAxis = FVector(1, 0, 0);
const FVector YAxis = FVector(0, 1, 0);
const FVector HalfHeight = FVector(0, 0, CylinderHeight / 2);
const float ArcAngle = Arc / Radius;
const int Sides = (int)((ArcAngle * 180) / (PI * 5));
Vertices.Init(FVector::ZeroVector, 2 * (Sides + 1));
UV0.Init(FVector2D::ZeroVector, 2 * (Sides + 1));
Triangles.Init(0, Sides * 6);
float CurrentAngle = -ArcAngle / 2;
const float AngleStep = ArcAngle / Sides;
for (int Side = 0; Side < Sides + 1; Side++)
{
FVector MidVertex = Radius * (FMath::Cos(CurrentAngle) * XAxis + FMath::Sin(CurrentAngle) * YAxis);
Vertices[2 * Side] = (MidVertex - HalfHeight) * CylinderScale;
Vertices[(2 * Side) + 1] = (MidVertex + HalfHeight) * CylinderScale;
UV0[2 * Side] = FVector2D(1 - (Side / (float)Sides), 0);
UV0[(2 * Side) + 1] = FVector2D(1 - (Side / (float)Sides), 1);
CurrentAngle += AngleStep;
if (Side < Sides)
{
Triangles[6 * Side + 0] = 2 * Side;
Triangles[6 * Side + 2] = 2 * Side + 1;
Triangles[6 * Side + 1] = 2 * (Side + 1) + 1;
Triangles[6 * Side + 3] = 2 * Side;
Triangles[6 * Side + 5] = 2 * (Side + 1) + 1;
Triangles[6 * Side + 4] = 2 * (Side + 1);
}
}
}
else if (LayerDesc.HasShape<FCubemapLayer>())
{
const float CubemapScale = 1000;
Vertices.Init(FVector::ZeroVector, 8);
Vertices[0] = FVector(-1.0, -1.0, -1.0) * CubemapScale;
Vertices[1] = FVector(-1.0, -1.0, 1.0) * CubemapScale;
Vertices[2] = FVector(-1.0, 1.0, -1.0) * CubemapScale;
Vertices[3] = FVector(-1.0, 1.0, 1.0) * CubemapScale;
Vertices[4] = FVector(1.0, -1.0, -1.0) * CubemapScale;
Vertices[5] = FVector(1.0, -1.0, 1.0) * CubemapScale;
Vertices[6] = FVector(1.0, 1.0, -1.0) * CubemapScale;
Vertices[7] = FVector(1.0, 1.0, 1.0) * CubemapScale;
Triangles.Reserve(24);
AddFaceIndices(0, 1, 3, 2, Triangles, false);
AddFaceIndices(4, 5, 7, 6, Triangles, true);
AddFaceIndices(0, 1, 5, 4, Triangles, true);
AddFaceIndices(2, 3, 7, 6, Triangles, false);
AddFaceIndices(0, 2, 6, 4, Triangles, false);
AddFaceIndices(1, 3, 7, 5, Triangles, true);
}
else if (LayerDesc.HasShape<FEACLayer>())
{
float Scale;
const FEACLayer& EACProps = LayerDesc.GetShape<FEACLayer>();
Scale = EACProps.Scale;
// Implements a cube mesh
// Cubemap Scale should be changed eventually to incorporate scale
Vertices.Init(FVector::ZeroVector, 8);
Vertices[0] = FVector(-1.0, -1.0, -1.0) * Scale;
Vertices[1] = FVector(-1.0, -1.0, 1.0) * Scale;
Vertices[2] = FVector(-1.0, 1.0, -1.0) * Scale;
Vertices[3] = FVector(-1.0, 1.0, 1.0) * Scale;
Vertices[4] = FVector(1.0, -1.0, -1.0) * Scale;
Vertices[5] = FVector(1.0, -1.0, 1.0) * Scale;
Vertices[6] = FVector(1.0, 1.0, -1.0) * Scale;
Vertices[7] = FVector(1.0, 1.0, 1.0) * Scale;
Triangles.Reserve(24);
AddFaceIndices(0, 1, 3, 2, Triangles, false);
AddFaceIndices(4, 5, 7, 6, Triangles, true);
AddFaceIndices(0, 1, 5, 4, Triangles, true);
AddFaceIndices(2, 3, 7, 6, Triangles, false);
AddFaceIndices(0, 2, 6, 4, Triangles, false);
AddFaceIndices(1, 3, 7, 5, Triangles, true);
}
}
void FPICOXRStereoLayer::PXRLayersCopy_RenderThread(FPICOXRRenderBridge* RenderBridge, FRHICommandListImmediate& RHICmdList)
{
check(IsInRenderingThread());
PXR_LOGV(PxrUnreal, "ID=%d, bTextureNeedUpdate=%d, IsVisible:%d, SwapChain.IsValid=%d, LayerDesc.Texture.IsValid=%d", ID, bTextureNeedUpdate, IsVisible(), SwapChain.IsValid(), LayerDesc.Texture.IsValid());
if (bTextureNeedUpdate && IsVisible())
{
// Copy textures
if (LayerDesc.Texture.IsValid() && SwapChain.IsValid())
{
bool bNoAlpha = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_TEX_NO_ALPHA_CHANNEL) != 0;
bool bInvertY = bMRCLayer || LayerDesc.HasShape<FCubemapLayer>() ? true : false;
// Mono
FRHITexture* SrcTexture = LayerDesc.Texture;
FRHITexture* DstTexture = SwapChain->GetTexture();
FIntRect DstRect, SrcRect;
#if PLATFORM_ANDROID
DstRect = SrcRect = FIntRect(LayerDesc.UVRect.Min.X * (float)PxrLayerCreateParam.width, LayerDesc.UVRect.Min.Y * (float)PxrLayerCreateParam.height,
LayerDesc.UVRect.Max.X * (float)PxrLayerCreateParam.width, LayerDesc.UVRect.Max.Y * (float)PxrLayerCreateParam.height);
#else
DstRect = SrcRect = FIntRect();
#endif
RenderBridge->TransferImage_RenderThread(RHICmdList, DstTexture, SrcTexture, DstRect, SrcRect, true, bNoAlpha, bMRCLayer, bInvertY);
// Stereo
if (LayerDesc.LeftTexture.IsValid() && LeftSwapChain.IsValid())
{
FRHITexture* LeftSrcTexture = LayerDesc.LeftTexture;
FRHITexture* LeftDstTexture = LeftSwapChain->GetTexture();
RenderBridge->TransferImage_RenderThread(RHICmdList, LeftDstTexture, LeftSrcTexture, DstRect, SrcRect, true, bNoAlpha, bMRCLayer, bInvertY);
}
bTextureNeedUpdate = false;
}
else
{
DestroyUnderlayMesh();
}
}
}
bool FPICOXRStereoLayer::InitPXRLayer_RenderThread(const FGameSettings* Settings, FPICOXRRenderBridge* CustomPresent, FDelayDeleteLayerManager* DelayDeletion, FRHICommandListImmediate& RHICmdList, const FPICOXRStereoLayer* InLayer)
{
check(IsInRenderingThread());
int32 MSAAValue = 1;
bool bNeedFFRSwapChain = false;
#ifdef PICO_CUSTOM_ENGINE
bool bNeedMotionVectorSwapChain=false;
#endif
if (ID == 0)
{
static const auto CVarMobileMSAA = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.MSAACount"));
MSAAValue = (CVarMobileMSAA ? CVarMobileMSAA->GetValueOnAnyThread() : 1);
if (CustomPresent->RHIString == TEXT("Vulkan"))
{
bNeedFFRSwapChain = true;
}
#ifdef PICO_CUSTOM_ENGINE
if (HMDDevice->IsSupportsSpaceWarp())
{
bNeedMotionVectorSwapChain=true;
}
#endif
}
else if(bSplashBlackProjectionLayer)
{
uint32 SizeX = 1;
uint32 SizeY = 1;
if (LayerDesc.Texture.IsValid())
{
FRHITexture* Texture2D = LayerDesc.Texture->GetTexture2D();
if (Texture2D)
{
SizeX = Texture2D->GetSizeX();
SizeY = Texture2D->GetSizeY();
}
}
uint32 Layout = 1;
if (HMDDevice->IsUsingMobileMultiView() && IsMobilePlatform(Settings->CurrentShaderPlatform))
{
Layout = 2;
}
PxrLayerCreateParam.layerShape = PXR_LAYER_PROJECTION;
PxrLayerCreateParam.width = SizeX;
PxrLayerCreateParam.height = SizeY;
PxrLayerCreateParam.faceCount = 1;
PxrLayerCreateParam.mipmapCount = 1;
PxrLayerCreateParam.sampleCount = 1;
PxrLayerCreateParam.arraySize = Layout;
PxrLayerCreateParam.layerLayout = Layout == 2 ? PXR_LAYER_LAYOUT_ARRAY : PXR_LAYER_LAYOUT_DOUBLE_WIDE;
#if PLATFORM_ANDROID
if (CustomPresent->RHIString == TEXT("Vulkan"))
{
PxrLayerCreateParam.format = IsMobileColorsRGB() ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
}
#endif
PxrLayerCreateParam.layerFlags |= PXR_LAYER_FLAG_STATIC_IMAGE;
}
else
{
PxrLayerCreateParam.layerShape = static_cast<PxrLayerShape>(GetShapeType());
PxrLayerCreateParam.layerType = IsLayerSupportDepth() ? PXR_UNDERLAY : PXR_OVERLAY;
if (LayerDesc.Texture.IsValid())
{
FRHITexture* Texture2D = LayerDesc.Texture->GetTexture2D();
FRHITexture* TextureCube = LayerDesc.Texture->GetTextureCube();
if (Texture2D)
{
PxrLayerCreateParam.width = Texture2D->GetSizeX();
PxrLayerCreateParam.height = Texture2D->GetSizeY();
PxrLayerCreateParam.sampleCount = Texture2D->GetNumSamples();
PxrLayerCreateParam.mipmapCount = Texture2D->GetNumMips();
}
else if (TextureCube)
{
PxrLayerCreateParam.width = PxrLayerCreateParam.height = TextureCube->GetSize();
PxrLayerCreateParam.sampleCount = 1;
PxrLayerCreateParam.mipmapCount = 1;
}
}
else
{
PxrLayerCreateParam.width = LayerDesc.QuadSize.X;
PxrLayerCreateParam.height = LayerDesc.QuadSize.Y;
PxrLayerCreateParam.sampleCount = 1;
PxrLayerCreateParam.mipmapCount = 1;
}
if (PxrLayerCreateParam.width == 0 || PxrLayerCreateParam.height == 0)
{
return false;
}
if (PxrLayerCreateParam.layerShape == PxrLayerShape::PXR_LAYER_CUBE /* || PxrLayerCreateParam.layerShape == PxrLayerShape::PXR_LAYER_EAC */)
{
PxrLayerCreateParam.faceCount = 6;
}
else
{
PxrLayerCreateParam.faceCount = 1;
}
PxrLayerCreateParam.arraySize = 1;
if (!(LayerDesc.Flags & IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE))
{
PxrLayerCreateParam.layerFlags |= PXR_LAYER_FLAG_STATIC_IMAGE;
}
else
{
PxrLayerCreateParam.layerFlags &= ~PXR_LAYER_FLAG_STATIC_IMAGE;
}
PxrLayerCreateParam.layerLayout = LayerDesc.LeftTexture.IsValid() ? PXR_LAYER_LAYOUT_STEREO : PXR_LAYER_LAYOUT_MONO;
#if PLATFORM_ANDROID
if (CustomPresent->RHIString == TEXT("Vulkan"))
{
PxrLayerCreateParam.format = IsMobileColorsRGB() ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
}
#endif
}
if (IfCanReuseLayers(InLayer))
{
//GameThread = RenderThread
PxrLayerID = InLayer->PxrLayerID;
PxrLayer = InLayer->PxrLayer;
SwapChain = InLayer->SwapChain;
LeftSwapChain = InLayer->LeftSwapChain;
FoveationSwapChain =InLayer->FoveationSwapChain;
#ifdef PICO_CUSTOM_ENGINE
MotionVectorSwapChain=InLayer->MotionVectorSwapChain;
MotionVectorDepthSwapChain=InLayer->MotionVectorDepthSwapChain;
#endif
bTextureNeedUpdate |= InLayer->bTextureNeedUpdate;
bNeedsTexSrgbCreate = InLayer->bNeedsTexSrgbCreate;
}
else
{
TArray<uint64> TextureResources;
TArray<uint64> LeftTextureResources;
TArray<uint64> FFRTextureResources;
#ifdef PICO_CUSTOM_ENGINE
TArray<uint64> MotionVectorTextureResources;
TArray<uint64> MotionVectorDepthTextureResources;
#endif
uint32_t FoveationWidth = 0;
uint32_t FoveationHeight = 0;
TextureResources.Empty();
LeftTextureResources.Empty();
FFRTextureResources.Empty();
bool bNativeTextureCreated = false;
#ifdef PICO_CUSTOM_ENGINE
int RenderTextureX = 0;
int RenderTextureY = 0;
FPICOXRHMDModule::GetPluginWrapper().GetConfigInt(PXR_RENDER_TEXTURE_WIDTH, &RenderTextureX);
FPICOXRHMDModule::GetPluginWrapper().GetConfigInt(PXR_RENDER_TEXTURE_HEIGHT, &RenderTextureY);
const uint32_t MotionVectorWidth = RenderTextureX/4;
const uint32_t MotionVectorHeight =RenderTextureY/4;
const uint32_t MotionVectorDepthWidth = MotionVectorWidth;
const uint32_t MotionVectorDepthHeight = MotionVectorHeight;
PXR_LOGI(PxrUnreal, "MotionVectorWidth =%d", MotionVectorWidth);
PXR_LOGI(PxrUnreal, "MotionVectorHeight =%d", MotionVectorHeight);
#endif
ExecuteOnRHIThread([&]()
{
#if PLATFORM_ANDROID
PxrLayerCreateParam.layerId = PxrLayerID = PxrLayerIDCounter;
if (FPICOXRHMDModule::GetPluginWrapper().bIsSessionInitialized && (FPICOXRHMDModule::GetPluginWrapper().CreateLayer(&PxrLayerCreateParam) == 0))
{
PxrLayerIDCounter++;
uint32_t ImageCounts = 0;
uint64_t LayerImages[2][3] = {};
uint64_t FoveationImages[3]={};
#ifdef PICO_CUSTOM_ENGINE
uint64_t MotionVectorImages[3]={};
uint64_t MotionDepthImages[3]={};
#endif
FPICOXRHMDModule::GetPluginWrapper().GetLayerImageCount(PxrLayerID, PXR_EYE_RIGHT, &ImageCounts);
ensure(ImageCounts != 0);
for (uint32_t i = 0; i < ImageCounts; i++)
{
FPICOXRHMDModule::GetPluginWrapper().GetLayerImage(PxrLayerID, PXR_EYE_RIGHT, i, &LayerImages[1][i]);
PXR_LOGI(PxrUnreal, "Pxr_GetLayerImage Right LayerImages[1][%d]=u_%u", i, (uint32_t)LayerImages[1][i]);
TextureResources.Add(LayerImages[1][i]);
#ifdef PICO_CUSTOM_ENGINE
if (bNeedMotionVectorSwapChain)
{
FPICOXRHMDModule::GetPluginWrapper().GetLayerImageExt(PxrLayerID,PXR_EYE_RIGHT,i,&MotionVectorImages[i],PXR_LAYER_IMAGE_TYPE_MOTION_VECTOR);
PXR_LOGI(PxrUnreal, "Pxr_GetLayerImageExt Right MotionVectorImage[%d]=u_%u", i, (uint32_t)MotionVectorImages[i]);
FPICOXRHMDModule::GetPluginWrapper().GetLayerImageExt(PxrLayerID,PXR_EYE_RIGHT,i,&MotionDepthImages[i],PXR_LAYER_IMAGE_TYPE_DEPTH);
PXR_LOGI(PxrUnreal, "Pxr_GetLayerImageExt Right MotionDepthImage[%d]=u_%u", i, (uint32_t)MotionDepthImages[i]);
MotionVectorTextureResources.Add(MotionVectorImages[i]);
MotionVectorDepthTextureResources.Add(MotionDepthImages[i]);
}
#endif
if (bNeedFFRSwapChain)
{
if (i == 0) {
FPICOXRHMDModule::GetPluginWrapper().GetLayerFoveationImage(PxrLayerID, PXR_EYE_RIGHT, &FoveationImages[0], &FoveationWidth, &FoveationHeight);
} else {
FPICOXRHMDModule::GetPluginWrapper().GetLayerImageExt(PxrLayerID, PXR_EYE_RIGHT, i, &FoveationImages[i], PXR_LAYER_IMAGE_TYPE_FDM);
}
FFRTextureResources.Add(FoveationImages[i]);
}
}
if (PxrLayerCreateParam.layerLayout == PXR_LAYER_LAYOUT_STEREO)
{
FPICOXRHMDModule::GetPluginWrapper().GetLayerImageCount(PxrLayerID, PXR_EYE_LEFT, &ImageCounts);
ensure(ImageCounts != 0);
for (uint32_t i = 0; i < ImageCounts; i++)
{
FPICOXRHMDModule::GetPluginWrapper().GetLayerImage(PxrLayerID, PXR_EYE_LEFT, i, &LayerImages[0][i]);
PXR_LOGI(PxrUnreal, "Pxr_GetLayerImage Left LayerImages[0][%d]=u_%u", i, (uint32_t)LayerImages[0][i]);
LeftTextureResources.Add(LayerImages[0][i]);
}
}
bNativeTextureCreated = true;
}
else
{
PXR_LOGE(PxrUnreal, "Create native texture failed!");
}
#endif
});
if (bNativeTextureCreated)
{
PxrLayer = MakeShareable<FPxrLayer>(new FPxrLayer(ID, PxrLayerID, DelayDeletion));
ERHIResourceType ResourceType;
if (PxrLayerCreateParam.layerShape == PxrLayerShape::PXR_LAYER_CUBE)
{
ResourceType = RRT_TextureCube;
}
else if (PxrLayerCreateParam.arraySize == 2)
{
ResourceType = RRT_Texture2DArray;
}
else
{
ResourceType = RRT_Texture2D;
}
ETextureCreateFlags Flags= TexCreate_None;
ETextureCreateFlags TargetableTextureFlags= TexCreate_None;
Flags = TargetableTextureFlags |= TexCreate_RenderTargetable | TexCreate_ShaderResource |TexCreate_ResolveTargetable |(IsMobileColorsRGB() ? TexCreate_SRGB : TexCreate_None);
SwapChain = CustomPresent->CreateSwapChain_RenderThread(ID,PxrLayerID, ResourceType, TextureResources, PF_R8G8B8A8, PxrLayerCreateParam.width, PxrLayerCreateParam.height, PxrLayerCreateParam.arraySize, PxrLayerCreateParam.mipmapCount, PxrLayerCreateParam.sampleCount, Flags, TargetableTextureFlags, MSAAValue);
if (PxrLayerCreateParam.layerLayout == PXR_LAYER_LAYOUT_STEREO)
{
LeftSwapChain = CustomPresent->CreateSwapChain_RenderThread(ID,PxrLayerID, ResourceType, LeftTextureResources, PF_R8G8B8A8, PxrLayerCreateParam.width, PxrLayerCreateParam.height, PxrLayerCreateParam.arraySize, PxrLayerCreateParam.mipmapCount, PxrLayerCreateParam.sampleCount, Flags, TargetableTextureFlags, MSAAValue);
}
if (bNeedFFRSwapChain)
{
ETextureCreateFlags TCF = TexCreate_Foveation;
FoveationSwapChain = CustomPresent->CreateSwapChain_RenderThread(ID,PxrLayerID, ResourceType, FFRTextureResources, PF_R8G8, FoveationWidth, FoveationHeight, PxrLayerCreateParam.arraySize, 1, 1, Flags, TCF, 1);
}
#ifdef PICO_CUSTOM_ENGINE
if (bNeedMotionVectorSwapChain)
{
EPixelFormat MvPixelFormat = PF_FloatRGBA;
ETextureCreateFlags MVTexCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
MotionVectorSwapChain = CustomPresent->CreateSwapChain_RenderThread(ID, PxrLayerID,ResourceType,MotionVectorTextureResources, MvPixelFormat, MotionVectorWidth, MotionVectorHeight, PxrLayerCreateParam.arraySize, 1, 1, MVTexCreateFlags, MVTexCreateFlags,1);
if (MotionVectorDepthTextureResources.Num() && MotionVectorDepthTextureResources[0] != (unsigned long long)nullptr)
{
ETextureCreateFlags MVDepthTexCreateFlags = TexCreate_ShaderResource | TexCreate_DepthStencilTargetable;
MotionVectorDepthSwapChain = CustomPresent->CreateSwapChain_RenderThread(ID, PxrLayerID,ResourceType,MotionVectorDepthTextureResources, PF_DepthStencil, MotionVectorDepthWidth, MotionVectorDepthHeight, PxrLayerCreateParam.arraySize, 1, 1, MVDepthTexCreateFlags, MVDepthTexCreateFlags,1);
}
else
{
MotionVectorDepthSwapChain = NULL;
}
}
else
{
MotionVectorSwapChain.Reset();
MotionVectorDepthSwapChain.Reset();
}
#endif
bTextureNeedUpdate = true;
}
else
{
PXR_LOGE(PxrUnreal, "Create SwapChain failed!");
return false;
}
}
if ((LayerDesc.Flags & IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE) && LayerDesc.Texture.IsValid() && IsVisible())
{
bTextureNeedUpdate = true;
}
return true;
}
bool FPICOXRStereoLayer::IfCanReuseLayers(const FPICOXRStereoLayer* InLayer) const
{
if (!InLayer || !InLayer->PxrLayer.IsValid())
{
return false;
}
#if PLATFORM_ANDROID
if (PxrLayerCreateParam.width != InLayer->PxrLayerCreateParam.width ||
PxrLayerCreateParam.height != InLayer->PxrLayerCreateParam.height ||
PxrLayerCreateParam.layerShape != InLayer->PxrLayerCreateParam.layerShape ||
PxrLayerCreateParam.layerLayout != InLayer->PxrLayerCreateParam.layerLayout ||
PxrLayerCreateParam.mipmapCount != InLayer->PxrLayerCreateParam.mipmapCount ||
PxrLayerCreateParam.sampleCount != InLayer->PxrLayerCreateParam.sampleCount ||
PxrLayerCreateParam.format != InLayer->PxrLayerCreateParam.format ||
PxrLayerCreateParam.layerFlags != InLayer->PxrLayerCreateParam.layerFlags ||
PxrLayerCreateParam.layerType != InLayer->PxrLayerCreateParam.layerType ||
bNeedsTexSrgbCreate != InLayer->bNeedsTexSrgbCreate
#ifdef PICO_CUSTOM_ENGINE
|| bEnableEyeTrackingFoveationRendering != InLayer->bEnableEyeTrackingFoveationRendering
#endif
)
{
return false;
}
if (TrackingMode != InLayer->TrackingMode)
{
return false;
}
#endif
return true;
}
void FPICOXRStereoLayer::ReleaseResources_RHIThread()
{
CheckInRHIThread();
PxrLayerID = 0;
PxrLayer.Reset();
SwapChain.Reset();
FoveationSwapChain.Reset();
LeftSwapChain.Reset();
bTextureNeedUpdate = false;
}
void FPICOXRStereoLayer::DestroyUnderlayMesh()
{
if (UnderlayActor)
{
if (UnderlayMeshComponent)
{
UnderlayMeshComponent->DestroyComponent();
UnderlayMeshComponent = nullptr;
}
UnderlayActor->Destroy();
UnderlayActor = nullptr;
}
}
void FPICOXRStereoLayer::IncrementSwapChainIndex_RHIThread(FPICOXRRenderBridge* RenderBridge)
{
if ((LayerDesc.Flags & IStereoLayers::LAYER_FLAG_HIDDEN) != 0)
{
return;
}
if (SwapChain && SwapChain.IsValid())
{
int32 index = 0;
#if PLATFORM_ANDROID
FPICOXRHMDModule::GetPluginWrapper().GetLayerNextImageIndex(PxrLayerID, &index);
#endif
while (index != SwapChain->GetSwapChainIndex_RHIThread())
{
SwapChain->IncrementSwapChainIndex_RHIThread();
}
#ifdef PICO_CUSTOM_ENGINE
if (MotionVectorSwapChain.IsValid())
{
while (index != MotionVectorSwapChain->GetSwapChainIndex_RHIThread())
{
MotionVectorSwapChain->IncrementSwapChainIndex_RHIThread();
}
}
if(MotionVectorDepthSwapChain.IsValid())
{
while (index != MotionVectorDepthSwapChain->GetSwapChainIndex_RHIThread())
{
MotionVectorDepthSwapChain->IncrementSwapChainIndex_RHIThread();
}
}
#endif
if (LeftSwapChain && LeftSwapChain.IsValid())
{
while (index != LeftSwapChain->GetSwapChainIndex_RHIThread())
{
LeftSwapChain->IncrementSwapChainIndex_RHIThread();
}
}
}
}
const void FPICOXRStereoLayer::SubmitLayer_RHIThread(const FGameSettings* Settings, const FPXRGameFrame* Frame)
{
PXR_LOGV(PxrUnreal, "Submit Layer:%u", ID);
float ColorScale[4] = { Settings->ColorScale.x, Settings->ColorScale.y, Settings->ColorScale.z, Settings->ColorScale.w };
float ColorOffset[4] = { Settings->ColorOffset.x, Settings->ColorOffset.y, Settings->ColorOffset.z, Settings->ColorOffset.w };
if (ID == 0)
{
PxrLayerProjection2 layerProjection = {};
layerProjection.header.layerId = PxrLayerID;
layerProjection.header.layerFlags = 0;
layerProjection.header.sensorFrameIndex = Frame->ViewNumber;
layerProjection.header.useLayerBlend=1;
PxrLayerBlend layerBlend={};
static const auto CVarBlendMode = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PICO.BlendModeSetting"));
const EPICOXRBlendModeType BlendModeType = static_cast<EPICOXRBlendModeType>(CVarBlendMode->GetValueOnAnyThread());
switch (BlendModeType) {
case EPICOXRBlendModeType::CoveringMode:
{
layerBlend.srcColor=PxrBlendFactor::PXR_BLEND_FACTOR_ONE;
layerBlend.dstColor=PxrBlendFactor::PXR_BLEND_FACTOR_ONE;
}
break;
case EPICOXRBlendModeType::ClipMode:
{
layerBlend.srcColor=PxrBlendFactor::PXR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
layerBlend.dstColor=PxrBlendFactor::PXR_BLEND_FACTOR_SRC_ALPHA;
}
break;
case EPICOXRBlendModeType::AdditiveMode:
{
layerBlend.srcColor=PxrBlendFactor::PXR_BLEND_FACTOR_ONE;
layerBlend.dstColor=PxrBlendFactor::PXR_BLEND_FACTOR_SRC_ALPHA;
}
break;
default: ;
}
layerBlend.srcAlpha=PxrBlendFactor::PXR_BLEND_FACTOR_ONE;
layerBlend.dstAlpha=PxrBlendFactor::PXR_BLEND_FACTOR_ONE;
layerProjection.header.layerBlend=layerBlend;
if (!HMDDevice->bNeedDrawBlackEye)
{
FMemory::Memcpy(layerProjection.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerProjection.header.colorBias, ColorOffset, sizeof(ColorOffset));
}
#ifdef PICO_CUSTOM_ENGINE
//PICO AppSpaceWarp
static const auto CVarPICOEnableSpaceWarpInternal1 = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PICO.SpaceWarp.EnableInternal"));
if (CVarPICOEnableSpaceWarpInternal1->GetValueOnAnyThread())
{
FTransform TrackingSpaceDeltaPose;
GetTrackingSpaceDeltaPose(Settings, Frame, TrackingSpaceDeltaPose);
FQuat PxrQuat=FPICOXRUtils::ConvertUnrealQuatToXRQuat(TrackingSpaceDeltaPose.GetRotation());
FVector PxrLocation= FPICOXRUtils::ConvertUnrealVectorToXRVector(TrackingSpaceDeltaPose.GetLocation(),Frame->WorldToMetersScale);
PxrPosef PxrDeltaPose = {};
PxrDeltaPose.position.x=PxrLocation.X;
PxrDeltaPose.position.y=PxrLocation.Y;
PxrDeltaPose.position.z=PxrLocation.Z;
PXR_LOGV(PxrUnreal, "AppSpaceWarp SubmitLayer_RHIThread PxrDeltaPose.position:X:%f,Y:%f,Z:%f", PxrDeltaPose.position.x,PxrDeltaPose.position.y,PxrDeltaPose.position.z);
PxrDeltaPose.orientation.x=PxrQuat.X;
PxrDeltaPose.orientation.y=PxrQuat.Y;
PxrDeltaPose.orientation.z=PxrQuat.Z;
PxrDeltaPose.orientation.w=PxrQuat.W;
PXR_LOGV(PxrUnreal, "AppSpaceWarp SubmitLayer_RHIThread PxrDeltaPose.orientation:X:%f,Y:%f,Z:%f,W:%f", PxrDeltaPose.orientation.x,PxrDeltaPose.orientation.y,PxrDeltaPose.orientation.z,PxrDeltaPose.orientation.w);
layerProjection.deltaPose=PxrDeltaPose;
layerProjection.header.layerFlags = PXR_LAYER_FLAG_USE_FRAME_EXTRAPOLATION;
layerProjection.minDepth=0.1f;
layerProjection.maxDepth=1.0f;
//Todo:Infinity is not supported for now
layerProjection.nearZ=1000000/Frame->WorldToMetersScale;
//Todo:GNearClippingPlane No unit conversion, just pass it on
layerProjection.farZ=GNearClippingPlane/Frame->WorldToMetersScale;
}
#endif
// Resize Eye Layer based on settings/
// NOTE: The size and position of the Eye Layer
// is updated in FPICOXRHMD::SetFinalViewRect(...)
if (Settings->IsAdaptiveResolutionEnabled())
{
layerProjection.header.useImageRect = true;
for (int EyeIndex = 0; EyeIndex < 2; ++EyeIndex)
{
layerProjection.header.imageRect[EyeIndex] = {
Settings->EyeRenderViewport[EyeIndex].Min.X,
Settings->EyeRenderViewport[EyeIndex].Min.Y,
Settings->EyeRenderViewport[EyeIndex].Width(),
Settings->EyeRenderViewport[EyeIndex].Height()
};
}
}
static const auto CVarPICOSuperResolution = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PICO.EnableSuperResolution"));
CVarPICOSuperResolution->GetValueOnAnyThread() == 1 ? layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_SUPER_RESOLUTION : layerProjection.header.layerFlags &= ~PXR_LAYER_FLAG_ENABLE_SUPER_RESOLUTION;
PXR_LOGV(PxrUnreal, "SuperResolution is %d",CVarPICOSuperResolution->GetValueOnAnyThread());
static const auto CVarPICOSharpening = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PICO.SharpeningSetting"));
const EPICOXRSharpeningType SharpeningType = static_cast<EPICOXRSharpeningType>(CVarPICOSharpening->GetValueOnAnyThread());
switch (SharpeningType) {
case EPICOXRSharpeningType::NormalSharpening:
{
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_NORMAL_SHARPENING;
PXR_LOGV(PxrUnreal, "NormalSharpening Layer Flag is enable");
}
break;
case EPICOXRSharpeningType::QualitySharpening:
{
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_QUALITY_SHARPENING;
PXR_LOGV(PxrUnreal, "QualitySharpening Layer Flag is enable");
}
break;
default: ;
}
static const auto CVarPICOSharpeningEnhance = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PICO.SharpeningEnhanceMode"));
const EPICOXRSharpeningEnhanceModeType EnhanceMode = static_cast<EPICOXRSharpeningEnhanceModeType>(CVarPICOSharpeningEnhance->GetValueOnAnyThread());
switch (EnhanceMode) {
case EPICOXRSharpeningEnhanceModeType::FixedFoveated:
{
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_FIXED_FOVEATED_SHARPENING;
PXR_LOGV(PxrUnreal, "FixedFoveatedSharpening Layer Flag is enable");
}
break;
case EPICOXRSharpeningEnhanceModeType::Adaptive:
{
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_SELF_ADAPTIVE_SHARPENING;
PXR_LOGV(PxrUnreal, "AdaptiveSharpening Layer Flag is enable");
}
break;
case EPICOXRSharpeningEnhanceModeType::Both:
{
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_FIXED_FOVEATED_SHARPENING;
layerProjection.header.layerFlags |= PXR_LAYER_FLAG_ENABLE_SELF_ADAPTIVE_SHARPENING;
PXR_LOGV(PxrUnreal, "FixedFoveatedSharpening and AdaptiveSharpening Layer Flag is enable");
}
break;
default: ;
}
FPICOXRHMDModule::GetPluginWrapper().SubmitLayer2((PxrLayerHeader2*)&layerProjection);
PXR_LOGV(PxrUnreal, "SubmitLayer2 Flag is 0x%08x",layerProjection.header.layerFlags);
}
else if (bSplashBlackProjectionLayer)
{
PxrLayerProjection layerProjection = {};
layerProjection.header.layerId = PxrLayerID;
layerProjection.header.layerFlags = 0;
layerProjection.header.sensorFrameIndex = Frame->ViewNumber;
FPICOXRHMDModule::GetPluginWrapper().SubmitLayer((PxrLayerHeader*)&layerProjection);
}
else
{
int LayerFlags = FPICOXRStereoLayersFlagsSupplier::Get()->GetPxrLayerFlags(LayerDesc.Flags);
if (!Settings->bApplyColorScaleAndOffsetToAllLayers || bSplashLayer)
{
for (int32 i = 0; i < 4; i++)
{
ColorScale[i] = 1;
ColorOffset[i] = 0;
}
}
FTransform BaseTransform = FTransform::Identity;
uint32 Flags = 0;
Flags |= bMRCLayer ? (1 << 30) : 0;
Flags |= LayerFlags;
PXR_LOGV(PxrUnreal, "Submit StereoLayer Flag is 0x%08x",Flags);
FVector LocationScaleInv(Frame->WorldToMetersScale);
FVector LocationScale = LocationScaleInv.Reciprocal();
PxrVector3f Scale = ToPxrVector3f(GetLayerScale() * LocationScale);
switch (LayerDesc.PositionType)
{
case IStereoLayers::WorldLocked:
BaseTransform.SetRotation(Frame->TrackingToWorld.GetRotation());
BaseTransform.SetLocation(Frame->TrackingToWorld.GetTranslation());
break;
case IStereoLayers::TrackerLocked:
break;
case IStereoLayers::FaceLocked:
Flags |= PXR_LAYER_FLAG_HEAD_LOCKED;
break;
}
FQuat Orientation = BaseTransform.GetRotation().Inverse() * GetLayerOrientation();
FVector Location = BaseTransform.InverseTransformPosition(GetLayerLocation());
FPose OutLayerPose = FPose(Orientation, Location);
if (LayerDesc.PositionType != IStereoLayers::FaceLocked)
{
ConvertPose_Private(FPose(Orientation, Location), OutLayerPose, Settings->BaseOrientation.Inverse(), Settings->BaseOrientation.Inverse().RotateVector(-Settings->BaseOffset * LocationScaleInv), 1.0);
}
PxrPosef PxrLayerSubmitPose;
PxrLayerSubmitPose.orientation = ToPxrQuatf(OutLayerPose.Orientation);
PxrLayerSubmitPose.position = ToPxrVector3f(OutLayerPose.Position * LocationScale);
int SizeX = PxrLayerCreateParam.width;
int SizeY = PxrLayerCreateParam.height;
float AspectRatio = SizeX ? (float)SizeY / (float)SizeX : 3.0f / 4.0f;
int32 ShapeType = GetShapeType();
if (ShapeType == (int32)PxrLayerShape::PXR_LAYER_QUAD)
{
PxrLayerQuad layerSubmit = {};
layerSubmit.header.layerId = PxrLayerID;
layerSubmit.header.compositionDepth = 0;
layerSubmit.header.sensorFrameIndex = Frame->ViewNumber;
layerSubmit.header.layerFlags = Flags;
FMemory::Memcpy(layerSubmit.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerSubmit.header.colorBias, ColorOffset, sizeof(ColorOffset));
layerSubmit.pose = PxrLayerSubmitPose;
float QuadSizeY = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO) ? LayerDesc.QuadSize.X * AspectRatio : LayerDesc.QuadSize.Y;
layerSubmit.size[0] = (float)(LayerDesc.QuadSize.X * Scale.x);
layerSubmit.size[1] = (float)(QuadSizeY * Scale.y);
FPICOXRHMDModule::GetPluginWrapper().SubmitLayer((PxrLayerHeader*)&layerSubmit);
}
else if (ShapeType == (int32)PxrLayerShape::PXR_LAYER_CYLINDER)
{
PxrLayerCylinder layerSubmit = {};
layerSubmit.header.layerId = PxrLayerID;
layerSubmit.header.compositionDepth = 0;
layerSubmit.header.sensorFrameIndex = Frame->ViewNumber;
layerSubmit.header.layerFlags = Flags;
FMemory::Memcpy(layerSubmit.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerSubmit.header.colorBias, ColorOffset, sizeof(ColorOffset));
layerSubmit.pose = PxrLayerSubmitPose;
const FCylinderLayer& CylinderProps = LayerDesc.GetShape<FCylinderLayer>();
float CylinderHeight = (LayerDesc.Flags & IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO) ? CylinderProps.OverlayArc * AspectRatio : CylinderProps.Height;
layerSubmit.centralAngle = CylinderProps.OverlayArc / CylinderProps.Radius;
layerSubmit.height = CylinderHeight * Scale.x;
layerSubmit.radius = CylinderProps.Radius * Scale.y;
FPICOXRHMDModule::GetPluginWrapper().SubmitLayer((PxrLayerHeader*)&layerSubmit);
}
else if (ShapeType == (int32)PxrLayerShape::PXR_LAYER_EQUIRECT)
{
PxrLayerEquirect layerSubmit = {};
layerSubmit.header.layerId = PxrLayerID;
layerSubmit.header.compositionDepth = 0;
layerSubmit.header.sensorFrameIndex = Frame->ViewNumber;
layerSubmit.header.layerFlags = Flags;
layerSubmit.header.layerShape = PxrLayerShape::PXR_LAYER_EQUIRECT;
layerSubmit.header.useImageRect = 1;
FMemory::Memcpy(layerSubmit.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerSubmit.header.colorBias, ColorOffset, sizeof(ColorOffset));
const FEquirectLayer& EquirectProps = LayerDesc.GetShape<FEquirectLayer>();
float ScaleX[2], ScaleY[2], BiasX[2], BiasY[2], imagerectx[2], imagerecty[2], imagerectwidth[2], imagerectheight[2], umax[2], umin[2], vmax[2], vmin[2];
umax[0] = EquirectProps.LeftUVRect.Max.X;
umin[0] = EquirectProps.LeftUVRect.Min.X;
vmax[0] = EquirectProps.LeftUVRect.Max.Y;
vmin[0] = EquirectProps.LeftUVRect.Min.Y;
umax[1] = EquirectProps.RightUVRect.Max.X;
umin[1] = EquirectProps.RightUVRect.Min.X;
vmax[1] = EquirectProps.RightUVRect.Max.Y;
vmin[1] = EquirectProps.RightUVRect.Min.Y;
//Scale is not currently used
ScaleX[0] = EquirectProps.LeftScale.X;
ScaleY[0] = EquirectProps.LeftScale.Y;
ScaleX[1] = EquirectProps.RightScale.X;
ScaleY[1] = EquirectProps.RightScale.Y;
//Bias not currently used
BiasX[0] = EquirectProps.LeftBias.X;
BiasY[0] = EquirectProps.LeftBias.Y;
BiasX[1] = EquirectProps.RightBias.X;
BiasY[1] = EquirectProps.RightBias.Y;
for (int32 EyeIndex = 0; EyeIndex < 2; EyeIndex++)
{
//The UV far point of the engine is in the upper left corner
float temp = vmin[EyeIndex];
vmin[EyeIndex] = 1 - vmax[EyeIndex];
vmax[EyeIndex] = 1 - temp;
//Sphere radius
layerSubmit.radius[EyeIndex] = 100.0f;
//Layer pose
layerSubmit.pose[EyeIndex] = PxrLayerSubmitPose;
//SrcRect
imagerectx[EyeIndex] = (int)(umin[EyeIndex] * PxrLayerCreateParam.width);
imagerecty[EyeIndex] = (int)(vmin[EyeIndex] * PxrLayerCreateParam.height);
imagerectwidth[EyeIndex] = PxrLayerCreateParam.width * (umax[EyeIndex] - umin[EyeIndex]);
imagerectheight[EyeIndex] = PxrLayerCreateParam.height * (vmax[EyeIndex] - vmin[EyeIndex]);
layerSubmit.header.imageRect[EyeIndex] = { (int)imagerectx[EyeIndex], (int)imagerecty[EyeIndex], (int)imagerectwidth[EyeIndex],(int)imagerectheight[EyeIndex] };
//DstRect
BiasX[EyeIndex] = -umin[EyeIndex] / (umax[EyeIndex] - umin[EyeIndex]);
BiasY[EyeIndex] = (vmax[EyeIndex] - 1) / (vmax[EyeIndex] - vmin[EyeIndex]);
ScaleX[EyeIndex] = 1 / (umax[EyeIndex] - umin[EyeIndex]);
ScaleY[EyeIndex] = 1 / (vmax[EyeIndex] - vmin[EyeIndex]);
layerSubmit.scaleX[EyeIndex] = ScaleX[EyeIndex];
layerSubmit.scaleY[EyeIndex] = ScaleY[EyeIndex];
layerSubmit.biasX[EyeIndex] = BiasX[EyeIndex];
layerSubmit.biasY[EyeIndex] = BiasY[EyeIndex];
PXR_LOGV(PxrUnreal, "EyeIndex:%d,ScaleX:%f,ScaleY:%fBiasX:%fBiasY:%fimagerectx:%fimagerecty:%f,imagerectwidth:%f,imagerectheight:%f",
EyeIndex, ScaleX[EyeIndex], ScaleY[EyeIndex], BiasX[EyeIndex], BiasY[EyeIndex], imagerectx[EyeIndex], imagerecty[EyeIndex], imagerectwidth[EyeIndex], imagerectheight[EyeIndex]);
}
int result;
result = FPICOXRHMDModule::GetPluginWrapper().SubmitLayer2((PxrLayerHeader2*)&layerSubmit);
if (result != (int)PxrReturnStatus::PXR_RET_SUCCESS)
{
PXR_LOGE(PxrUnreal, "Submit Layer:%d PxrLayerEquirect Failed!:%d", PxrLayerID, result);
}
}
else if (ShapeType == (int32)PxrLayerShape::PXR_LAYER_CUBE)
{
PxrLayerCube2 layerSubmit = {};
layerSubmit.header.layerId = PxrLayerID;
layerSubmit.header.compositionDepth = 0;
layerSubmit.header.sensorFrameIndex = Frame->ViewNumber;
layerSubmit.header.layerFlags = Flags;
layerSubmit.header.layerShape = PxrLayerShape::PXR_LAYER_CUBE;
FMemory::Memcpy(layerSubmit.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerSubmit.header.colorBias, ColorOffset, sizeof(ColorOffset));
for (int32 EyeIndex = 0; EyeIndex < 2; EyeIndex++)
{
layerSubmit.pose[EyeIndex] = PxrLayerSubmitPose;
}
int result;
result = FPICOXRHMDModule::GetPluginWrapper().SubmitLayer2((PxrLayerHeader2*)&layerSubmit);
if (result != (int)PxrReturnStatus::PXR_RET_SUCCESS)
{
PXR_LOGE(PxrUnreal, "Submit Layer:%d PxrLayerCube2 Failed!:%d", PxrLayerID, result);
}
}
else if (ShapeType == (int32)PxrLayerShape::PXR_LAYER_EAC)
{
const FEACLayer& EACProps = LayerDesc.GetShape<FEACLayer>();
PxrLayerEAC2 layerSubmit = {};
layerSubmit.header.layerId = PxrLayerID;
layerSubmit.header.compositionDepth = -1;
layerSubmit.header.sensorFrameIndex = Frame->ViewNumber;
layerSubmit.header.layerFlags = Flags;
layerSubmit.header.layerShape = PxrLayerShape::PXR_LAYER_EAC;
layerSubmit.header.useImageRect = 1;
FMemory::Memcpy(layerSubmit.header.colorScale, ColorScale, sizeof(ColorScale));
FMemory::Memcpy(layerSubmit.header.colorBias, ColorOffset, sizeof(ColorOffset));
/* Start of Unique Parameters for EAC */
layerSubmit.modelType = EACProps.GetModelType();
layerSubmit.overlapFactor = EACProps.OverlapFactor;
for (int32 EyeIndex = 0; EyeIndex < 2; EyeIndex++)
{
layerSubmit.header.imageRect[EyeIndex] = {
int32(FGenericPlatformMath::RoundToInt(SizeX * EACProps.UVRect[EyeIndex].Min.X)),
int32(FGenericPlatformMath::RoundToInt(SizeY * EACProps.UVRect[EyeIndex].Min.Y)),
int32(FGenericPlatformMath::RoundToInt(SizeX * (EACProps.UVRect[EyeIndex].GetSize().X))),
int32(FGenericPlatformMath::RoundToInt(SizeY * (EACProps.UVRect[EyeIndex].GetSize().Y)))
};
layerSubmit.pose[EyeIndex] = PxrLayerSubmitPose;
FVector Offset = FPICOXRUtils::ConvertUnrealVectorToXRVector(EACProps.Offset[EyeIndex], 1.0);
FQuat OffsetRot = FPICOXRUtils::ConvertUnrealQuatToXRQuat(EACProps.OffsetRot[EyeIndex]);
layerSubmit.offset[EyeIndex] = { float(Offset.X), float(Offset.Y), float(Offset.Z) };
layerSubmit.offsetRot[EyeIndex] = { float(OffsetRot.X), float(OffsetRot.Y), float(OffsetRot.Z), float(OffsetRot.W) };
}
/* End of Unique Parameters for EAC */
int result;
result = FPICOXRHMDModule::GetPluginWrapper().SubmitLayer2((PxrLayerHeader2*)&layerSubmit);
if (result != (int)PxrReturnStatus::PXR_RET_SUCCESS)
{
PXR_LOGE(PxrUnreal, "Submit Layer:%d PxrLayerEAC Failed!:%d", PxrLayerID, result);
}
}
}
}
int32 FPICOXRStereoLayer::GetShapeType()
{
int32 ShapeType = 0;
#if PLATFORM_ANDROID
if (LayerDesc.HasShape<FQuadLayer>())
{
ShapeType = 1;
}
else if (LayerDesc.HasShape<FCylinderLayer>())
{
ShapeType = 2;
}
else if (LayerDesc.HasShape<FEquirectLayer>())
{
ShapeType = 3;
}
else if (LayerDesc.HasShape<FCubemapLayer>())
{
ShapeType = 5;
}
else if (LayerDesc.HasShape<FEACLayer>())
{
ShapeType = 6;
}
#endif
return ShapeType;
}
void FPICOXRStereoLayer::SetEyeLayerDesc(uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, FString RHIString,bool EnableSubSampled)
{
PxrLayerCreateParam.layerShape = PXR_LAYER_PROJECTION;
PxrLayerCreateParam.width = SizeX;
PxrLayerCreateParam.height = SizeY;
PxrLayerCreateParam.faceCount = 1;
PxrLayerCreateParam.mipmapCount = NumMips;
PxrLayerCreateParam.sampleCount = NumSamples;
PxrLayerCreateParam.arraySize = ArraySize;
PxrLayerCreateParam.layerLayout = ArraySize == 2 ? PXR_LAYER_LAYOUT_ARRAY : PXR_LAYER_LAYOUT_DOUBLE_WIDE;
#if PLATFORM_ANDROID
if (RHIString == TEXT("Vulkan"))
{
PxrLayerCreateParam.format = IsMobileColorsRGB() ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
}
if (EnableSubSampled)
{
PxrLayerCreateParam.layerFlags |= PXR_LAYER_FLAG_ENABLE_SUBSAMPLED;
}
else
{
PxrLayerCreateParam.layerFlags &= ~PXR_LAYER_FLAG_ENABLE_SUBSAMPLED;
}
#ifdef PICO_CUSTOM_ENGINE
if (HMDDevice->IsSupportsSpaceWarp())
{
PxrLayerCreateParam.layerFlags |= PXR_LAYER_FLAG_ENABLE_FRAME_EXTRAPOLATION ;
}
#endif
#endif
}
const FName FEACLayer::ShapeName = FName("EACLayer");
uint32_t FEACLayer::GetModelType() const
{
// Determine if EAC is Viewport. This is the case when offset is zero and offsetRot is identity.
bool bIsViewportEAC = !(Offset[0].IsNearlyZero()
&& Offset[1].IsNearlyZero()
&& OffsetRot[0].IsIdentity()
&& OffsetRot[1].IsIdentity());
switch (Subtype)
{
case ESubtypeEAC::EAC360:
return bIsViewportEAC ? 1 : 0;
break;
case ESubtypeEAC::EAC180:
return 4;
break;
}
return 0;
}
void UStereoLayerShapeEAC::SetOverlapFactor(float InOverlapFactor)
{
if (OverlapFactor == InOverlapFactor)
{
return;
}
OverlapFactor = InOverlapFactor;
MarkStereoLayerDirty();
}
void UStereoLayerShapeEAC::SetSubtype(ESubtypeEAC InSubtype)
{
if (Subtype == InSubtype)
{
return;
}
Subtype = InSubtype;
MarkStereoLayerDirty();
}
void UStereoLayerShapeEAC::ApplyShape(IStereoLayers::FLayerDesc& LayerDesc)
{
LayerDesc.SetShape<FEACLayer>(Scale, OverlapFactor, Subtype, LeftUVRect, RightUVRect, Offset, OffsetRot);
}
#if WITH_EDITOR
void UStereoLayerShapeEAC::DrawShapeVisualization(const class FSceneView* View, class FPrimitiveDrawInterface* PDI)
{
}
#endif