diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp
index 3bb94f2f4b0a..b2b246851060 100644
--- a/Common/VR/PPSSPPVR.cpp
+++ b/Common/VR/PPSSPPVR.cpp
@@ -162,6 +162,7 @@ void InitVROnAndroid(void* vm, void* activity, const char* system, int version,
} else if ((strcmp(vendor, "META") == 0) || (strcmp(vendor, "OCULUS") == 0)) {
VR_SetPlatformFLag(VR_PLATFORM_CONTROLLER_QUEST, true);
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_FOVEATION, true);
+ VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_PASSTHROUGH, true);
VR_SetPlatformFLag(VR_PLATFORM_EXTENSION_PERFORMANCE, true);
}
VR_SetPlatformFLag(VR_PLATFORM_RENDERER_VULKAN, (GPUBackend)g_Config.iGPUBackend == GPUBackend::VULKAN);
@@ -792,6 +793,7 @@ bool StartVRRender() {
// Set customizations
__DisplaySetFramerate(g_Config.bForce72Hz ? 72 : 60);
VR_SetConfigFloat(VR_CONFIG_CANVAS_DISTANCE, g_Config.fCanvasDistance);
+ VR_SetConfig(VR_CONFIG_PASSTHROUGH, g_Config.bPassthrough);
vrMirroring[VR_MIRRORING_UPDATED] = false;
return true;
}
@@ -828,6 +830,10 @@ bool IsMultiviewSupported() {
return false;
}
+bool IsPassthroughSupported() {
+ return VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PASSTHROUGH);
+}
+
bool IsFlatVRGame() {
return vrFlatGame;
}
diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h
index 59264d67fea3..b2fd9ec31feb 100644
--- a/Common/VR/PPSSPPVR.h
+++ b/Common/VR/PPSSPPVR.h
@@ -51,6 +51,7 @@ void PostVRFrameRender();
int GetVRFBOIndex();
int GetVRPassesCount();
bool IsMultiviewSupported();
+bool IsPassthroughSupported();
bool IsFlatVRGame();
bool IsFlatVRScene();
bool IsGameVRScene();
diff --git a/Common/VR/VRBase.cpp b/Common/VR/VRBase.cpp
index 715ea6644ea7..6a22203586e7 100644
--- a/Common/VR/VRBase.cpp
+++ b/Common/VR/VRBase.cpp
@@ -61,6 +61,9 @@ void VR_Init( void* system, const char* name, int version ) {
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_INSTANCE)) {
extensions.push_back(XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME);
}
+ if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PASSTHROUGH)) {
+ extensions.push_back(XR_FB_PASSTHROUGH_EXTENSION_NAME);
+ }
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_PERFORMANCE)) {
extensions.push_back(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
extensions.push_back(XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME);
diff --git a/Common/VR/VRBase.h b/Common/VR/VRBase.h
index 2d3405f8a7b1..8642640db00f 100644
--- a/Common/VR/VRBase.h
+++ b/Common/VR/VRBase.h
@@ -42,12 +42,16 @@ static void OXR_CheckErrors(XrInstance instance, XrResult result, const char* fu
#define OXR(func) func;
#endif
-enum { ovrMaxLayerCount = 2 };
+#define DECL_PFN(pfn) PFN_##pfn pfn = nullptr
+#define INIT_PFN(pfn) OXR(xrGetInstanceProcAddr(engine->appState.Instance, #pfn, (PFN_xrVoidFunction*)(&pfn)))
+
+enum { ovrMaxLayerCount = 3 };
enum { ovrMaxNumEyes = 2 };
typedef union {
XrCompositionLayerProjection Projection;
XrCompositionLayerCylinderKHR Cylinder;
+ XrCompositionLayerPassthroughFB Passthrough;
} ovrCompositorLayer_Union;
typedef struct {
@@ -122,6 +126,7 @@ enum VRPlatformFlag {
VR_PLATFORM_CONTROLLER_QUEST,
VR_PLATFORM_EXTENSION_FOVEATION,
VR_PLATFORM_EXTENSION_INSTANCE,
+ VR_PLATFORM_EXTENSION_PASSTHROUGH,
VR_PLATFORM_EXTENSION_PERFORMANCE,
VR_PLATFORM_RENDERER_VULKAN,
VR_PLATFORM_TRACKING_FLOOR,
diff --git a/Common/VR/VRRenderer.cpp b/Common/VR/VRRenderer.cpp
index 5a9a20892665..6c22312311af 100644
--- a/Common/VR/VRRenderer.cpp
+++ b/Common/VR/VRRenderer.cpp
@@ -20,6 +20,17 @@ float vrConfigFloat[VR_CONFIG_FLOAT_MAX] = {};
XrVector3f hmdorientation;
+XrPassthroughFB passthrough = XR_NULL_HANDLE;
+XrPassthroughLayerFB passthroughLayer = XR_NULL_HANDLE;
+DECL_PFN(xrCreatePassthroughFB);
+DECL_PFN(xrDestroyPassthroughFB);
+DECL_PFN(xrPassthroughStartFB);
+DECL_PFN(xrPassthroughPauseFB);
+DECL_PFN(xrCreatePassthroughLayerFB);
+DECL_PFN(xrDestroyPassthroughLayerFB);
+DECL_PFN(xrPassthroughLayerPauseFB);
+DECL_PFN(xrPassthroughLayerResumeFB);
+
void VR_UpdateStageBounds(ovrApp* pappState) {
XrExtent2Df stageBounds = {};
@@ -179,6 +190,17 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) {
VR_DestroyRenderer(engine);
}
+ if (VR_GetPlatformFlag(VRPlatformFlag::VR_PLATFORM_EXTENSION_PASSTHROUGH)) {
+ INIT_PFN(xrCreatePassthroughFB);
+ INIT_PFN(xrDestroyPassthroughFB);
+ INIT_PFN(xrPassthroughStartFB);
+ INIT_PFN(xrPassthroughPauseFB);
+ INIT_PFN(xrCreatePassthroughLayerFB);
+ INIT_PFN(xrDestroyPassthroughLayerFB);
+ INIT_PFN(xrPassthroughLayerPauseFB);
+ INIT_PFN(xrPassthroughLayerResumeFB);
+ }
+
int eyeW, eyeH;
VR_GetResolution(engine, &eyeW, &eyeH);
VR_SetConfig(VR_CONFIG_VIEWPORT_WIDTH, eyeW);
@@ -221,10 +243,32 @@ void VR_InitRenderer( engine_t* engine, bool multiview ) {
ovrRenderer_SetFoveation(&engine->appState.Instance, &engine->appState.Session, &engine->appState.Renderer, XR_FOVEATION_LEVEL_HIGH_TOP_FB, 0, XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB);
}
#endif
+
+ if (VR_GetPlatformFlag(VRPlatformFlag::VR_PLATFORM_EXTENSION_PASSTHROUGH)) {
+ XrPassthroughCreateInfoFB ptci = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB};
+ XrResult result;
+ OXR(result = xrCreatePassthroughFB(engine->appState.Session, &ptci, &passthrough));
+
+ if (XR_SUCCEEDED(result)) {
+ XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB};
+ plci.passthrough = passthrough;
+ plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB;
+ OXR(xrCreatePassthroughLayerFB(engine->appState.Session, &plci, &passthroughLayer));
+ }
+
+ OXR(xrPassthroughStartFB(passthrough));
+ OXR(xrPassthroughLayerResumeFB(passthroughLayer));
+ }
initialized = true;
}
void VR_DestroyRenderer( engine_t* engine ) {
+ if (VR_GetPlatformFlag(VRPlatformFlag::VR_PLATFORM_EXTENSION_PASSTHROUGH)) {
+ OXR(xrPassthroughLayerPauseFB(passthroughLayer));
+ OXR(xrPassthroughPauseFB(passthrough));
+ OXR(xrDestroyPassthroughFB(passthrough));
+ passthrough = XR_NULL_HANDLE;
+ }
ovrRenderer_Destroy(&engine->appState.Renderer);
free(projections);
initialized = false;
@@ -328,6 +372,16 @@ void VR_EndFrame( engine_t* engine ) {
void VR_FinishFrame( engine_t* engine ) {
+ if (VR_GetPlatformFlag(VRPlatformFlag::VR_PLATFORM_EXTENSION_PASSTHROUGH) && VR_GetConfig(VR_CONFIG_PASSTHROUGH)) {
+ if (passthroughLayer != XR_NULL_HANDLE) {
+ XrCompositionLayerPassthroughFB passthrough_layer = {XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB};
+ passthrough_layer.layerHandle = passthroughLayer;
+ passthrough_layer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
+ passthrough_layer.space = XR_NULL_HANDLE;
+ engine->appState.Layers[engine->appState.LayerCount++].Passthrough = passthrough_layer;
+ }
+ }
+
int vrMode = vrConfig[VR_CONFIG_MODE];
XrCompositionLayerProjectionView projection_layer_elements[2] = {};
if ((vrMode == VR_MODE_MONO_6DOF) || (vrMode == VR_MODE_STEREO_6DOF)) {
diff --git a/Common/VR/VRRenderer.h b/Common/VR/VRRenderer.h
index 8ed786965606..c583740b6e84 100644
--- a/Common/VR/VRRenderer.h
+++ b/Common/VR/VRRenderer.h
@@ -4,8 +4,8 @@
#include "VRMath.h"
enum VRConfig {
- //switching between 2D and 3D
- VR_CONFIG_MODE,
+ //switching between mode
+ VR_CONFIG_MODE, VR_CONFIG_PASSTHROUGH,
//mouse cursor
VR_CONFIG_MOUSE_SIZE, VR_CONFIG_MOUSE_X, VR_CONFIG_MOUSE_Y,
//viewport setup
diff --git a/Core/Config.cpp b/Core/Config.cpp
index 7ce91c706450..5753b803b7c6 100644
--- a/Core/Config.cpp
+++ b/Core/Config.cpp
@@ -839,6 +839,7 @@ static const ConfigSetting vrSettings[] = {
ConfigSetting("VREnableMotions", &g_Config.bEnableMotions, true, CfgFlag::PER_GAME),
ConfigSetting("VRForce72Hz", &g_Config.bForce72Hz, true, CfgFlag::PER_GAME),
ConfigSetting("VRManualForceVR", &g_Config.bManualForceVR, false, CfgFlag::PER_GAME),
+ ConfigSetting("VRPassthrough", &g_Config.bPassthrough, false, CfgFlag::PER_GAME),
ConfigSetting("VRRescaleHUD", &g_Config.bRescaleHUD, true, CfgFlag::PER_GAME),
ConfigSetting("VRCameraDistance", &g_Config.fCameraDistance, 0.0f, CfgFlag::PER_GAME),
ConfigSetting("VRCameraHeight", &g_Config.fCameraHeight, 0.0f, CfgFlag::PER_GAME),
diff --git a/Core/Config.h b/Core/Config.h
index 0b23b59c8e20..ae0c95b3d05b 100644
--- a/Core/Config.h
+++ b/Core/Config.h
@@ -453,6 +453,7 @@ struct Config {
bool bEnableMotions;
bool bForce72Hz;
bool bManualForceVR;
+ bool bPassthrough;
bool bRescaleHUD;
float fCameraDistance;
float fCameraHeight;
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index 817e8ee7d10c..dd7b0bc23f37 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -1152,6 +1152,9 @@ void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
vr6DoF->SetEnabledPtr(&g_Config.bEnableVR);
vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));
vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));
+ if (IsPassthroughSupported()) {
+ vrSettings->Add(new CheckBox(&g_Config.bPassthrough, vr->T("Enable passthrough")));
+ }
vrSettings->Add(new ItemHeader(vr->T("VR camera")));
vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, 12.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));
diff --git a/android/VRManifest.xml b/android/VRManifest.xml
index 4dfda7647a58..dc1a3aa1eb7f 100644
--- a/android/VRManifest.xml
+++ b/android/VRManifest.xml
@@ -3,6 +3,7 @@
+