// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.

#include "OculusXRHMD_Splash.h"

#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "RenderingThread.h"
#include "Misc/ScopeLock.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "StereoLayerFunctionLibrary.h"
#include "TextureResource.h"

#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidEGL.h"
#include "Android/AndroidApplication.h"
#include "OculusXRHMDTypes.h"
#endif

namespace OculusXRHMD
{

	//-------------------------------------------------------------------------------------------------
	// FSplash
	//-------------------------------------------------------------------------------------------------

	FSplash::FSplash(FOculusXRHMD* InOculusXRHMD)
		: OculusXRHMD(InOculusXRHMD), CustomPresent(InOculusXRHMD->GetCustomPresent_Internal()), FramesOutstanding(0), NextLayerId(1), bInitialized(false), bIsShown(false), bNeedSplashUpdate(false), bShouldShowSplash(false), SystemDisplayInterval(1 / 90.0f)
	{
		// Create empty quad layer for UE layer
		{
			IStereoLayers::FLayerDesc LayerDesc;
			LayerDesc.QuadSize = FVector2D(0.01f, 0.01f);
			LayerDesc.Priority = 0;
			LayerDesc.PositionType = IStereoLayers::TrackerLocked;
			LayerDesc.Texture = nullptr;
			UELayer = MakeShareable(new FLayer(NextLayerId++));
			UELayer->SetDesc(LayerDesc);
		}
	}

	FSplash::~FSplash()
	{
		// Make sure RenTicker is freed in Shutdown
		check(!Ticker.IsValid())
	}

	void FSplash::Tick_RenderThread(float DeltaTime)
	{
		CheckInRenderThread();

		if (FramesOutstanding > 0)
		{
			UE_LOG(LogHMD, VeryVerbose, TEXT("Splash skipping frame; too many frames outstanding"));
			return;
		}

		const double TimeInSeconds = FPlatformTime::Seconds();
		const double DeltaTimeInSeconds = TimeInSeconds - LastTimeInSeconds;

		if (DeltaTimeInSeconds > 2.f * SystemDisplayInterval && Layers_RenderThread_DeltaRotation.Num() > 0)
		{
			FScopeLock ScopeLock(&RenderThreadLock);
			for (TTuple<FLayerPtr, FQuat>& Info : Layers_RenderThread_DeltaRotation)
			{
				FLayerPtr Layer = Info.Key;
				const FQuat& DeltaRotation = Info.Value;
				check(Layer.IsValid());
				check(!DeltaRotation.Equals(FQuat::Identity)); // Only layers with non-zero delta rotation should be in the DeltaRotation array.

				IStereoLayers::FLayerDesc LayerDesc = Layer->GetDesc();
				LayerDesc.Transform.SetRotation(LayerDesc.Transform.GetRotation() * DeltaRotation);
				LayerDesc.Transform.NormalizeRotation();
				Layer->SetDesc(LayerDesc);
			}
			LastTimeInSeconds = TimeInSeconds;
		}

		RenderFrame_RenderThread(FRHICommandListExecutor::GetImmediateCommandList());
	}

	void FSplash::LoadSettings()
	{
		UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
		check(HMDSettings);
		ClearSplashes();
		for (const FOculusXRSplashDesc& SplashDesc : HMDSettings->SplashDescs)
		{
			AddSplash(SplashDesc);
		}

		if (HMDSettings->bAutoEnabled)
		{
			if (!PreLoadLevelDelegate.IsValid())
			{
				PreLoadLevelDelegate = FCoreUObjectDelegates::PreLoadMap.AddSP(this, &FSplash::OnPreLoadMap);
			}
			if (!PostLoadLevelDelegate.IsValid())
			{
				PostLoadLevelDelegate = FCoreUObjectDelegates::PostLoadMapWithWorld.AddSP(this, &FSplash::OnPostLoadMap);
			}
		}
		else
		{
			if (PreLoadLevelDelegate.IsValid())
			{
				FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
				PreLoadLevelDelegate.Reset();
			}
			if (PostLoadLevelDelegate.IsValid())
			{
				FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
				PostLoadLevelDelegate.Reset();
			}
		}
	}

	void FSplash::OnPreLoadMap(const FString&)
	{
		DoShow();
	}

	void FSplash::OnPostLoadMap(UWorld* LoadedWorld)
	{
		// Don't auto-hide splash if show loading screen is called explicitly
		if (!bShouldShowSplash)
		{
			UE_LOG(LogHMD, Log, TEXT("FSplash::OnPostLoadMap Hide Auto Splash"));
			HideLoadingScreen();
		}
	}

#if WITH_EDITOR
	void FSplash::OnPieBegin(bool bIsSimulating)
	{
		LoadSettings();
	}
#endif

	void FSplash::Startup()
	{
		CheckInGameThread();

		if (!bInitialized)
		{
			Settings = OculusXRHMD->CreateNewSettings();
			Frame = OculusXRHMD->CreateNewGameFrame();
			// keep units in meters rather than UU (because UU make not much sense).
			Frame->WorldToMetersScale = 1.0f;

			float SystemDisplayFrequency;
			if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&SystemDisplayFrequency)))
			{
				SystemDisplayInterval = 1.0f / SystemDisplayFrequency;
			}

			LoadSettings();

			OculusXRHMD->InitDevice();

#if WITH_EDITOR
			PieBeginDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FSplash::OnPieBegin);
#else
			UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
			check(HMDSettings);
			if (HMDSettings->bAutoEnabled)
			{
				UE_LOG(LogHMD, Log, TEXT("FSplash::Startup Show Splash on Startup"));
				DoShow();
			}
#endif

			OculusXRHMD->Settings_RenderThread = OculusXRHMD->Settings->Clone();

			bInitialized = true;
		}
	}

	void FSplash::StopTicker()
	{
		CheckInGameThread();

		if (!bIsShown)
		{
			ExecuteOnRenderThread([this]() {
				if (Ticker.IsValid())
				{
					Ticker->Unregister();
					Ticker = nullptr;
				}
			});
			UnloadTextures();
		}
	}

	void FSplash::StartTicker()
	{
		CheckInGameThread();

		if (!Ticker.IsValid())
		{
			Ticker = MakeShareable(new FTicker(this));

			ExecuteOnRenderThread([this]() {
				LastTimeInSeconds = FPlatformTime::Seconds();
				Ticker->Register();
			});
		}
	}

	void FSplash::RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList)
	{
		CheckInRenderThread();

		FScopeLock ScopeLock(&RenderThreadLock);

		// RenderFrame
		FSettingsPtr XSettings = Settings->Clone();
		FGameFramePtr XFrame = Frame->Clone();
		XFrame->FrameNumber = OculusXRHMD->NextFrameNumber;
		XFrame->ShowFlags.Rendering = true;
		TArray<FLayerPtr> XLayers = Layers_RenderThread_Input;

		ensure(XLayers.Num() != 0);

		ovrpResult Result;
		if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OculusXRHMD->WaitFrameNumber != XFrame->FrameNumber)
		{
			UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u"), XFrame->FrameNumber);
			if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame(XFrame->FrameNumber)))
			{
				UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u failed (%d)"), XFrame->FrameNumber, Result);
				XFrame->ShowFlags.Rendering = false;
			}
			else
			{
				OculusXRHMD->WaitFrameNumber = XFrame->FrameNumber;
				OculusXRHMD->NextFrameNumber = XFrame->FrameNumber + 1;
				FPlatformAtomics::InterlockedIncrement(&FramesOutstanding);
			}
		}
		else
		{
			XFrame->ShowFlags.Rendering = false;
		}

		if (XFrame->ShowFlags.Rendering)
		{
			if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().Update3(ovrpStep_Render, XFrame->FrameNumber, 0.0)))
			{
				UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().Update3 %u failed (%d)"), XFrame->FrameNumber, Result);
			}
		}

		{
			int32 LayerIndex = 0;
			int32 LayerIndex_RenderThread = 0;

			while (LayerIndex < XLayers.Num() && LayerIndex_RenderThread < Layers_RenderThread.Num())
			{
				uint32 LayerIdA = XLayers[LayerIndex]->GetId();
				uint32 LayerIdB = Layers_RenderThread[LayerIndex_RenderThread]->GetId();

				if (LayerIdA < LayerIdB)
				{
					XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
				}
				else if (LayerIdA > LayerIdB)
				{
					OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
				}
				else
				{
					XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList, Layers_RenderThread[LayerIndex_RenderThread++].Get());
				}
			}

			while (LayerIndex < XLayers.Num())
			{
				XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
			}

			while (LayerIndex_RenderThread < Layers_RenderThread.Num())
			{
				OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
			}
		}

		Layers_RenderThread = XLayers;

		for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
		{
			Layers_RenderThread[LayerIndex]->UpdateTexture_RenderThread(XSettings.Get(), CustomPresent, RHICmdList);
		}

		// This submit is required since splash happens before the game is rendering, so layers won't be submitted with game render commands
		CustomPresent->SubmitGPUCommands_RenderThread(RHICmdList);

		// RHIFrame
		for (int32 LayerIndex = 0; LayerIndex < XLayers.Num(); LayerIndex++)
		{
			XLayers[LayerIndex] = XLayers[LayerIndex]->Clone();
		}

		ExecuteOnRHIThread_DoNotWait([this, XSettings, XFrame, XLayers]() {
			ovrpResult ResultT;

			if (XFrame->ShowFlags.Rendering)
			{
				UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u"), XFrame->FrameNumber);
				if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().BeginFrame4(XFrame->FrameNumber, CustomPresent->GetOvrpCommandQueue())))
				{
					UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
					XFrame->ShowFlags.Rendering = false;
				}
			}

			FPlatformAtomics::InterlockedDecrement(&FramesOutstanding);

			Layers_RHIThread = XLayers;
			Layers_RHIThread.Sort(FLayerPtr_ComparePriority());

			if (XFrame->ShowFlags.Rendering)
			{
				TArray<const ovrpLayerSubmit*> LayerSubmitPtr;
				LayerSubmitPtr.SetNum(Layers_RHIThread.Num());

				for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
				{
					LayerSubmitPtr[LayerIndex] = Layers_RHIThread[LayerIndex]->UpdateLayer_RHIThread(XSettings.Get(), XFrame.Get(), LayerIndex);
				}

				UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u"), XFrame->FrameNumber);
				if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().EndFrame4(XFrame->FrameNumber, LayerSubmitPtr.GetData(), LayerSubmitPtr.Num(), CustomPresent->GetOvrpCommandQueue())))
				{
					UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
				}
				else
				{
					for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
					{
						Layers_RHIThread[LayerIndex]->IncrementSwapChainIndex_RHIThread(CustomPresent);
					}
				}
			}
		});
	}

	void FSplash::ReleaseResources_RHIThread()
	{
		for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
		{
			Layers_RenderThread[LayerIndex]->ReleaseResources_RHIThread();
		}

		for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
		{
			Layers_RHIThread[LayerIndex]->ReleaseResources_RHIThread();
		}

		Layers_RenderThread.Reset();
		Layers_RHIThread.Reset();
	}

	void FSplash::PreShutdown()
	{
		CheckInGameThread();
	}

	void FSplash::Shutdown()
	{
		CheckInGameThread();

#if WITH_EDITOR
		if (PieBeginDelegateHandle.IsValid())
		{
			FEditorDelegates::BeginPIE.Remove(PieBeginDelegateHandle);
			PieBeginDelegateHandle.Reset();
		}
#endif

		if (PreLoadLevelDelegate.IsValid())
		{
			FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
			PreLoadLevelDelegate.Reset();
		}
		if (PostLoadLevelDelegate.IsValid())
		{
			FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
			PostLoadLevelDelegate.Reset();
		}

		if (bInitialized)
		{
			ExecuteOnRenderThread([this]() {
				if (Ticker)
				{
					Ticker->Unregister();
					Ticker = nullptr;
				}

				ExecuteOnRHIThread([this]() {
					SplashLayers.Reset();
					Layers_RenderThread.Reset();
					Layers_RenderThread_Input.Reset();
					Layers_RHIThread.Reset();
				});
			});

			bInitialized = false;
		}
	}

	int FSplash::AddSplash(const FOculusXRSplashDesc& Desc)
	{
		CheckInGameThread();

		FScopeLock ScopeLock(&RenderThreadLock);
		return SplashLayers.Add(FSplashLayer(Desc));
	}

	void FSplash::AddSplash(const FSplashDesc& Splash)
	{
		FOculusXRSplashDesc OculusDesc;
		OculusDesc.TransformInMeters = Splash.Transform;
		OculusDesc.QuadSizeInMeters = Splash.QuadSize;
		OculusDesc.DeltaRotation = Splash.DeltaRotation;
		OculusDesc.bNoAlphaChannel = Splash.bIgnoreAlpha;
		OculusDesc.bIsDynamic = Splash.bIsDynamic || Splash.bIsExternal;
		OculusDesc.TextureOffset = Splash.UVRect.Min;
		OculusDesc.TextureScale = Splash.UVRect.Max;
		OculusDesc.LoadedTexture = Splash.Texture;

		AddSplash(OculusDesc);
	}

	void FSplash::ClearSplashes()
	{
		CheckInGameThread();

		FScopeLock ScopeLock(&RenderThreadLock);
		SplashLayers.Reset();
	}

	bool FSplash::GetSplash(unsigned InSplashLayerIndex, FOculusXRSplashDesc& OutDesc)
	{
		CheckInGameThread();

		FScopeLock ScopeLock(&RenderThreadLock);
		if (InSplashLayerIndex < unsigned(SplashLayers.Num()))
		{
			OutDesc = SplashLayers[int32(InSplashLayerIndex)].Desc;
			return true;
		}
		return false;
	}

	IStereoLayers::FLayerDesc FSplash::StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc)
	{
		IStereoLayers::FLayerDesc LayerDesc;
		if (OculusDesc.LoadedTexture->GetTextureCube() != nullptr)
		{
			LayerDesc.SetShape<FCubemapLayer>();
		}
		// else LayerDesc.Shape defaults to FQuadLayer

		LayerDesc.Transform = OculusDesc.TransformInMeters * FTransform(OculusXRHMD->GetSplashRotation().Quaternion());
		LayerDesc.QuadSize = OculusDesc.QuadSizeInMeters;
		LayerDesc.UVRect = FBox2D(OculusDesc.TextureOffset, OculusDesc.TextureOffset + OculusDesc.TextureScale);
		LayerDesc.Priority = INT32_MAX - (int32)(OculusDesc.TransformInMeters.GetTranslation().X * 1000.f);
		LayerDesc.PositionType = IStereoLayers::TrackerLocked;
		LayerDesc.Texture = OculusDesc.LoadedTexture;
		LayerDesc.Flags = IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO | (OculusDesc.bNoAlphaChannel ? IStereoLayers::LAYER_FLAG_TEX_NO_ALPHA_CHANNEL : 0) | (OculusDesc.bIsDynamic ? IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE : 0);

		return LayerDesc;
	}

	void FSplash::DoShow()
	{
		CheckInGameThread();

		OculusXRHMD->SetSplashRotationToForward();

		// Create new textures
		UnloadTextures();

		// Make sure all UTextures are loaded and contain Resource->TextureRHI
		bool bWaitForRT = false;

		for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
		{
			FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];

			if (SplashLayer.Desc.TexturePath.IsValid())
			{
				// load temporary texture (if TexturePath was specified)
				LoadTexture(SplashLayer);
			}
			if (SplashLayer.Desc.LoadingTexture && SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
			{
				SplashLayer.Desc.LoadingTexture->UpdateResource();
				bWaitForRT = true;
			}
		}

		FlushRenderingCommands();

		for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
		{
			FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];

			//@DBG BEGIN
			if (SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
			{
				if (SplashLayer.Desc.LoadingTexture->GetResource() && SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI)
				{
					SplashLayer.Desc.LoadedTexture = SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI;
				}
				else
				{
					UE_LOG(LogHMD, Warning, TEXT("Splash, %s - no Resource"), *SplashLayer.Desc.LoadingTexture->GetDesc());
				}
			}
			//@DBG END

			if (SplashLayer.Desc.LoadedTexture)
			{
				SplashLayer.Layer = MakeShareable(new FLayer(NextLayerId++));
				SplashLayer.Layer->SetDesc(StereoLayerDescFromOculusSplashDesc(SplashLayer.Desc));
			}
		}

		{
			//add oculus-generated layers through the OculusVR settings area
			FScopeLock ScopeLock(&RenderThreadLock);
			Layers_RenderThread_DeltaRotation.Reset();
			Layers_RenderThread_Input.Reset();
			for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); SplashLayerIndex++)
			{
				const FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];

				if (SplashLayer.Layer.IsValid())
				{
					FLayerPtr ClonedLayer = SplashLayer.Layer->Clone();
					Layers_RenderThread_Input.Add(ClonedLayer);

					// Register layers that need to be rotated every n ticks
					if (!SplashLayer.Desc.DeltaRotation.Equals(FQuat::Identity))
					{
						Layers_RenderThread_DeltaRotation.Emplace(ClonedLayer, SplashLayer.Desc.DeltaRotation);
					}
				}
			}

			//add UE VR splash screen
			FOculusXRSplashDesc UESplashDesc = OculusXRHMD->GetUESplashScreenDesc();
			if (UESplashDesc.LoadedTexture != nullptr)
			{
				UELayer.Reset();
				UELayer = MakeShareable(new FLayer(NextLayerId++));
				UELayer->SetDesc(StereoLayerDescFromOculusSplashDesc(UESplashDesc));
				Layers_RenderThread_Input.Add(UELayer->Clone());
			}

			Layers_RenderThread_Input.Sort(FLayerPtr_CompareId());
		}

		if (Layers_RenderThread_Input.Num() > 0)
		{
			// If no textures are loaded, this will push black frame
			StartTicker();
			bIsShown = true;
			UE_LOG(LogHMD, Log, TEXT("FSplash::DoShow"));
		}
		else
		{
			UE_LOG(LogHMD, Log, TEXT("No splash layers in FSplash::DoShow"));
		}
	}

	void FSplash::DoHide()
	{
		CheckInGameThread();

		UE_LOG(LogHMD, Log, TEXT("FSplash::DoHide"));
		bIsShown = false;

		StopTicker();
	}

	void FSplash::UpdateLoadingScreen_GameThread()
	{
		if (bNeedSplashUpdate)
		{
			if (bShouldShowSplash)
			{
				DoShow();
			}
			else
			{
				DoHide();
			}

			bNeedSplashUpdate = false;
		}
	}

	void FSplash::ShowLoadingScreen()
	{
		bShouldShowSplash = true;

		// DoShow will be called from UpdateSplashScreen_Gamethread().
		// This can can happen if the splashes are already being shown, as it will reset the relative positions and delta rotations of the layers.
		bNeedSplashUpdate = true;
	}

	void FSplash::HideLoadingScreen()
	{
		bShouldShowSplash = false;
		bNeedSplashUpdate = bIsShown; // no need to call DoHide when the splash is already hidden
	}

	void FSplash::UnloadTextures()
	{
		CheckInGameThread();

		// unload temporary loaded textures
		FScopeLock ScopeLock(&RenderThreadLock);
		for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
		{
			if (SplashLayers[SplashLayerIndex].Desc.TexturePath.IsValid())
			{
				UnloadTexture(SplashLayers[SplashLayerIndex]);
			}
		}
	}

	void FSplash::LoadTexture(FSplashLayer& InSplashLayer)
	{
		CheckInGameThread();

		UnloadTexture(InSplashLayer);

		UE_LOG(LogLoadingSplash, Log, TEXT("Loading texture for splash %s..."), *InSplashLayer.Desc.TexturePath.GetAssetName());
		InSplashLayer.Desc.LoadingTexture = Cast<UTexture>(InSplashLayer.Desc.TexturePath.TryLoad());
		if (InSplashLayer.Desc.LoadingTexture != nullptr)
		{
			UE_LOG(LogLoadingSplash, Log, TEXT("...Success. "));
		}
		InSplashLayer.Desc.LoadedTexture = nullptr;
		InSplashLayer.Layer.Reset();
	}

	void FSplash::UnloadTexture(FSplashLayer& InSplashLayer)
	{
		CheckInGameThread();

		InSplashLayer.Desc.LoadingTexture = nullptr;
		InSplashLayer.Desc.LoadedTexture = nullptr;
		InSplashLayer.Layer.Reset();
	}

} // namespace OculusXRHMD

#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
