// Copyright Epic Games, Inc. All Rights Reserved. #include "StereoRenderUtils.h" #include "Misc/App.h" #include "ShaderPlatformCachedIniValue.h" #include "DataDrivenShaderPlatformInfo.h" #include "RHI.h" #include "RenderUtils.h" // enable this to printf-debug stereo rendering aspects on a device #define UE_DEBUG_STEREO_ASPECTS (!UE_BUILD_SHIPPING && !UE_BUILD_TEST && !UE_EDITOR) #define UE_SUPPORT_MMV_FALLBACK 0 #if UE_DEBUG_STEREO_ASPECTS #define UE_DEBUG_SSA_LOG_INIT(Platform) \ bool bSSALogEnable = false; \ { \ static EShaderPlatform LastLogged[2] = { SP_NumPlatforms, SP_NumPlatforms }; \ if (UNLIKELY(LastLogged[0] != Platform && LastLogged[1] != Platform)) \ { \ LastLogged[1] = LastLogged[0]; \ LastLogged[0] = Platform; \ bSSALogEnable = FApp::CanEverRender(); \ } \ } #define UE_DEBUG_SSA_LOG(Verbosity, Format, ...) \ { \ if (UNLIKELY(bSSALogEnable)) \ { \ UE_LOG(LogInit, Verbosity, TEXT("FStereoShaderAspects: %s"), *FString::Printf(Format, ##__VA_ARGS__)); \ } \ } #else #define UE_DEBUG_SSA_LOG_INIT(Platform) #define UE_DEBUG_SSA_LOG(Verbosity, Format, ...) #endif #define UE_DEBUG_SSA_LOG_BOOL(Boolean) \ { \ UE_DEBUG_SSA_LOG(Log, TEXT(#Boolean) TEXT(" = %d"), Boolean); \ } namespace UE::StereoRenderUtils { RENDERCORE_API FStereoShaderAspects::FStereoShaderAspects(EShaderPlatform Platform) : bInstancedStereoEnabled(false) , bMobileMultiViewEnabled(false) , bInstancedMultiViewportEnabled(false) , bInstancedStereoNative(false) , bMobileMultiViewNative(false) , bMobileMultiViewFallback(false) { check(Platform < EShaderPlatform::SP_NumPlatforms); UE_DEBUG_SSA_LOG_INIT(Platform); // Would be nice to use URendererSettings, but not accessible in RenderCore static FShaderPlatformCachedIniValue CVarInstancedStereo(TEXT("vr.InstancedStereo")); static FShaderPlatformCachedIniValue CVarMobileMultiView(TEXT("vr.MobileMultiView")); const bool bInstancedStereo = CVarInstancedStereo.Get(Platform); const bool bMobilePlatform = IsMobilePlatform(Platform); const bool bMobilePostprocessing = IsMobileHDR(); const bool bMobileMultiView = CVarMobileMultiView.Get(Platform); // If we're in a non-rendering run (cooker, DDC commandlet, anything with -nullrhi), don't check GRHI* setting, as it reflects runtime RHI capabilities. const bool bMultiViewportCapable = (GRHISupportsArrayIndexFromAnyShader || !FApp::CanEverRender()) && RHISupportsMultiViewport(Platform); bInstancedStereoNative = !bMobilePlatform && bInstancedStereo && RHISupportsInstancedStereo(Platform); UE_DEBUG_SSA_LOG(Log, TEXT("--- StereoAspects begin ---")); UE_DEBUG_SSA_LOG(Log, TEXT("Platform=%s (%d)"), *LexToString(Platform), static_cast(Platform)); UE_DEBUG_SSA_LOG_BOOL(bInstancedStereo); UE_DEBUG_SSA_LOG_BOOL(bMobilePlatform); UE_DEBUG_SSA_LOG_BOOL(bMobilePostprocessing); UE_DEBUG_SSA_LOG_BOOL(bMobileMultiView); UE_DEBUG_SSA_LOG_BOOL(bMultiViewportCapable); UE_DEBUG_SSA_LOG_BOOL(bInstancedStereoNative); UE_DEBUG_SSA_LOG(Log, TEXT("---")); const bool bMobileMultiViewCoreSupport = bMobilePlatform && bMobileMultiView; if (bMobileMultiViewCoreSupport) { UE_DEBUG_SSA_LOG(Log, TEXT("RHISupportsMobileMultiView(%s) = %d."), *LexToString(Platform), RHISupportsMobileMultiView(Platform)); UE_DEBUG_SSA_LOG(Log, TEXT("RHISupportsInstancedStereo(%s) = %d."), *LexToString(Platform), RHISupportsInstancedStereo(Platform)); if (RHISupportsMobileMultiView(Platform)) { bMobileMultiViewNative = true; } else if (RHISupportsInstancedStereo(Platform)) { // ISR in mobile shaders is achieved via layered RTs and eye-dependent layer index, so unlike on desktop, it does not depend on multi-viewport // If we're in a non-rendering run (cooker, DDC commandlet, anything with -nullrhi), don't check GRHI* setting, as it reflects runtime RHI capabilities. const bool bVertexShaderLayerCapable = (GRHISupportsArrayIndexFromAnyShader || !FApp::CanEverRender()) && RHISupportsVertexShaderLayer(Platform); UE_DEBUG_SSA_LOG_BOOL(bVertexShaderLayerCapable); if (bVertexShaderLayerCapable) { // Even if MMV fallback support is disabled, we still set it to true internally to log a deprecation warning bMobileMultiViewFallback = true; } } } UE_DEBUG_SSA_LOG_BOOL(bMobileMultiViewCoreSupport); UE_DEBUG_SSA_LOG_BOOL(bMobileMultiViewNative); UE_DEBUG_SSA_LOG_BOOL(bMobileMultiViewFallback); UE_DEBUG_SSA_LOG(Log, TEXT("---")); // "instanced stereo" is confusingly used to refer to two two modes: // 1) regular aka "native" ISR, where the views are selected via SV_ViewportArrayIndex - uses non-mobile shaders // 2) "mobile multiview fallback" ISR, which writes to a texture layer via SV_RenderTargetArrayIndex - uses mobile shaders // IsInstancedStereoEnabled() will be true in both cases bInstancedMultiViewportEnabled = bInstancedStereoNative && bMultiViewportCapable; // Since instanced stereo now relies on multi-viewport capability, it cannot be separately enabled from it. bInstancedStereoEnabled = bInstancedStereoNative; bMobileMultiViewEnabled = bMobileMultiViewNative; // MMV fallback is deprecated as of UE 5.6 #if UE_SUPPORT_MMV_FALLBACK bInstancedStereoEnabled |= bMobileMultiViewFallback; bMobileMultiViewEnabled |= bMobileMultiViewFallback; #endif UE_DEBUG_SSA_LOG_BOOL(bInstancedMultiViewportEnabled); UE_DEBUG_SSA_LOG_BOOL(bInstancedStereoEnabled); UE_DEBUG_SSA_LOG_BOOL(bMobileMultiViewEnabled); UE_DEBUG_SSA_LOG(Log, TEXT("--- StereoAspects end ---")); // check the following invariants checkf(!bMobileMultiViewNative || !bInstancedStereoEnabled, TEXT("When a platform supports MMV natively, ISR should not be enabled.")); #if UE_SUPPORT_MMV_FALLBACK checkf(!(bMobileMultiViewEnabled && bInstancedStereoEnabled) || bMobileMultiViewFallback, TEXT("If a platform uses MMV fallback, both MMV and ISR should be enabled.")); checkf(!bInstancedStereoEnabled || (bInstancedMultiViewportEnabled || bMobileMultiViewFallback), TEXT("If ISR is enabled, we need either multi-viewport (since we no longer support clip-distance method) or MMV fallback (which uses vertex layer).")); #else checkf(!bInstancedStereoEnabled || bInstancedMultiViewportEnabled, TEXT("If ISR is enabled, we need either multi-viewport (since we no longer support clip-distance method).")); #endif } RENDERCORE_API FStereoShaderAspects::FStereoShaderAspects() : bInstancedStereoEnabled(false) , bMobileMultiViewEnabled(false) , bInstancedMultiViewportEnabled(false) , bInstancedStereoNative(false) , bMobileMultiViewNative(false) , bMobileMultiViewFallback(false) {} RENDERCORE_API void LogISRInit(const UE::StereoRenderUtils::FStereoShaderAspects& Aspects) { UE_LOG(LogInit, Log, TEXT("XR: Instanced Stereo Rendering is %s"), (Aspects.IsInstancedStereoEnabled() ? TEXT("Enabled") : TEXT("Disabled"))); UE_LOG(LogInit, Log, TEXT("XR: MultiViewport is %s"), (Aspects.IsInstancedMultiViewportEnabled() ? TEXT("Enabled") : TEXT("Disabled"))); UE_LOG(LogInit, Log, TEXT("XR: Mobile Multiview is %s"), (Aspects.IsMobileMultiViewEnabled() ? TEXT("Enabled") : TEXT("Disabled"))); PRAGMA_DISABLE_DEPRECATION_WARNINGS if (Aspects.IsMobileMultiViewFallbackEnabled()) { #if UE_SUPPORT_MMV_FALLBACK UE_LOG(LogInit, Log, TEXT("XR: Using MMV fallback (instanced stereo + mobile shaders via layered RTs).")) #else UE_LOG(LogInit, Warning, TEXT("XR: Using mobile shader platform and vr.MobileMultiView=1, but MMV is not supported by the RHI. Falling back to view-by-view rendering. Try using Vulkan editor for native MMV support on desktop.")) #endif } PRAGMA_ENABLE_DEPRECATION_WARNINGS } RENDERCORE_API void VerifyISRConfig(const UE::StereoRenderUtils::FStereoShaderAspects& Aspects, EShaderPlatform ShaderPlatform) { // If instanced stereo is enabled, we should also have either multiviewport enabled, or mmv fallback enabled. // Otherwise, exit gracefully with a message box if (Aspects.IsInstancedStereoEnabled() && !Aspects.IsInstancedMultiViewportEnabled() && !Aspects.IsMobileMultiViewEnabled()) { UE_LOG(LogInit, Log, TEXT("ShaderPlatform=%d RHISupportsInstancedStereo()=%d GRHISupportsArrayIndexFromAnyShader=%d"), ShaderPlatform, RHISupportsMultiViewport(ShaderPlatform), GRHISupportsArrayIndexFromAnyShader); // MessageBoxExt may not yet handle unattended runs itself (see UE-165694), so special-case here to avoid getting stuck in a commandlet with rendering enabled const FText MessageText = NSLOCTEXT("InstancedStereo", "UnableToUseInstancedStereoRenderingText", "Cannot render an Instanced Stereo-enabled project due to a missing functionality on the system. Please check log files for more info."); if (!FApp::IsUnattended()) { FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *MessageText.ToString(), *NSLOCTEXT("InstancedStereo", "UnableToUseInstancedStereoRendering", "Unable to use Instanced Stereo Rendering.").ToString()); } else { UE_LOG(LogInit, Error, TEXT("%s"), *MessageText.ToString()); } FPlatformMisc::RequestExitWithStatus(true, 1); // unreachable } } } // namespace UE::StereoRenderUtils