Files
UnrealEngine/Engine/Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private/OpenXRHMD_Swapchain.cpp
2025-05-18 13:04:45 +08:00

509 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OpenXRHMD_Swapchain.h"
#include "OpenXRCore.h"
#include "XRThreadUtils.h"
#include "Epic_openxr.h"
static TAutoConsoleVariable<int32> CVarOpenXRSwapchainRetryCount(
TEXT("vr.OpenXRSwapchainRetryCount"),
9,
TEXT("Number of times the OpenXR plugin will attempt to wait for the next swapchain image."),
ECVF_RenderThreadSafe);
FOpenXRSwapchain::FOpenXRSwapchain(TArray<FTextureRHIRef>&& InRHITextureSwapChain, const FTextureRHIRef & InRHITexture, XrSwapchain InHandle) :
FXRSwapChain(MoveTemp(InRHITextureSwapChain), InRHITexture),
Handle(InHandle),
ImageAcquired(false),
ImageReady(false)
{
}
FOpenXRSwapchain::~FOpenXRSwapchain()
{
XR_ENSURE(xrDestroySwapchain(Handle));
}
// FIXME: The Vulkan extension requires access to the VkQueue in xrAcquireSwapchainImage,
// so calling this function on any other thread than the RHI thread is unsafe on some runtimes.
void FOpenXRSwapchain::IncrementSwapChainIndex_RHIThread()
{
bool WasAcquired = false;
ImageAcquired.compare_exchange_strong(WasAcquired, true);
if (WasAcquired)
{
UE_LOG(LogHMD, Verbose, TEXT("Attempted to redundantly acquire image %d in swapchain %p"), SwapChainIndex_RHIThread.load(), reinterpret_cast<const void*>(Handle));
return;
}
SCOPED_NAMED_EVENT(AcquireImage, FColor::Red);
uint32_t SwapChainIndex = 0;
XrSwapchainImageAcquireInfo Info;
Info.type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO;
Info.next = nullptr;
XR_ENSURE(xrAcquireSwapchainImage(Handle, &Info, &SwapChainIndex));
RHITexture = RHITextureSwapChain[SwapChainIndex];
SwapChainIndex_RHIThread = SwapChainIndex;
UE_LOG(LogHMD, VeryVerbose, TEXT("FOpenXRSwapchain::IncrementSwapChainIndex_RHIThread() Acquired image %d in swapchain %p metal texture: 0x%x"), SwapChainIndex, reinterpret_cast<const void*>(Handle), RHITexture.GetReference()->GetNativeResource());
}
void FOpenXRSwapchain::WaitCurrentImage_RHIThread(int64 Timeout)
{
check(IsInRenderingThread() || IsInRHIThread());
bool WasAcquired = true;
ImageAcquired.compare_exchange_strong(WasAcquired, false);
if (!WasAcquired)
{
UE_LOG(LogHMD, Warning, TEXT("Attempted to wait on unacquired image %d in swapchain %p"), SwapChainIndex_RHIThread.load(), reinterpret_cast<const void*>(Handle));
return;
}
bool WasReady = false;
ImageReady.compare_exchange_strong(WasReady, true);
if (WasReady)
{
UE_LOG(LogHMD, Verbose, TEXT("Attempted to redundantly wait on image %d in swapchain %p"), SwapChainIndex_RHIThread.load(), reinterpret_cast<const void*>(Handle));
return;
}
SCOPED_NAMED_EVENT(WaitImage, FColor::Red);
XrSwapchainImageWaitInfo WaitInfo;
WaitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
WaitInfo.next = nullptr;
WaitInfo.timeout = Timeout;
XrResult WaitResult = XR_SUCCESS;
int RetryCount = CVarOpenXRSwapchainRetryCount.GetValueOnAnyThread();
do
{
XR_ENSURE(WaitResult = xrWaitSwapchainImage(Handle, &WaitInfo));
if (WaitResult == XR_TIMEOUT_EXPIRED) //-V547
{
UE_LOG(LogHMD, Warning, TEXT("Timed out waiting on swapchain image %u! Attempts remaining %d."), SwapChainIndex_RHIThread.load(), RetryCount);
}
} while (WaitResult == XR_TIMEOUT_EXPIRED && RetryCount-- > 0);
if (WaitResult != XR_SUCCESS) //-V547
{
// We can't continue without acquiring a new swapchain image since we won't have an image available to render to.
UE_LOG(LogHMD, Fatal, TEXT("Failed to wait on acquired swapchain image. This usually indicates a problem with the OpenXR runtime."));
}
UE_LOG(LogHMD, VeryVerbose, TEXT("FOpenXRSwapchain::WaitCurrentImage_RHIThread() Waited on image swapchain %p"), reinterpret_cast<const void*>(Handle));
}
void FOpenXRSwapchain::ReleaseCurrentImage_RHIThread(IRHICommandContext* RHICmdContext)
{
check(IsInRenderingThread() || IsInRHIThread());
bool WasReady = true;
ImageReady.compare_exchange_strong(WasReady, false);
if (!WasReady)
{
UE_LOG(LogHMD, Warning, TEXT("Attempted to release image %d in swapchain %p that wasn't ready for being written to."), SwapChainIndex_RHIThread.load(), reinterpret_cast<const void*>(Handle));
return;
}
SCOPED_NAMED_EVENT(ReleaseImage, FColor::Red);
void* Next = nullptr;
XrRHIContextEPIC RHIContextEPIC = { (XrStructureType)XR_TYPE_RHI_CONTEXT_EPIC };
if (RHICmdContext != nullptr)
{
RHIContextEPIC.RHIContext = RHICmdContext;
RHIContextEPIC.next = Next;
Next = &RHIContextEPIC;
}
XrSwapchainImageReleaseInfo ReleaseInfo;
ReleaseInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
ReleaseInfo.next = Next;
XR_ENSURE(xrReleaseSwapchainImage(Handle, &ReleaseInfo));
UE_LOG(LogHMD, VeryVerbose, TEXT("FOpenXRSwapchain::ReleaseCurrentImage_RHIThread() Released on image in swapchain %p"), reinterpret_cast<const void*>(Handle));
}
uint8 FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(XrSession InSession, uint8 RequestedFormat, TFunction<uint32(uint8)> ToPlatformFormat /*= nullptr*/)
{
if (!ToPlatformFormat)
{
ToPlatformFormat = [](uint8 InFormat) { return GPixelFormats[InFormat].PlatformFormat; };
}
uint32_t FormatsCount = 0;
XR_ENSURE(xrEnumerateSwapchainFormats(InSession, 0, &FormatsCount, nullptr));
TArray<int64_t> Formats;
Formats.SetNum(FormatsCount);
XR_ENSURE(xrEnumerateSwapchainFormats(InSession, (uint32_t)Formats.Num(), &FormatsCount, Formats.GetData()));
ensure(FormatsCount == Formats.Num());
// Return immediately if the runtime supports the exact format being requested.
uint32 PlatformFormat = ToPlatformFormat(RequestedFormat);
if (Formats.Contains(PlatformFormat))
{
return RequestedFormat;
}
// Search for a fallback format in order of preference (first element in the array has the highest preference).
uint8 FallbackFormat = 0;
uint32 FallbackPlatformFormat = 0;
for (int64_t Format : Formats) //-V1078
{
if (RequestedFormat == PF_DepthStencil)
{
if (Format == ToPlatformFormat(PF_D24))
{
FallbackFormat = PF_D24;
FallbackPlatformFormat = Format;
break;
}
}
else
{
if (Format == ToPlatformFormat(PF_B8G8R8A8))
{
FallbackFormat = PF_B8G8R8A8;
FallbackPlatformFormat = Format;
break;
}
else if (Format == ToPlatformFormat(PF_R8G8B8A8))
{
FallbackFormat = PF_R8G8B8A8;
FallbackPlatformFormat = Format;
break;
}
}
}
if (!FallbackFormat)
{
UE_LOG(LogHMD, Warning, TEXT("No compatible swapchain format found!"));
return PF_Unknown;
}
UE_LOG(LogHMD, Warning, TEXT("Swapchain format not supported (%d), falling back to runtime preferred format (%d)."), PlatformFormat, FallbackPlatformFormat);
return FallbackFormat;
}
XrSwapchain FOpenXRSwapchain::CreateSwapchain(XrSession InSession, uint32 PlatformFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, void* Next /*= nullptr*/)
{
XrSwapchainUsageFlags Usage = 0;
if (!(CreateFlags & TexCreate_SRGB))
{
// On Windows both sRGB and non-sRGB integer formats have gamma correction, so since we
// do gamma correction ourselves in the post-processor we allocate a non-SRGB format.
// On OpenXR non-sRGB formats are assumed to be linear without gamma correction,
// so we always allocate an sRGB swapchain format. Thus we need to specify the
// mutable flag so we can output gamma corrected colors into an sRGB swapchain without
// the implicit gamma correction. On mobile platforms the TexCreate_SRGB flag is specified
// which indicates the post-processor is disabled and we do need implicit gamma correction.
// We skip setting this flag on those platforms as it would incur a large performance hit.
Usage |= XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT;
}
if (EnumHasAnyFlags(CreateFlags, TexCreate_RenderTargetable))
{
Usage |= XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
}
if (EnumHasAnyFlags(CreateFlags, TexCreate_DepthStencilTargetable))
{
Usage |= XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
if (EnumHasAnyFlags(CreateFlags, TexCreate_ShaderResource))
{
Usage |= XR_SWAPCHAIN_USAGE_SAMPLED_BIT;
}
if (EnumHasAnyFlags(CreateFlags, TexCreate_UAV))
{
Usage |= XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT;
}
XrSwapchain Swapchain;
XrSwapchainCreateInfo info;
info.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
info.next = Next;
info.createFlags = EnumHasAnyFlags(CreateFlags, TexCreate_Dynamic) ? 0 : XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;
info.usageFlags = Usage;
info.format = PlatformFormat;
info.sampleCount = NumSamples;
info.width = SizeX;
info.height = SizeY;
info.faceCount = 1;
info.arraySize = ArraySize;
info.mipCount = NumMips;
if (!XR_ENSURE(xrCreateSwapchain(InSession, &info, &Swapchain)))
{
return XR_NULL_HANDLE;
}
return Swapchain;
}
// TexCreate_Dynamic flag is being used in the function above to determine whether to set XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT. Originally,
// the Dynamic flag was non-functional in RHI, but now is used to specify whether a texture is expected to be frequently updated by the CPU.
// This flag doesn't make sense for textures created in this file, which aren't updated by the CPU, but external client code still needs to
// set the flag, so we sanitize the flag out of any RHI textures we create.
//
// TODO: In the future, it would be cleaner to specify the static swapchain flag separately, rather than overloading TexCreate_Dynamic, but
// that requires deprecating public APIs. And there's a risk of clients leaving the flag set anyway when updating their code, so it
// seems reasonable to continue to remove the flag going forward.
static ETextureCreateFlags SanitizeTextureCreateFlags(ETextureCreateFlags Flags)
{
EnumRemoveFlags(Flags, TexCreate_Dynamic);
return Flags;
}
template<typename T>
TArray<T> EnumerateImages(XrSwapchain InSwapchain, XrStructureType InType, void* Next = nullptr)
{
TArray<T> Images;
uint32_t ChainCount;
xrEnumerateSwapchainImages(InSwapchain, 0, &ChainCount, nullptr);
Images.AddZeroed(ChainCount);
for (auto& Image : Images)
{
Image.type = InType;
Image.next = Next;
}
XR_ENSURE(xrEnumerateSwapchainImages(InSwapchain, ChainCount, &ChainCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(Images.GetData())));
return Images;
}
void FOpenXRSwapchain::GetFragmentDensityMaps(TArray<FTextureRHIRef>& OutTextureChain, const bool bIsMobileMultiViewEnabled)
{
#ifdef XR_USE_GRAPHICS_API_VULKAN
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
XrSwapchainImageFoveationVulkanFB FoveationImage{ XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB };
FoveationImage.next = nullptr;
void* Next = &FoveationImage;
XrSwapchain Swapchain = GetHandle();
TArray<XrSwapchainImageVulkanKHR> Images = EnumerateImages<XrSwapchainImageVulkanKHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR, Next);
if(!Images.IsEmpty())
{
OutTextureChain.Reset(Images.Num());
}
for (const XrSwapchainImageVulkanKHR& Image : Images)
{
const XrBaseOutStructure* NextHeader = reinterpret_cast<const XrBaseOutStructure*>(Image.next);
const XrSwapchainImageFoveationVulkanFB* FoveationImageVulkan = reinterpret_cast<const XrSwapchainImageFoveationVulkanFB*>(NextHeader);
OutTextureChain.Add(static_cast<FTextureRHIRef>(bIsMobileMultiViewEnabled ?
VulkanRHI->RHICreateTexture2DArrayFromResource(GPixelFormats[PF_R8G8].UnrealFormat, FoveationImageVulkan->width, FoveationImageVulkan->height, 2, 1, 1, FoveationImageVulkan->image, TexCreate_Foveation, FClearValueBinding::White) :
VulkanRHI->RHICreateTexture2DFromResource(GPixelFormats[PF_R8G8].UnrealFormat, FoveationImageVulkan->width, FoveationImageVulkan->height, 1, 1, FoveationImageVulkan->image, TexCreate_Foveation, FClearValueBinding::White)
));
}
#else
OutTextureChain.Empty();
#endif
}
#ifdef XR_USE_GRAPHICS_API_D3D11
FXRSwapChainPtr CreateSwapchain_D3D11(XrSession InSession, uint8 Format, uint8& OutActualFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, const FClearValueBinding& ClearValueBinding, ETextureCreateFlags AuxiliaryCreateFlags)
{
TFunction<uint32(uint8)> ToPlatformFormat = [](uint8 InFormat)
{
return GetID3D11DynamicRHI()->RHIGetSwapChainFormat(static_cast<EPixelFormat>(InFormat));
};
Format = FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(InSession, Format, ToPlatformFormat);
if (!Format)
{
return nullptr;
}
OutActualFormat = Format;
XrSwapchain Swapchain = FOpenXRSwapchain::CreateSwapchain(InSession, ToPlatformFormat(Format), SizeX, SizeY, ArraySize, NumMips, NumSamples, CreateFlags);
if (!Swapchain)
{
return nullptr;
}
ID3D11DynamicRHI* D3D11RHI = GetID3D11DynamicRHI();
TArray<FTextureRHIRef> TextureChain;
TArray<XrSwapchainImageD3D11KHR> Images = EnumerateImages<XrSwapchainImageD3D11KHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR);
for (const auto& Image : Images)
{
TextureChain.Add(static_cast<FTextureRHIRef>((ArraySize > 1) ?
D3D11RHI->RHICreateTexture2DArrayFromResource(GPixelFormats[Format].UnrealFormat, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding, Image.texture) :
D3D11RHI->RHICreateTexture2DFromResource(GPixelFormats[Format].UnrealFormat, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding, Image.texture)
));
}
return CreateXRSwapChain<FOpenXRSwapchain>(MoveTemp(TextureChain), (FTextureRHIRef&)TextureChain[0], Swapchain);
}
#endif
#ifdef XR_USE_GRAPHICS_API_D3D12
FXRSwapChainPtr CreateSwapchain_D3D12(XrSession InSession, uint8 Format, uint8& OutActualFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, const FClearValueBinding& ClearValueBinding, ETextureCreateFlags AuxiliaryCreateFlags)
{
TFunction<uint32(uint8)> ToPlatformFormat = [](uint8 InFormat)
{
return GetID3D12DynamicRHI()->RHIGetSwapChainFormat(static_cast<EPixelFormat>(InFormat));
};
Format = FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(InSession, Format, ToPlatformFormat);
if (!Format)
{
return nullptr;
}
OutActualFormat = Format;
XrSwapchain Swapchain = FOpenXRSwapchain::CreateSwapchain(InSession, ToPlatformFormat(Format), SizeX, SizeY, ArraySize, NumMips, NumSamples, CreateFlags);
if (!Swapchain)
{
return nullptr;
}
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
TArray<FTextureRHIRef> TextureChain;
TArray<XrSwapchainImageD3D12KHR> Images = EnumerateImages<XrSwapchainImageD3D12KHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR);
for (const auto& Image : Images)
{
TextureChain.Add(static_cast<FTextureRHIRef>((ArraySize > 1) ?
DynamicRHI->RHICreateTexture2DArrayFromResource(GPixelFormats[Format].UnrealFormat, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding, Image.texture) :
DynamicRHI->RHICreateTexture2DFromResource(GPixelFormats[Format].UnrealFormat, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding, Image.texture)
));
}
return CreateXRSwapChain<FOpenXRSwapchain>(MoveTemp(TextureChain), (FTextureRHIRef&)TextureChain[0], Swapchain);
}
#endif
#ifdef XR_USE_GRAPHICS_API_OPENGL
FXRSwapChainPtr CreateSwapchain_OpenGL(XrSession InSession, uint8 Format, uint8& OutActualFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, const FClearValueBinding& ClearValueBinding, ETextureCreateFlags AuxiliaryCreateFlags)
{
Format = FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(InSession, Format);
if (!Format)
{
return nullptr;
}
OutActualFormat = Format;
XrSwapchain Swapchain = FOpenXRSwapchain::CreateSwapchain(InSession, GPixelFormats[Format].PlatformFormat, SizeX, SizeY, ArraySize, NumMips, NumSamples, CreateFlags);
if (!Swapchain)
{
return nullptr;
}
TArray<FTextureRHIRef> TextureChain;
IOpenGLDynamicRHI* DynamicRHI = GetIOpenGLDynamicRHI();
TArray<XrSwapchainImageOpenGLKHR> Images = EnumerateImages<XrSwapchainImageOpenGLKHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR);
for (const auto& Image : Images)
{
FTextureRHIRef NewTexture = (ArraySize > 1) ?
DynamicRHI->RHICreateTexture2DArrayFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, ArraySize, NumMips, NumSamples, 1, ClearValueBinding, Image.image, SanitizeTextureCreateFlags(CreateFlags)) :
DynamicRHI->RHICreateTexture2DFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, NumMips, NumSamples, 1, ClearValueBinding, Image.image, SanitizeTextureCreateFlags(CreateFlags));
TextureChain.Add(NewTexture.GetReference());
}
return CreateXRSwapChain<FOpenXRSwapchain>(MoveTemp(TextureChain), (FTextureRHIRef&)TextureChain[0], Swapchain);
}
#endif
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
FXRSwapChainPtr CreateSwapchain_OpenGLES(XrSession InSession, uint8 Format, uint8& OutActualFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, const FClearValueBinding& ClearValueBinding, ETextureCreateFlags AuxiliaryCreateFlags)
{
Format = FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(InSession, Format);
if (!Format)
{
return nullptr;
}
OutActualFormat = Format;
void* Next = nullptr;
XrSwapchainCreateInfoFoveationFB FoveationCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB };
if (EnumHasAllFlags(AuxiliaryCreateFlags, TexCreate_Foveation))
{
FoveationCreateInfo.next = Next;
FoveationCreateInfo.flags = XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB;
Next = &FoveationCreateInfo;
}
XrSwapchain Swapchain = FOpenXRSwapchain::CreateSwapchain(InSession, GPixelFormats[Format].PlatformFormat, SizeX, SizeY, ArraySize, NumMips, NumSamples, CreateFlags, Next);
if (!Swapchain)
{
return nullptr;
}
TArray<FTextureRHIRef> TextureChain;
IOpenGLDynamicRHI* DynamicRHI = GetIOpenGLDynamicRHI();
TArray<XrSwapchainImageOpenGLESKHR> Images = EnumerateImages<XrSwapchainImageOpenGLESKHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR);
for (const auto& Image : Images)
{
FTextureRHIRef NewTexture = (ArraySize > 1) ?
DynamicRHI->RHICreateTexture2DArrayFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, ArraySize, NumMips, NumSamples, 1, ClearValueBinding, Image.image, SanitizeTextureCreateFlags(CreateFlags)) :
DynamicRHI->RHICreateTexture2DFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, NumMips, NumSamples, 1, ClearValueBinding, Image.image, SanitizeTextureCreateFlags(CreateFlags));
TextureChain.Add(NewTexture.GetReference());
}
return CreateXRSwapChain<FOpenXRSwapchain>(MoveTemp(TextureChain), (FTextureRHIRef&)TextureChain[0], Swapchain);
}
#endif
#ifdef XR_USE_GRAPHICS_API_VULKAN
FXRSwapChainPtr CreateSwapchain_Vulkan(XrSession InSession, uint8 Format, uint8& OutActualFormat, uint32 SizeX, uint32 SizeY, uint32 ArraySize, uint32 NumMips, uint32 NumSamples, ETextureCreateFlags CreateFlags, const FClearValueBinding& ClearValueBinding, ETextureCreateFlags AuxiliaryCreateFlags)
{
TFunction<uint32(uint8)> ToPlatformFormat = [](uint8 InFormat)
{
// UE renders a gamma-corrected image so we need to use an sRGB format if available
return GetIVulkanDynamicRHI()->RHIGetSwapChainVkFormat(static_cast<EPixelFormat>(InFormat));
};
Format = FOpenXRSwapchain::GetNearestSupportedSwapchainFormat(InSession, Format, ToPlatformFormat);
if (!Format)
{
return nullptr;
}
OutActualFormat = Format;
// When we specify the mutable format flag we want to inform the runtime that we'll only use a
// linear and an sRGB view format to allow for efficiency optimizations.
TArray<VkFormat> ViewFormatList;
ViewFormatList.Add((VkFormat)ToPlatformFormat(Format)); // sRGB format
ViewFormatList.Add((VkFormat)GPixelFormats[Format].PlatformFormat); // linear format
XrVulkanSwapchainFormatListCreateInfoKHR FormatListInfo = { XR_TYPE_VULKAN_SWAPCHAIN_FORMAT_LIST_CREATE_INFO_KHR };
FormatListInfo.viewFormatCount = ViewFormatList.Num();
FormatListInfo.viewFormats = ViewFormatList.GetData();
// OpenXR wants to create sRGB swapchains. When the swapchain that is being created does not conform to this,
// we need to tell OpenXR what formats the swapchain will be accessed in.
// The XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT will also be set in FOpenXRSwapchain::CreateSwapchain.
// We don't need to specify additional formats if the swapchain is being created as sRGB.
void* Next = !(CreateFlags & TexCreate_SRGB) ? &FormatListInfo : nullptr;
XrSwapchainCreateInfoFoveationFB FoveationCreateInfo { XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB };
if (EnumHasAllFlags(AuxiliaryCreateFlags, TexCreate_Foveation))
{
FoveationCreateInfo.next = Next;
FoveationCreateInfo.flags = XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB;
Next = &FoveationCreateInfo;
}
XrSwapchain Swapchain = FOpenXRSwapchain::CreateSwapchain(InSession, ViewFormatList[0], SizeX, SizeY, ArraySize, NumMips, NumSamples, CreateFlags, Next);
if (!Swapchain)
{
return nullptr;
}
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
TArray<FTextureRHIRef> TextureChain;
TArray<XrSwapchainImageVulkanKHR> Images = EnumerateImages<XrSwapchainImageVulkanKHR>(Swapchain, XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR);
for (const auto& Image : Images)
{
TextureChain.Add(static_cast<FTextureRHIRef>((ArraySize > 1) ?
VulkanRHI->RHICreateTexture2DArrayFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, ArraySize, NumMips, NumSamples, Image.image, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding) :
VulkanRHI->RHICreateTexture2DFromResource(GPixelFormats[Format].UnrealFormat, SizeX, SizeY, NumMips, NumSamples, Image.image, SanitizeTextureCreateFlags(CreateFlags), ClearValueBinding)
));
}
return CreateXRSwapChain<FOpenXRSwapchain>(MoveTemp(TextureChain), (FTextureRHIRef&)TextureChain[0], Swapchain);
}
#endif