// @lint-ignore-every LICENSELINT // Copyright Epic Games, Inc. All Rights Reserved. #include "OculusXRHMD_CustomPresent.h" #if OCULUS_HMD_SUPPORTED_PLATFORMS #include "OculusXRHMD.h" #include "ScreenRendering.h" #include "PipelineStateCache.h" #include "ClearQuad.h" #include "OculusShaders.h" #include "CommonRenderResources.h" #include "RHIStaticStates.h" #if PLATFORM_ANDROID #include "Android/AndroidJNI.h" #include "Android/AndroidEGL.h" #include "Android/AndroidApplication.h" #include "Android/AndroidPlatformMisc.h" #endif #define VULKAN_CUBEMAP_POSITIVE_Y 2 #define VULKAN_CUBEMAP_NEGATIVE_Y 3 namespace OculusXRHMD { /** * A pixel shader for rendering a textured screen element with mip maps and array slice. */ class FScreenPSMipLevelArray : public FGlobalShader { DECLARE_SHADER_TYPE(FScreenPSMipLevelArray, Global); public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; } FScreenPSMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory); InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler")); InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel")); InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice")); } FScreenPSMipLevelArray() {} void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel) { SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture); SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel); SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice); } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel) { SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI); SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel); SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice); } private: LAYOUT_FIELD(FShaderResourceParameter, InTexture); LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler); LAYOUT_FIELD(FShaderParameter, InMipLevelParameter); LAYOUT_FIELD(FShaderParameter, InArraySliceParameter); }; IMPLEMENT_SHADER_TYPE(, FScreenPSMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainMipLevel"), SF_Pixel); /** * A pixel shader for rendering a textured screen element with mip maps and array slice. */ class FScreenPSsRGBSourceMipLevelArray : public FGlobalShader { DECLARE_SHADER_TYPE(FScreenPSsRGBSourceMipLevelArray, Global); public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; } FScreenPSsRGBSourceMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory); InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler")); InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel")); InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice")); } FScreenPSsRGBSourceMipLevelArray() {} void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel) { SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture); SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel); SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice); } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel) { SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI); SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel); SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice); } private: LAYOUT_FIELD(FShaderResourceParameter, InTexture); LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler); LAYOUT_FIELD(FShaderParameter, InMipLevelParameter); LAYOUT_FIELD(FShaderParameter, InArraySliceParameter); }; IMPLEMENT_SHADER_TYPE(, FScreenPSsRGBSourceMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainsRGBSourceMipLevel"), SF_Pixel); //------------------------------------------------------------------------------------------------- // FCustomPresent //------------------------------------------------------------------------------------------------- FCustomPresent::FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool bInSupportsSRGB) : OculusXRHMD(InOculusXRHMD) , RenderAPI(InRenderAPI) , DefaultPixelFormat(InDefaultPixelFormat) , bSupportsSRGB(bInSupportsSRGB) , bSupportsSubsampled(false) , bIsStandaloneStereoDevice(false) { CheckInGameThread(); DefaultOvrpTextureFormat = GetOvrpTextureFormat(GetDefaultPixelFormat()); DefaultDepthOvrpTextureFormat = ovrpTextureFormat_None; #if PLATFORM_ANDROID bIsStandaloneStereoDevice = FAndroidMisc::GetDeviceMake() == FString("Oculus"); #endif // grab a pointer to the renderer module for displaying our mirror window static const FName RendererModuleName("Renderer"); RendererModule = FModuleManager::GetModulePtr(RendererModuleName); } void FCustomPresent::ReleaseResources_RHIThread() { CheckInRHIThread(); if (MirrorTextureRHI.IsValid()) { FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2(); MirrorTextureRHI = nullptr; } } void FCustomPresent::Shutdown() { CheckInGameThread(); // OculusXRHMD is going away, but this object can live on until viewport is destroyed ExecuteOnRenderThread([this]() { ExecuteOnRHIThread([this]() { OculusXRHMD = nullptr; }); }); } bool FCustomPresent::NeedsNativePresent() { return !bIsStandaloneStereoDevice; } bool FCustomPresent::Present(int32& SyncInterval) { CheckInRHIThread(); if (OculusXRHMD) { FGameFrame* Frame_RHIThread = OculusXRHMD->GetFrame_RHIThread(); if (Frame_RHIThread) { FinishRendering_RHIThread(); } } SyncInterval = 0; // VSync off return NeedsNativePresent(); } void FCustomPresent::UpdateMirrorTexture_RenderThread() { SCOPE_CYCLE_COUNTER(STAT_BeginRendering); CheckInRenderThread(); const ESpectatorScreenMode MirrorWindowMode = OculusXRHMD->GetSpectatorScreenMode_RenderThread(); const FIntPoint MirrorWindowSize = OculusXRHMD->GetFrame_RenderThread()->WindowSize; if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized()) { // Need to destroy mirror texture? if (MirrorTextureRHI.IsValid()) { const auto MirrorTextureSize = FIntPoint(MirrorTextureRHI->GetDesc().Extent.X, MirrorTextureRHI->GetDesc().Extent.Y); if (MirrorWindowMode != ESpectatorScreenMode::Distorted || MirrorWindowSize != MirrorTextureSize) { ExecuteOnRHIThread([]() { FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2(); }); MirrorTextureRHI = nullptr; } } // Need to create mirror texture? if (!MirrorTextureRHI.IsValid() && MirrorWindowMode == ESpectatorScreenMode::Distorted && MirrorWindowSize.X != 0 && MirrorWindowSize.Y != 0) { const int Width = MirrorWindowSize.X; const int Height = MirrorWindowSize.Y; ovrpTextureHandle TextureHandle; ExecuteOnRHIThread([&]() { FOculusXRHMDModule::GetPluginWrapper().SetupMirrorTexture2(GetOvrpDevice(), Height, Width, GetDefaultOvrpTextureFormat(), &TextureHandle); }); UE_LOG(LogHMD, Log, TEXT("Allocated a new mirror texture (size %d x %d)"), Width, Height); ETextureCreateFlags TexCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable; MirrorTextureRHI = CreateTexture_RenderThread(Width, Height, GetDefaultPixelFormat(), FClearValueBinding::None, 1, 1, 1, RRT_Texture2D, TextureHandle, TexCreateFlags)->GetTexture2D(); } } } void FCustomPresent::FinishRendering_RHIThread() { SCOPE_CYCLE_COUNTER(STAT_FinishRendering); CheckInRHIThread(); #if STATS if (OculusXRHMD->GetFrame_RHIThread()->ShowFlags.Rendering) { ovrpAppLatencyTimings AppLatencyTimings; if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppLatencyTimings2(&AppLatencyTimings))) { SET_FLOAT_STAT(STAT_LatencyRender, AppLatencyTimings.LatencyRender * 1000.0f); SET_FLOAT_STAT(STAT_LatencyTimewarp, AppLatencyTimings.LatencyTimewarp * 1000.0f); SET_FLOAT_STAT(STAT_LatencyPostPresent, AppLatencyTimings.LatencyPostPresent * 1000.0f); SET_FLOAT_STAT(STAT_ErrorRender, AppLatencyTimings.ErrorRender * 1000.0f); SET_FLOAT_STAT(STAT_ErrorTimewarp, AppLatencyTimings.ErrorTimewarp * 1000.0f); } } #endif OculusXRHMD->FinishRHIFrame_RHIThread(); #if PLATFORM_ANDROID float GPUFrameTime = 0.0f; if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetGPUFrameTime(&GPUFrameTime))) { SubmitGPUFrameTime(GPUFrameTime); } #endif } EPixelFormat FCustomPresent::GetPixelFormat(EPixelFormat Format) const { switch (Format) { // case PF_B8G8R8A8: case PF_FloatRGBA: case PF_FloatR11G11B10: // case PF_R8G8B8A8: case PF_G16: case PF_R16F: case PF_R32_FLOAT: case PF_ShadowDepth: case PF_D24: return Format; } return GetDefaultPixelFormat(); } EPixelFormat FCustomPresent::GetPixelFormat(ovrpTextureFormat Format) const { switch (Format) { // case ovrpTextureFormat_R8G8B8A8_sRGB: // case ovrpTextureFormat_R8G8B8A8: // return PF_R8G8B8A8; case ovrpTextureFormat_R16G16B16A16_FP: return PF_FloatRGBA; case ovrpTextureFormat_R11G11B10_FP: return PF_FloatR11G11B10; // case ovrpTextureFormat_B8G8R8A8_sRGB: // case ovrpTextureFormat_B8G8R8A8: // return PF_B8G8R8A8; case ovrpTextureFormat_R16: return PF_G16; // G stands for grey here, not green, and is actually R16 in RHI case ovrpTextureFormat_R16_FP: return PF_R16F; case ovrpTextureFormat_R32_FP: return PF_R32_FLOAT; case ovrpTextureFormat_D16: return PF_ShadowDepth; // ShadowDepth maps to D16 in Vulkan case ovrpTextureFormat_D24_S8: return PF_D24; } return GetDefaultPixelFormat(); } ovrpTextureFormat FCustomPresent::GetOvrpTextureFormat(EPixelFormat Format, bool usesRGB) const { switch (GetPixelFormat(Format)) { case PF_B8G8R8A8: return bSupportsSRGB && usesRGB ? ovrpTextureFormat_B8G8R8A8_sRGB : ovrpTextureFormat_B8G8R8A8; case PF_FloatRGBA: return ovrpTextureFormat_R16G16B16A16_FP; case PF_FloatR11G11B10: return ovrpTextureFormat_R11G11B10_FP; case PF_R8G8B8A8: return bSupportsSRGB && usesRGB ? ovrpTextureFormat_R8G8B8A8_sRGB : ovrpTextureFormat_R8G8B8A8; case PF_G16: return ovrpTextureFormat_R16; case PF_R16F: return ovrpTextureFormat_R16_FP; case PF_R32_FLOAT: return ovrpTextureFormat_R32_FP; case PF_ShadowDepth: return ovrpTextureFormat_D16; case PF_D24: return ovrpTextureFormat_D24_S8; } return ovrpTextureFormat_None; } bool FCustomPresent::IsSRGB(ovrpTextureFormat InFormat) { switch (InFormat) { case ovrpTextureFormat_B8G8R8A8_sRGB: case ovrpTextureFormat_R8G8B8A8_sRGB: return true; } return false; } int FCustomPresent::GetSystemRecommendedMSAALevel() const { int SystemRecommendedMSAALevel = 1; FOculusXRHMDModule::GetPluginWrapper().GetSystemRecommendedMSAALevel2(&SystemRecommendedMSAALevel); return SystemRecommendedMSAALevel; } FXRSwapChainPtr FCustomPresent::CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName) { TArray RHITextureSwapChain = CreateSwapChainTextures_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures, InTexCreateFlags, DebugName); FTextureRHIRef RHITexture = GDynamicRHI->RHICreateAliasedTexture(RHITextureSwapChain[0]); return CreateXRSwapChain(MoveTemp(RHITextureSwapChain), RHITexture); } TArray FCustomPresent::CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName) { CheckInRenderThread(); TArray RHITextureSwapChain; { for (int32 TextureIndex = 0; TextureIndex < InTextures.Num(); ++TextureIndex) { FTextureRHIRef TexRef = CreateTexture_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures[TextureIndex], InTexCreateFlags); FString TexName = FString::Printf(TEXT("%s (%d/%d)"), DebugName, TextureIndex, InTextures.Num()); TexRef->SetName(*TexName); RHIBindDebugLabelName(TexRef, *TexName); RHITextureSwapChain.Add(TexRef); } } return RHITextureSwapChain; } void FCustomPresent::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture, FIntRect DstRect, FIntRect SrcRect, bool bAlphaPremultiply, bool bNoAlphaWrite, bool bInvertY, bool sRGBSource, bool bInvertAlpha) const { CheckInRenderThread(); FIntPoint DstSize; FIntPoint SrcSize; if (DstTexture->GetDesc().IsTexture2D() && SrcTexture->GetDesc().IsTexture2D()) { DstSize = FIntPoint(DstTexture->GetSizeX(), DstTexture->GetSizeY()); SrcSize = FIntPoint(SrcTexture->GetSizeX(), SrcTexture->GetSizeY()); } else if (DstTexture->GetDesc().IsTextureCube() && SrcTexture->GetDesc().IsTextureCube()) { DstSize = FIntPoint(DstTexture->GetSize(), DstTexture->GetSize()); SrcSize = FIntPoint(SrcTexture->GetSize(), SrcTexture->GetSize()); } else { return; } if (DstRect.IsEmpty()) { DstRect = FIntRect(FIntPoint::ZeroValue, DstSize); } if (SrcRect.IsEmpty()) { SrcRect = FIntRect(FIntPoint::ZeroValue, SrcSize); } const uint32 ViewportWidth = DstRect.Width(); const uint32 ViewportHeight = DstRect.Height(); const FIntPoint TargetSize(ViewportWidth, ViewportHeight); float U = SrcRect.Min.X / (float)SrcSize.X; float V = SrcRect.Min.Y / (float)SrcSize.Y; float USize = SrcRect.Width() / (float)SrcSize.X; float VSize = SrcRect.Height() / (float)SrcSize.Y; #if PLATFORM_ANDROID // on android, top-left isn't 0/0 but 1/0. if (bInvertY) { V = 1.0f - V; VSize = -VSize; } #endif FRHITexture* SrcTextureRHI = SrcTexture; RHICmdList.Transition(FRHITransitionInfo(SrcTextureRHI, ERHIAccess::Unknown, ERHIAccess::SRVGraphics)); FGraphicsPipelineStateInitializer GraphicsPSOInit; if (bInvertAlpha) { // write RGBA, RGB = src.rgb * 1 + dst.rgb * 0, A = src.a * 0 + dst.a * (1 - src.a) GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else if (bAlphaPremultiply) { if (bNoAlphaWrite) { // for quads, write RGB, RGB = src.rgb * 1 + dst.rgb * 0 GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { // for quads, write RGBA, RGB = src.rgb * src.a + dst.rgb * 0, A = src.a + dst.a * 0 GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } else { if (bNoAlphaWrite) { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { // for mirror window, write RGBA, RGB = src.rgb * src.a + dst.rgb * (1 - src.a), A = src.a * 1 + dst.a * (1 - src a) GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; const auto FeatureLevel = OculusXRHMD->GetSettings_RenderThread()->CurrentFeatureLevel; auto ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef VertexShader(ShaderMap); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); if (DstTexture->GetDesc().IsTexture2D()) { sRGBSource &= EnumHasAnyFlags(SrcTexture->GetFlags(), TexCreate_SRGB); // Need to copy over mip maps on Android since they are not generated like they are on PC #if PLATFORM_ANDROID uint32 NumMips = SrcTexture->GetNumMips(); #else uint32 NumMips = 1; #endif const bool bUseTexArrayShader = SrcTexture->GetDesc().IsTextureArray() && DstTexture->GetDesc().IsTextureArray(); const int32 SliceCount = bUseTexArrayShader ? FMath::Min(SrcTexture->GetDesc().ArraySize, DstTexture->GetDesc().ArraySize) : 1; for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++) { FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store); RPInfo.ColorRenderTargets[0].MipIndex = MipIndex; for (int32 SliceIndex = 0; SliceIndex < SliceCount; ++SliceIndex) { RPInfo.ColorRenderTargets[0].ArraySlice = SliceIndex; RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTexture")); { const uint32 MipViewportWidth = ViewportWidth >> MipIndex; const uint32 MipViewportHeight = ViewportHeight >> MipIndex; const FIntPoint MipTargetSize(MipViewportWidth, MipViewportHeight); if (bNoAlphaWrite || bInvertAlpha) { RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f); DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White); } RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState::GetRHI() : TStaticSamplerState::GetRHI(); if (!sRGBSource) { if (bUseTexArrayShader) { TShaderMapRef PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex); RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters); } else { TShaderMapRef PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); #if UE_VERSION_OLDER_THAN(5, 3, 0) PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex); #else FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex); RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters); #endif } } else { if (bUseTexArrayShader) { TShaderMapRef PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex); RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters); } else { TShaderMapRef PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); #if UE_VERSION_OLDER_THAN(5, 3, 0) PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex); #else FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex); RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters); #endif } } RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Min.X + MipViewportWidth, DstRect.Min.Y + MipViewportHeight, 1.0f); RendererModule->DrawRectangle( RHICmdList, 0, 0, MipViewportWidth, MipViewportHeight, U, V, USize, VSize, MipTargetSize, FIntPoint(1, 1), VertexShader, EDRF_Default); } RHICmdList.EndRenderPass(); } } } else { for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++) { FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store); // On Vulkan the positive and negative Y faces of the cubemap need to be flipped if (RenderAPI == ovrpRenderAPI_Vulkan) { int NewFaceIndex = 0; if (FaceIndex == VULKAN_CUBEMAP_POSITIVE_Y) NewFaceIndex = VULKAN_CUBEMAP_NEGATIVE_Y; else if (FaceIndex == VULKAN_CUBEMAP_NEGATIVE_Y) NewFaceIndex = VULKAN_CUBEMAP_POSITIVE_Y; else NewFaceIndex = FaceIndex; RPInfo.ColorRenderTargets[0].ArraySlice = NewFaceIndex; } else { RPInfo.ColorRenderTargets[0].ArraySlice = FaceIndex; } RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTextureFace")); { if (bNoAlphaWrite) { DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White); } RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); TShaderMapRef PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState::GetRHI() : TStaticSamplerState::GetRHI(); #if UE_VERSION_OLDER_THAN(5, 3, 0) PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, FaceIndex); #else FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters(); PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, FaceIndex); RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters); #endif RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f); RendererModule->DrawRectangle( RHICmdList, 0, 0, ViewportWidth, ViewportHeight, #if PLATFORM_ANDROID U, V, USize, VSize, #else U, 1.0 - V, USize, -VSize, #endif TargetSize, FIntPoint(1, 1), VertexShader, EDRF_Default); } RHICmdList.EndRenderPass(); } } } void FCustomPresent::SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList) { CheckInRenderThread(); RHICmdList.SubmitCommandsHint(); } } // namespace OculusXRHMD #endif //OCULUS_HMD_SUPPORTED_PLATFORMS