Merge pull request #60 from DrBeef/master

Super Immersive Mode
This commit is contained in:
Amanda Watson 2024-02-15 14:32:26 -06:00 committed by GitHub
commit b96a9fe6da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 580 additions and 83 deletions

View file

@ -14,8 +14,7 @@ enum class BooleanSetting(
PLUGIN_LOADER("plugin_loader", Settings.SECTION_SYSTEM, false), PLUGIN_LOADER("plugin_loader", Settings.SECTION_SYSTEM, false),
ALLOW_PLUGIN_LOADER("allow_plugin_loader", Settings.SECTION_SYSTEM, true), ALLOW_PLUGIN_LOADER("allow_plugin_loader", Settings.SECTION_SYSTEM, true),
SWAP_SCREEN("swap_screen", Settings.SECTION_LAYOUT, false), SWAP_SCREEN("swap_screen", Settings.SECTION_LAYOUT, false),
VR_EXTRA_PERFORMANCE_MODE("vr_extra_performance_mode", Settings.SECTION_VR, false), VR_EXTRA_PERFORMANCE_MODE("vr_extra_performance_mode", Settings.SECTION_VR, false);
VR_IMMERSIVE_MODE("vr_immersive_mode", Settings.SECTION_VR, false);
override var boolean: Boolean = defaultValue override var boolean: Boolean = defaultValue

View file

@ -47,7 +47,11 @@ enum class IntSetting(
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1), USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1),
VR_ENVIRONMENT("vr_environment", Settings.SECTION_VR, VR_ENVIRONMENT("vr_environment", Settings.SECTION_VR,
if (hMDType == VRUtils.HMDType.QUEST3.value) 1 else 2), if (hMDType == VRUtils.HMDType.QUEST3.value) 1 else 2),
VR_CPU_LEVEL("vr_cpu_level", Settings.SECTION_VR, 3); VR_CPU_LEVEL("vr_cpu_level", Settings.SECTION_VR, 3),
VR_IMMERSIVE_MODE("vr_immersive_mode", Settings.SECTION_VR, 0),
VR_IMMERSIVE_POSITIONAL_FACTOR("vr_immersive_positional_factor", Settings.SECTION_VR, 0),
VR_IMMERSIVE_POSITIONAL_GAME_SCALER("vr_immersive_positional_game_scaler", Settings.SECTION_VR, 0),
VR_SI_MODE_REGISTER_OFFSET("vr_si_mode_register_offset", Settings.SECTION_VR, 0);
override var int: Int = defaultValue override var int: Int = defaultValue

View file

@ -759,7 +759,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
R.string.factor3d, R.string.factor3d,
R.string.factor3d_description, R.string.factor3d_description,
0, 0,
100, 400,
"%", "%",
IntSetting.STEREOSCOPIC_3D_DEPTH.key, IntSetting.STEREOSCOPIC_3D_DEPTH.key,
IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat() IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat()
@ -1111,13 +1111,53 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.NEW_3DS.defaultValue IntSetting.NEW_3DS.defaultValue
) )
) )
add(HeaderSetting(R.string.immersive_mode))
add( add(
SwitchSetting( SingleChoiceSetting(
BooleanSetting.VR_IMMERSIVE_MODE, IntSetting.VR_IMMERSIVE_MODE,
R.string.vr_immersive_mode_title, R.string.vr_immersive_mode_title,
R.string.vr_immersive_mode_description, R.string.vr_immersive_mode_description,
BooleanSetting.VR_IMMERSIVE_MODE.key, R.array.vrImmersiveModeNames,
BooleanSetting.VR_IMMERSIVE_MODE.defaultValue R.array.vrImmersiveModeValues,
IntSetting.VR_IMMERSIVE_MODE.key,
IntSetting.VR_IMMERSIVE_MODE.defaultValue
)
)
add(
SliderSetting(
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR,
R.string.vr_immersive_pos_factor_title,
R.string.vr_immersive_pos_factor_description,
0,
40,
"x",
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR.key,
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR.defaultValue.toFloat()
)
)
add(
SingleChoiceSetting(
IntSetting.VR_IMMERSIVE_POSITIONAL_GAME_SCALER,
R.string.vr_immersive_pos_game_scaler_title,
R.string.vr_immersive_pos_game_scaler_description,
R.array.vrPosFactorGameScalerNames,
R.array.vrPosFactorGameScalerValues,
IntSetting.VR_IMMERSIVE_POSITIONAL_GAME_SCALER.key,
IntSetting.VR_IMMERSIVE_POSITIONAL_GAME_SCALER.defaultValue
)
)
add(
SliderSetting(
IntSetting.VR_SI_MODE_REGISTER_OFFSET,
R.string.vr_si_mode_register_offset_title,
R.string.vr_si_mode_register_offset_description,
0,
92,
"register",
IntSetting.VR_SI_MODE_REGISTER_OFFSET.key,
IntSetting.VR_SI_MODE_REGISTER_OFFSET.defaultValue.toFloat()
) )
) )
} }

View file

@ -285,21 +285,26 @@ void Config::ReadValues() {
VRSettings::values.cpu_level = VRSettings::values.cpu_level =
VRSettings::values.extra_performance_mode_enabled ? XR_HIGHEST_CPU_PERF_LEVEL VRSettings::values.extra_performance_mode_enabled ? XR_HIGHEST_CPU_PERF_LEVEL
: VRSettings::CPUPrefToPerfSettingsLevel(sdl2_config->GetInteger( : VRSettings::CPUPrefToPerfSettingsLevel(sdl2_config->GetInteger(
"VR", "vr_cpu_level", 3)); "VR", "vr_cpu_level", 3));
Settings::values.vr_use_immersive_mode = sdl2_config->GetBoolean( VRSettings::values.vr_immersive_mode = sdl2_config->GetInteger(
"VR", "vr_immersive_mode", false); "VR", "vr_immersive_mode", 0);
Settings::values.vr_immersive_mode = VRSettings::values.vr_immersive_mode;
VRSettings::values.vr_si_mode_register_offset = sdl2_config->GetInteger(
"VR", "vr_si_mode_register_offset", 0);
Settings::values.vr_si_mode_register_offset = VRSettings::values.vr_si_mode_register_offset;
VRSettings::values.vr_immersive_positional_factor = sdl2_config->GetInteger(
"VR", "vr_immersive_positional_factor", 0);
Settings::values.vr_immersive_positional_factor = VRSettings::values.vr_immersive_positional_factor;
VRSettings::values.vr_immersive_positional_game_scaler = sdl2_config->GetInteger(
"VR", "vr_immersive_positional_game_scaler", 0);
Settings::values.vr_immersive_positional_game_scaler = VRSettings::values.vr_immersive_positional_game_scaler;
if (Settings::values.vr_use_immersive_mode) { if (Settings::values.vr_immersive_mode.GetValue() > 0) {
LOG_INFO(Config, "VR immersive mode enabled"); LOG_INFO(Config, "VR immersive mode enabled");
// no point rendering passthrough in immersive mode // no point rendering passthrough in immersive mode
VRSettings::values.vr_environment = VRSettings::values.vr_environment =
static_cast<uint32_t>(VRSettings::VREnvironmentType::VOID); static_cast<uint32_t>(VRSettings::VREnvironmentType::VOID);
// We originally had two immersive modes, but I cut them down to fit in
// the shader map's bitfield.
VRSettings::values.vr_immersive_mode = 2;
// When immersive mode is enabled, only OpenGL is supported.
Settings::values.graphics_api = Settings::GraphicsAPI::OpenGL;
} }
// Miscellaneous // Miscellaneous

View file

@ -43,12 +43,15 @@ public:
XrViewConfigurationProperties mViewportConfig = {}; XrViewConfigurationProperties mViewportConfig = {};
static constexpr XrViewConfigurationType VIEW_CONFIG_TYPE = static constexpr XrViewConfigurationType VIEW_CONFIG_TYPE =
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrSpace mHeadSpace = XR_NULL_HANDLE; XrSpace mHeadSpace = XR_NULL_HANDLE;
XrSpace mViewSpace = XR_NULL_HANDLE;
XrSpace mForwardDirectionSpace = XR_NULL_HANDLE; XrSpace mForwardDirectionSpace = XR_NULL_HANDLE;
XrSpace mLocalSpace = XR_NULL_HANDLE; XrSpaceLocation headLocation = {};
XrSpace mStageSpace = XR_NULL_HANDLE;
size_t mMaxLayerCount = 0; XrSpace mLocalSpace = XR_NULL_HANDLE;
XrSpace mStageSpace = XR_NULL_HANDLE;
size_t mMaxLayerCount = 0;
// EGL context // EGL context
std::unique_ptr<EglContext> mEglContext; std::unique_ptr<EglContext> mEglContext;

View file

@ -30,9 +30,8 @@ License : Licensed under GPLv3 or any later version.
namespace { namespace {
constexpr float lowerPanelScaleFactor = 0.75f; constexpr float defaultLowerPanelScaleFactor = 0.75f;
constexpr float superImmersiveRadius = 0.5f;
const std::vector<float> immersiveLevelFactor = {1.0f, 5.0f, 3.0f};
/** Used to translate texture coordinates into the corresponding coordinates /** Used to translate texture coordinates into the corresponding coordinates
* on the Android Activity Window. * on the Android Activity Window.
@ -233,11 +232,10 @@ GameSurfaceLayer::GameSurfaceLayer(const XrVector3f&& position, JNIEnv* env, job
, mActivityObject(activityObject) , mActivityObject(activityObject)
{ {
assert(mImmersiveMode == 0 || mImmersiveMode == 1);
if (mImmersiveMode > 0) { if (mImmersiveMode > 0) {
ALOGI("Using VR immersive mode {}", mImmersiveMode); ALOGI("Using VR immersive mode {}", mImmersiveMode);
mTopPanelFromWorld.position.z = mLowerPanelFromWorld.position.z; mTopPanelFromWorld.position.z = mLowerPanelFromWorld.position.z;
mLowerPanelFromWorld.position.y = -1.0f - (0.5f * (mImmersiveMode - 1)); mLowerPanelFromWorld.position.y = -1.0f;
} }
const int32_t initializationStatus = Init(activityObject, position, session); const int32_t initializationStatus = Init(activityObject, position, session);
if (initializationStatus < 0) { if (initializationStatus < 0) {
@ -257,22 +255,27 @@ void GameSurfaceLayer::SetSurface() const {
mEnv->CallStaticVoidMethod(mVrGameSurfaceClass, setSurfaceMethodID, mActivityObject, mSurface); mEnv->CallStaticVoidMethod(mVrGameSurfaceClass, setSurfaceMethodID, mActivityObject, mSurface);
} }
void GameSurfaceLayer::Frame(const XrSpace& space,
std::vector<XrCompositionLayer>& layers,
uint32_t& layerCount) const
void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLayer>& layers,
uint32_t& layerCount, const XrPosef& headPose, const float& immersiveModeFactor, const bool showLowerPanel)
{ {
const uint32_t panelWidth = mSwapchain.mWidth / 2; const uint32_t panelWidth = mSwapchain.mWidth / 2;
const uint32_t panelHeight = mSwapchain.mHeight / 2; const uint32_t panelHeight = mSwapchain.mHeight / 2;
const double aspectRatio = const double aspectRatio =
static_cast<double>(2 * panelWidth) / static_cast<double>(panelHeight); static_cast<double>(2 * panelWidth) / static_cast<double>(panelHeight);
// Prevent a seam between the top and bottom view // Prevent a seam between the top and bottom view
constexpr uint32_t verticalBorderTex = 1; constexpr uint32_t verticalBorderTex = 1;
const bool useCylinder = (GetCylinderSysprop() != 0) || (mImmersiveMode > 0); const bool useCylinder = (GetCylinderSysprop() != 0) || (mImmersiveMode > 0);
if (useCylinder) { if (useCylinder) {
// Create the Top Display Panel (Curved display) // Create the Top Display Panel (Curved display)
for (uint32_t eye = 0; eye < NUM_EYES; eye++) { for (uint32_t eye = 0; eye < NUM_EYES; eye++) {
XrPosef topPanelFromWorld = mTopPanelFromWorld;
if (mImmersiveMode > 1 && !showLowerPanel)
{
topPanelFromWorld = GetTopPanelFromHeadPose(eye, headPose);
}
XrCompositionLayerCylinderKHR layer = {}; XrCompositionLayerCylinderKHR layer = {};
layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR; layer.type = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR;
@ -283,6 +286,12 @@ void GameSurfaceLayer::Frame(const XrSpace& space,
layer.space = space; layer.space = space;
// Radius effectively controls the width of the cylinder shape.
// Central angle controls how much of the cylinder is
// covered by the texture. Together, they control the
// scale of the texture.
const float radius = (mImmersiveMode < 2 || showLowerPanel) ? GetRadiusSysprop() : superImmersiveRadius;
layer.eyeVisibility = eye == 0 ? XR_EYE_VISIBILITY_LEFT : XR_EYE_VISIBILITY_RIGHT; layer.eyeVisibility = eye == 0 ? XR_EYE_VISIBILITY_LEFT : XR_EYE_VISIBILITY_RIGHT;
memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage)); memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage));
layer.subImage.swapchain = mSwapchain.mHandle; layer.subImage.swapchain = mSwapchain.mHandle;
@ -290,18 +299,12 @@ void GameSurfaceLayer::Frame(const XrSpace& space,
layer.subImage.imageRect.offset.y = 0; layer.subImage.imageRect.offset.y = 0;
layer.subImage.imageRect.extent.width = panelWidth; layer.subImage.imageRect.extent.width = panelWidth;
layer.subImage.imageRect.extent.height = panelHeight - verticalBorderTex; layer.subImage.imageRect.extent.height = panelHeight - verticalBorderTex;
layer.subImage.imageArrayIndex = 0; layer.subImage.imageArrayIndex = 0;
layer.pose = mTopPanelFromWorld; layer.pose = topPanelFromWorld;
layer.pose.position.z += GetRadiusSysprop(); layer.pose.position.z += (mImmersiveMode < 2) ? radius : 0.0f;
layer.radius = radius;
// Radius effectively controls the width of the cylinder shape.
// Central angle controls how much of the cylinder is
// covered by the texture. Together, they control the
// scale of the texture.
const float radius = GetRadiusSysprop();
layer.radius = radius;
layer.centralAngle = (!mImmersiveMode ? GetCentralAngleSysprop() layer.centralAngle = (!mImmersiveMode ? GetCentralAngleSysprop()
: 55.0f * immersiveLevelFactor[mImmersiveMode]) * : GameSurfaceLayer::DEFAULT_CYLINDER_CENTRAL_ANGLE_DEGREES * immersiveModeFactor) *
MATH_FLOAT_PI / 180.0f; MATH_FLOAT_PI / 180.0f;
layer.aspectRatio = -aspectRatio; layer.aspectRatio = -aspectRatio;
layers[layerCount++].mCylinder = layer; layers[layerCount++].mCylinder = layer;
@ -339,6 +342,8 @@ void GameSurfaceLayer::Frame(const XrSpace& space,
layers[layerCount++].mQuad = layer; layers[layerCount++].mQuad = layer;
} }
} }
// Create the Lower Display Panel (flat touchscreen) // Create the Lower Display Panel (flat touchscreen)
// When citra is in stereo mode, this panel is also rendered in stereo (i.e. // When citra is in stereo mode, this panel is also rendered in stereo (i.e.
// twice), but the image is mono. Therefore, take the right half of the // twice), but the image is mono. Therefore, take the right half of the
@ -346,6 +351,7 @@ void GameSurfaceLayer::Frame(const XrSpace& space,
// FIXME we waste rendering time rendering both displays. That said, We also // FIXME we waste rendering time rendering both displays. That said, We also
// waste rendering time copying the buffer between runtimes. No time for // waste rendering time copying the buffer between runtimes. No time for
// that now! // that now!
if (showLowerPanel)
{ {
const uint32_t cropHoriz = 90 * mResolutionFactor; const uint32_t cropHoriz = 90 * mResolutionFactor;
XrCompositionLayerQuad layer = {}; XrCompositionLayerQuad layer = {};
@ -362,20 +368,20 @@ void GameSurfaceLayer::Frame(const XrSpace& space,
memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage)); memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage));
layer.subImage.swapchain = mSwapchain.mHandle; layer.subImage.swapchain = mSwapchain.mHandle;
layer.subImage.imageRect.offset.x = layer.subImage.imageRect.offset.x =
(cropHoriz / 2) / immersiveLevelFactor[mImmersiveMode] + (cropHoriz / 2) / immersiveModeFactor +
panelWidth * (0.5f - (0.5f / immersiveLevelFactor[mImmersiveMode])); panelWidth * (0.5f - (0.5f / immersiveModeFactor));
layer.subImage.imageRect.offset.y = layer.subImage.imageRect.offset.y =
panelHeight + verticalBorderTex + panelHeight + verticalBorderTex +
panelHeight * (0.5f - (0.5f / immersiveLevelFactor[mImmersiveMode])); panelHeight * (0.5f - (0.5f / immersiveModeFactor));
layer.subImage.imageRect.extent.width = layer.subImage.imageRect.extent.width =
(panelWidth - cropHoriz) / immersiveLevelFactor[mImmersiveMode]; (panelWidth - cropHoriz) / immersiveModeFactor;
layer.subImage.imageRect.extent.height = panelHeight / immersiveLevelFactor[mImmersiveMode]; layer.subImage.imageRect.extent.height = panelHeight / immersiveModeFactor;
layer.subImage.imageArrayIndex = 0; layer.subImage.imageArrayIndex = 0;
layer.pose = mLowerPanelFromWorld; layer.pose = mLowerPanelFromWorld;
const auto scale = GetDensityScaleForSize(panelWidth - cropHoriz, -panelHeight, const auto scale = GetDensityScaleForSize(panelWidth - cropHoriz, -panelHeight,
lowerPanelScaleFactor, mResolutionFactor); defaultLowerPanelScaleFactor, mResolutionFactor);
layer.size.width = scale.x; layer.size.width = scale.x * defaultLowerPanelScaleFactor;
layer.size.height = scale.y; layer.size.height = scale.y * defaultLowerPanelScaleFactor;
layers[layerCount++].mQuad = layer; layers[layerCount++].mQuad = layer;
} }
} }
@ -400,7 +406,7 @@ bool GameSurfaceLayer::GetRayIntersectionWithPanel(const XrVector3f& start,
const uint32_t panelWidth = mSwapchain.mWidth / 2; const uint32_t panelWidth = mSwapchain.mWidth / 2;
const uint32_t panelHeight = mSwapchain.mHeight / 2; const uint32_t panelHeight = mSwapchain.mHeight / 2;
const XrVector2f scale = const XrVector2f scale =
GetDensityScaleForSize(panelWidth, panelHeight, lowerPanelScaleFactor, mResolutionFactor); GetDensityScaleForSize(panelWidth, panelHeight, defaultLowerPanelScaleFactor, mResolutionFactor);
return ::GetRayIntersectionWithPanel(mLowerPanelFromWorld, panelWidth, panelHeight, scale, return ::GetRayIntersectionWithPanel(mLowerPanelFromWorld, panelWidth, panelHeight, scale,
start, end, result2d, result3d); start, end, result2d, result3d);
} }
@ -424,6 +430,23 @@ void GameSurfaceLayer::SetTopPanelFromController(const XrVector3f& controllerPos
mTopPanelFromWorld = XrPosef{windowRotation, windowPosition}; mTopPanelFromWorld = XrPosef{windowRotation, windowPosition};
} }
XrPosef GameSurfaceLayer::GetTopPanelFromHeadPose(uint32_t eye, const XrPosef& headPose) {
XrVector3f panelPosition = headPose.position;
XrVector3f forward, up, right;
XrMath::Quatf::ToVectors(headPose.orientation, forward, right, up);
panelPosition.z += superImmersiveRadius * (forward.x * 0.4f);
panelPosition.y -= superImmersiveRadius * (forward.z * 0.4f);
panelPosition.x += superImmersiveRadius * (forward.y * 0.4f);
panelPosition.z += up.x / 12.f;
panelPosition.y -= up.z / 12.f;
panelPosition.x += up.y / 12.f;
return XrPosef{headPose.orientation, panelPosition};
}
// based on thumbstick, modify the depth of the top panel // based on thumbstick, modify the depth of the top panel
void GameSurfaceLayer::SetTopPanelFromThumbstick(const float thumbstickY) { void GameSurfaceLayer::SetTopPanelFromThumbstick(const float thumbstickY) {
static constexpr float kDepthSpeed = 0.05f; static constexpr float kDepthSpeed = 0.05f;

View file

@ -98,11 +98,12 @@ public:
* @param space the XrSpace this layer should be positioned with. The * @param space the XrSpace this layer should be positioned with. The
* center of the layer is placed in the center of the FOV. * center of the layer is placed in the center of the FOV.
* @param layers the array of layers to populate * @param layers the array of layers to populate
* @param layerCount the layer count passed to XrEndFrame. This is incremented by * @param layerCount the number of layers in the array
* the number of layers added by this function. * @param visibleLowerPanel whether the lower panel is shown/visible
*/ */
void Frame(const XrSpace& space, std::vector<XrCompositionLayer>& layers, void Frame(const XrSpace& space, std::vector<XrCompositionLayer>& layers,
uint32_t& layerCount) const; uint32_t& layerCount, const XrPosef& headPose,
const float& immersiveModeFactor, const bool visibleLowerPanel);
/** Given an origin, direction of a ray, /** Given an origin, direction of a ray,
* returns the coordinates of where the ray will intersects * returns the coordinates of where the ray will intersects
@ -132,6 +133,7 @@ public:
XrVector2f& result2d, XrVector2f& result2d,
XrPosef& result3d) const; XrPosef& result3d) const;
void SetTopPanelFromController(const XrVector3f& controllerPosition); void SetTopPanelFromController(const XrVector3f& controllerPosition);
XrPosef GetTopPanelFromHeadPose(uint32_t eye, const XrPosef& headPose);
void SetTopPanelFromThumbstick(const float thumbstickY); void SetTopPanelFromThumbstick(const float thumbstickY);

View file

@ -84,6 +84,112 @@ public:
} }
}; };
class Matrixf
{
public:
static void Identity(XrVector4f mat[4]) {
mat[0] = {1.f, 0.f, 0.f, 0.f};
mat[1] = {0.f, 1.f, 0.f, 0.f};
mat[2] = {0.f, 0.f, 1.f, 0.f};
mat[3] = {0.f, 0.f, 0.f, 1.f};
}
static XrVector4f XrVector4f_Multiply(const XrVector4f mat[4], const XrVector4f &v)
{
XrVector4f out;
out.x = mat[0].x * v.x + mat[0].y * v.y + mat[0].z * v.z + mat[0].w * v.w;
out.y = mat[1].x * v.x + mat[1].y * v.y + mat[1].z * v.z + mat[1].w * v.w;
out.z = mat[2].x * v.x + mat[2].y * v.y + mat[2].z * v.z + mat[2].w * v.w;
out.w = mat[3].x * v.x + mat[3].y * v.y + mat[3].z * v.z + mat[3].w * v.w;
return out;
}
static XrVector3f XrVector3f_Multiply(const XrVector3f mat[3], const XrVector3f &v)
{
XrVector3f out;
out.x = mat[0].x * v.x + mat[0].y * v.y + mat[0].z * v.z;
out.y = mat[1].x * v.x + mat[1].y * v.y + mat[1].z * v.z;
out.z = mat[2].x * v.x + mat[2].y * v.y + mat[2].z * v.z;
return out;
}
// Returns a 3x3 minor of a 4x4 matrix.
static float ToMinor(const float *matrix, int r0, int r1, int r2, int c0, int c1, int c2) {
return matrix[4 * r0 + c0] *
(matrix[4 * r1 + c1] * matrix[4 * r2 + c2] - matrix[4 * r2 + c1] * matrix[4 * r1 + c2]) -
matrix[4 * r0 + c1] *
(matrix[4 * r1 + c0] * matrix[4 * r2 + c2] - matrix[4 * r2 + c0] * matrix[4 * r1 + c2]) +
matrix[4 * r0 + c2] *
(matrix[4 * r1 + c0] * matrix[4 * r2 + c1] - matrix[4 * r2 + c0] * matrix[4 * r1 + c1]);
}
static void ToInverse(const XrVector4f in[4], XrVector4f out[4]) {
float *matrix = (float*)in;
float *inv_mat = (float*)out;
const float rcpDet =
1.0f / (matrix[0] * ToMinor(matrix, 1, 2, 3, 1, 2, 3) -
matrix[1] * ToMinor(matrix, 1, 2, 3, 0, 2, 3) +
matrix[2] * ToMinor(matrix, 1, 2, 3, 0, 1, 3) -
matrix[3] * ToMinor(matrix, 1, 2, 3, 0, 1, 2));
inv_mat[0] = ToMinor(matrix, 1, 2, 3, 1, 2, 3) * rcpDet;
inv_mat[1] = -ToMinor(matrix, 0, 2, 3, 1, 2, 3) * rcpDet;
inv_mat[2] = ToMinor(matrix, 0, 1, 3, 1, 2, 3) * rcpDet;
inv_mat[3] = -ToMinor(matrix, 0, 1, 2, 1, 2, 3) * rcpDet;
inv_mat[4] = -ToMinor(matrix, 1, 2, 3, 0, 2, 3) * rcpDet;
inv_mat[5] = ToMinor(matrix, 0, 2, 3, 0, 2, 3) * rcpDet;
inv_mat[6] = -ToMinor(matrix, 0, 1, 3, 0, 2, 3) * rcpDet;
inv_mat[7] = ToMinor(matrix, 0, 1, 2, 0, 2, 3) * rcpDet;
inv_mat[8] = ToMinor(matrix, 1, 2, 3, 0, 1, 3) * rcpDet;
inv_mat[9] = -ToMinor(matrix, 0, 2, 3, 0, 1, 3) * rcpDet;
inv_mat[10] = ToMinor(matrix, 0, 1, 3, 0, 1, 3) * rcpDet;
inv_mat[11] = -ToMinor(matrix, 0, 1, 2, 0, 1, 3) * rcpDet;
inv_mat[12] = -ToMinor(matrix, 1, 2, 3, 0, 1, 2) * rcpDet;
inv_mat[13] = ToMinor(matrix, 0, 2, 3, 0, 1, 2) * rcpDet;
inv_mat[14] = -ToMinor(matrix, 0, 1, 3, 0, 1, 2) * rcpDet;
inv_mat[15] = ToMinor(matrix, 0, 1, 2, 0, 1, 2) * rcpDet;
}
static void Projection(XrVector4f result[4], const float fov_x, const float fov_y,
const float nearZ, const float farZ) {
float *projectionMatrix = (float*)result;
float xmin, xmax, ymin, ymax;
float width, height, depth;
ymax = nearZ * tan( fov_y );
ymin = -ymax;
xmax = nearZ * tan( fov_x );
xmin = -xmax;
width = xmax - xmin;
height = ymax - ymin;
depth = farZ - nearZ;
projectionMatrix[0] = 2 * nearZ / width;
projectionMatrix[4] = 0;
projectionMatrix[8] = ( xmax + xmin ) / width;
projectionMatrix[12] = 0;
projectionMatrix[1] = 0;
projectionMatrix[5] = 2 * nearZ / height;
projectionMatrix[9] = ( ymax + ymin ) / height;
projectionMatrix[13] = 0;
projectionMatrix[2] = 0;
projectionMatrix[6] = 0;
projectionMatrix[10] = -( farZ + nearZ ) / depth;
projectionMatrix[14] = -2 * farZ * nearZ / depth;
projectionMatrix[3] = 0;
projectionMatrix[7] = 0;
projectionMatrix[11] = -1;
projectionMatrix[15] = 0;
}
};
class Quatf { class Quatf {
public: public:
static XrQuaternionf Identity() { return XrQuaternionf{0.0f, 0.0f, 0.0f, 1.0f}; } static XrQuaternionf Identity() { return XrQuaternionf{0.0f, 0.0f, 0.0f, 1.0f}; }
@ -181,6 +287,65 @@ public:
(up.z - forward.y) / s}; (up.z - forward.y) / s};
} }
} }
static void ToRotationMatrix(const XrQuaternionf& q, float rotation[16]) {
float x2 = q.x + q.x;
float y2 = q.y + q.y;
float z2 = q.z + q.z;
float xx2 = q.x * x2;
float xy2 = q.x * y2;
float xz2 = q.x * z2;
float yy2 = q.y * y2;
float yz2 = q.y * z2;
float zz2 = q.z * z2;
float sx2 = q.w * x2;
float sy2 = q.w * y2;
float sz2 = q.w * z2;
float r[16] = {1 - (yy2 + zz2), xy2 + sz2, xz2 - sy2, 0.f, // column 0
xy2 - sz2, 1 - (xx2 + zz2), yz2 + sx2, 0.f, // column 1
xz2 + sy2, yz2 - sx2, 1 - (xx2 + yy2), 0.f, // column 2
0.f, 0.f, 0.f, 1};// column 3
std::memcpy(rotation, r, sizeof(float ) * 16);
}
static void ToVectors(const XrQuaternionf& q, XrVector3f& forward,
XrVector3f& right, XrVector3f& up) {
XrVector3f mat[3];
const float ww = q.w * q.w;
const float xx = q.x * q.x;
const float yy = q.y * q.y;
const float zz = q.z * q.z;
mat[0] = {
ww + xx - yy - zz,
2 * (q.x * q.y - q.w * q.z),
2 * (q.x * q.z + q.w * q.y)};
mat[1] = {
2 * (q.x * q.y + q.w * q.z),
ww - xx + yy - zz,
2 * (q.y * q.z - q.w * q.x)};
mat[2] = {
2 * (q.x * q.z - q.w * q.y),
2 * (q.y * q.z + q.w * q.x),
ww - xx - yy + zz};
XrVector3f glFlip[3] = {{0, 0, -1},
{1, 0, 0},
{0, 1, 0}};
XrVector3f f = Matrixf::XrVector3f_Multiply(mat, glFlip[0]);
XrVector3f r = Matrixf::XrVector3f_Multiply(mat, glFlip[1]);
XrVector3f u = Matrixf::XrVector3f_Multiply(mat, glFlip[2]);
forward = {-f.z, -f.x, f.y};
right = {-r.z, -r.x, r.y};
up = {-u.z, -u.x, u.y};
}
}; };
class Posef { class Posef {

View file

@ -37,6 +37,10 @@ License : Licensed under GPLv3 or any later version.
#include <sys/prctl.h> #include <sys/prctl.h>
#include <unistd.h> #include <unistd.h>
#include "video_core/renderer_base.h"
#include "video_core/gpu.h"
#include "core/core.h"
#if defined(DEBUG_INPUT_VERBOSE) #if defined(DEBUG_INPUT_VERBOSE)
#define ALOG_INPUT_VERBOSE(...) ALOGI(__VA_ARGS__) #define ALOG_INPUT_VERBOSE(...) ALOGI(__VA_ARGS__)
#else #else
@ -74,6 +78,8 @@ std::chrono::time_point<std::chrono::steady_clock> gOnCreateStartTime;
std::unique_ptr<OpenXr> gOpenXr; std::unique_ptr<OpenXr> gOpenXr;
MessageQueue gMessageQueue; MessageQueue gMessageQueue;
const std::vector<float> immersiveScaleFactor = {1.0f, 3.0f, 1.8f};
void ForwardButtonStateChangeToCitra(JNIEnv* jni, jobject activityObject, void ForwardButtonStateChangeToCitra(JNIEnv* jni, jobject activityObject,
jmethodID forwardVRInputMethodID, const int androidButtonCode, jmethodID forwardVRInputMethodID, const int androidButtonCode,
const XrBool32 xrButtonState) { const XrBool32 xrButtonState) {
@ -456,6 +462,14 @@ private:
&gOpenXr->mForwardDirectionSpace)); &gOpenXr->mForwardDirectionSpace));
} }
{
const XrReferenceSpaceCreateInfo sci = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
nullptr, XR_REFERENCE_SPACE_TYPE_VIEW,
XrMath::Posef::Identity()};
OXR(xrCreateReferenceSpace(gOpenXr->mSession, &sci,
&gOpenXr->mViewSpace));
}
// Get the pose of the local space. // Get the pose of the local space.
XrSpaceLocation lsl = {XR_TYPE_SPACE_LOCATION}; XrSpaceLocation lsl = {XR_TYPE_SPACE_LOCATION};
OXR(xrLocateSpace(gOpenXr->mForwardDirectionSpace, gOpenXr->mLocalSpace, OXR(xrLocateSpace(gOpenXr->mForwardDirectionSpace, gOpenXr->mLocalSpace,
@ -472,9 +486,95 @@ private:
OXR(xrCreateReferenceSpace(gOpenXr->mSession, &sci, &gOpenXr->mHeadSpace)); OXR(xrCreateReferenceSpace(gOpenXr->mSession, &sci, &gOpenXr->mHeadSpace));
} }
gOpenXr->headLocation = {XR_TYPE_SPACE_LOCATION};
OXR(xrLocateSpace(gOpenXr->mViewSpace, gOpenXr->mHeadSpace, frameState.predictedDisplayTime, &gOpenXr->headLocation));
mInputStateFrame.SyncHandPoses(gOpenXr->mSession, mInputStateStatic, gOpenXr->mLocalSpace, mInputStateFrame.SyncHandPoses(gOpenXr->mSession, mInputStateStatic, gOpenXr->mLocalSpace,
frameState.predictedDisplayTime); frameState.predictedDisplayTime);
//XrMath::Vector3f::
XrVector3f leftVec = {
gOpenXr->headLocation.pose.position.x - mInputStateFrame.mHandPositions[InputStateFrame::LEFT_CONTROLLER].pose.position.x,
gOpenXr->headLocation.pose.position.y - mInputStateFrame.mHandPositions[InputStateFrame::LEFT_CONTROLLER].pose.position.y,
gOpenXr->headLocation.pose.position.z - mInputStateFrame.mHandPositions[InputStateFrame::LEFT_CONTROLLER].pose.position.z,
};
const float lengthLeft = XrMath::Vector3f::Length(leftVec);
XrVector3f rightVec = {
gOpenXr->headLocation.pose.position.x - mInputStateFrame.mHandPositions[InputStateFrame::RIGHT_CONTROLLER].pose.position.x,
gOpenXr->headLocation.pose.position.y - mInputStateFrame.mHandPositions[InputStateFrame::RIGHT_CONTROLLER].pose.position.y,
gOpenXr->headLocation.pose.position.z - mInputStateFrame.mHandPositions[InputStateFrame::RIGHT_CONTROLLER].pose.position.z,
};
const float lengthRight = XrMath::Vector3f::Length(rightVec);
const float length = std::min(lengthLeft, lengthRight);
// This block is for testing which uinform offset is needed
// for a given game to implement new super-immersive profiles if needed
static bool increase = false;
static int uoffset = -1;
{
if (VRSettings::values.vr_immersive_mode > 90)
{
if (mInputStateFrame.mThumbrestTouchState[InputStateFrame::RIGHT_CONTROLLER].currentState)
{
if (increase)
{
++uoffset;
increase = false;
}
//There are 96 Vec4f; since we are applying 4 of them at a time we need to loop
// after 92
if (uoffset > 92)
{
uoffset = 0;
}
}
else
{
increase = true;
}
}
}
bool showLowerPanel = true;
float immersiveModeFactor = (VRSettings::values.vr_immersive_mode < 2) ? immersiveScaleFactor[VRSettings::values.vr_immersive_mode] : immersiveScaleFactor[2];
// Push the HMD position through to the Rasterizer to pass on to the VS Uniform
if (Core::System::GetInstance().IsPoweredOn() &&
Core::System::GetInstance().GPU().Renderer().Rasterizer())
{
if (VRSettings::values.vr_immersive_mode == 0 ||
//If in normal immersive mode then look down for the lower panel to reveal itself (for some reason the Roll function returns pitch)
(VRSettings::values.vr_immersive_mode == 1 && XrMath::Quatf::GetRollInRadians(gOpenXr->headLocation.pose.orientation) < -MATH_FLOAT_PI / 8.0f) ||
//If in "super immersive" mode then put controller next to head in order to disable the mode temporarily
(VRSettings::values.vr_immersive_mode > 2 && length < 0.2))
{
XrVector4f identity[4] = {};
XrMath::Matrixf::Identity(identity);
immersiveModeFactor = 1.0f;
Core::System::GetInstance().GPU().Renderer().Rasterizer()->SetVRData(1, immersiveModeFactor, -1, (float*)identity);
}
else
{
XrVector4f transform[4] = {};
XrMath::Quatf::ToRotationMatrix(gOpenXr->headLocation.pose.orientation, (float*)transform);
//Calculate the inverse
XrVector4f inv_transform[4];
XrMath::Matrixf::ToInverse(transform, inv_transform);
XrQuaternionf invertedOrientation = XrMath::Quatf::Inverted(gOpenXr->headLocation.pose.orientation);
XrVector3f position = XrMath::Quatf::Rotate(invertedOrientation, gOpenXr->headLocation.pose.position);
float gamePosScaler = powf(10.f, VRSettings::values.vr_immersive_positional_game_scaler);
inv_transform[3].x = -position.x * VRSettings::values.vr_immersive_positional_factor * gamePosScaler;
inv_transform[3].y = -position.y * VRSettings::values.vr_immersive_positional_factor * gamePosScaler;
inv_transform[3].z = -position.z * VRSettings::values.vr_immersive_positional_factor * gamePosScaler;
Core::System::GetInstance().GPU().Renderer().Rasterizer()->SetVRData(VRSettings::values.vr_immersive_mode, immersiveModeFactor, uoffset, (float*)inv_transform);
showLowerPanel = false;
}
}
////////////////////////////////////////////////// //////////////////////////////////////////////////
// Set the compositor layers for this frame. // Set the compositor layers for this frame.
////////////////////////////////////////////////// //////////////////////////////////////////////////
@ -490,11 +590,13 @@ private:
layers[layerCount++].Passthrough = passthroughLayer; layers[layerCount++].Passthrough = passthroughLayer;
} }
mGameSurfaceLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount); mGameSurfaceLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount, gOpenXr->headLocation.pose,
immersiveModeFactor, showLowerPanel);
if (mShouldShowErrorMessage) { if (mShouldShowErrorMessage) {
mErrorMessageLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount); mErrorMessageLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount);
} }
if (mIsKeyboardActive) { mKeyboardLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount); } if (mIsKeyboardActive) { mKeyboardLayer->Frame(gOpenXr->mLocalSpace, layers, layerCount); }
{ {

View file

@ -44,6 +44,9 @@ struct Values {
int32_t vr_environment = 0; int32_t vr_environment = 0;
int32_t vr_immersive_mode = 0; int32_t vr_immersive_mode = 0;
bool extra_performance_mode_enabled = false; bool extra_performance_mode_enabled = false;
int32_t vr_si_mode_register_offset = -1;
int32_t vr_immersive_positional_factor = 0;
int32_t vr_immersive_positional_game_scaler = 0;
} extern values; } extern values;
} // namespace VRSettings } // namespace VRSettings

View file

@ -468,4 +468,30 @@
<item>3</item> <item>3</item>
<item>4</item> <item>4</item>
</integer-array> </integer-array>
<string-array name="vrImmersiveModeNames">
<item>Off</item>
<item>180 Degree Immersive</item>
<item>Super Immersive - Profile 1</item>
<item>Super Immersive - Profile 2 (Set Register Offset for game)</item>
</string-array>
<integer-array name="vrImmersiveModeValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
<string-array name="vrPosFactorGameScalerNames">
<item>1x</item>
<item>10x</item>
<item>100x</item>
</string-array>
<integer-array name="vrPosFactorGameScalerValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
</resources> </resources>

View file

@ -682,8 +682,15 @@
<string name="vr_extra_performance_mode">VR Extra Performance Mode</string> <string name="vr_extra_performance_mode">VR Extra Performance Mode</string>
<string name="vr_cpu_level">CPU level</string> <string name="vr_cpu_level">CPU level</string>
<string name="vr_cpu_level_description">Higher levels may improve audio/emulation performance, but will drain the battery faster</string> <string name="vr_cpu_level_description">Higher levels may improve audio/emulation performance, but will drain the battery faster</string>
<string name="immersive_mode">Immersive Mode</string>
<string name="vr_immersive_mode_title">[WARNING: Graphics Artifacts] Immersive Mode (Experimental)</string> <string name="vr_immersive_mode_title">[WARNING: Graphics Artifacts] Immersive Mode (Experimental)</string>
<string name="vr_immersive_mode_description">Extends the top screen around a cylinder for immersive gameplay</string> <string name="vr_immersive_mode_description">Extends the top screen around a cylinder for immersive gameplay</string>
<string name="vr_si_mode_register_offset_title">Super Immersive Mode Profile 2 Register Offset</string>
<string name="vr_si_mode_register_offset_description">Sets the register offset used for the view when using Super Immersive Mode Profile 2 - varies by game</string>
<string name="vr_immersive_pos_factor_title">Immersive Mode Positional Movement Factor</string>
<string name="vr_immersive_pos_factor_description">Adjusts how much movement left/right/up/down affects the game camera (0 = disabled)</string>
<string name="vr_immersive_pos_game_scaler_title">Adjusts Positional Movement Factor by a scaler</string>
<string name="vr_immersive_pos_game_scaler_description">Adjusts how much movement left/right/up/down affects the game camera, some games required larger values, so this multiplies the positional factor</string>
<string name="vr_graphics_warning">DANGER: not all games/levels look good in immersive mode. Large visual artifacts are common when this option is selected</string> <string name="vr_graphics_warning">DANGER: not all games/levels look good in immersive mode. Large visual artifacts are common when this option is selected</string>
<string name="vr_graphics_warning_short">WARNING: this WILL cause visual artifacting in most content</string> <string name="vr_graphics_warning_short">WARNING: this WILL cause visual artifacting in most content</string>
<string name="vr_new_3ds_description">Should be enabled for most titles. When disabled, improves performance of specific titles (e. g. Star Fox 64 3D)</string> <string name="vr_new_3ds_description">Should be enabled for most titles. When disabled, improves performance of specific titles (e. g. Star Fox 64 3D)</string>

View file

@ -556,7 +556,10 @@ struct Values {
u64 audio_bitrate; u64 audio_bitrate;
// VR // VR
Setting<bool> vr_use_immersive_mode{false, "vr_immersive_mode"}; Setting<u32> vr_immersive_mode{0, "vr_immersive_mode"};
Setting<u32> vr_si_mode_register_offset{-1, "vr_si_mode_register_offset"};
Setting<u32> vr_immersive_positional_factor{0, "vr_immersive_positional_factor"};
Setting<u32> vr_immersive_positional_game_scaler{0, "vr_immersive_positional_game_scaler"};
}; };
extern Values values; extern Values values;

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/alignment.h" #include "common/alignment.h"
#include "common/settings.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/pica/pica_core.h" #include "video_core/pica/pica_core.h"
#include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_accelerated.h"
@ -858,4 +859,105 @@ void RasterizerAccelerated::SyncClipPlane() {
} }
} }
void RasterizerAccelerated::SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16])
{
if (vs_uniform_block_data.data.vr_immersive_mode_factor != immersiveModeFactor)
{
vs_uniform_block_data.data.vr_immersive_mode_factor = immersiveModeFactor;
vs_uniform_block_data.dirty = true;
}
vr_uoffset = uoffset;
vr_immersive_mode = vrImmersiveMode;
std::memcpy(vr_view, view, sizeof(float) * 16);
}
static void MatrixTranspose(float m[16], const float src[16]) {
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
m[i * 4 + j] = src[j * 4 + i];
}
}
}
static void MatrixMultiply(float m[16], const float a[16], const float b[16]) {
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
m[i * 4 + j] = a[j] * b[i*4] + a[j+4] * b[i*4+1] + a[j+8] * b[i*4+2] + a[j+12] * b[i*4+3];
}
}
}
void RasterizerAccelerated::ApplyVRDataToPicaVSUniforms(Pica::Shader::Generator::VSPicaUniformData &vs_uniforms)
{
if (vr_immersive_mode)
{
auto &f = vs_uniforms.uniforms.f;
int viewMatrixIndex = -1;
int mode = vr_immersive_mode;
int matrixMode = vr_immersive_mode;
if (vr_immersive_mode > 9)
{
mode = vr_immersive_mode / 10;
matrixMode = vr_immersive_mode % 10;
}
switch (mode)
{
//OOT / MM specific
case 2:
if (vs_uniforms.uniforms.bools[1].b != 0 // this is essential
&& f[0][3] != 1.0) // This fixes the HUD
{
viewMatrixIndex = 4;
}
break;
case 3:
viewMatrixIndex = Settings::values.vr_si_mode_register_offset.GetValue();
break;
case 9:
//TEST MODE
viewMatrixIndex = vr_uoffset;
break;
default:
viewMatrixIndex = -1;
break;
}
if (viewMatrixIndex != -1 && vs_uniforms.uniforms.f.size() > viewMatrixIndex)
{
if (matrixMode == 2)
{
f[viewMatrixIndex][0] = vr_view[0];
f[viewMatrixIndex][1] = vr_view[4];
f[viewMatrixIndex][2] = vr_view[8];
f[viewMatrixIndex + 1][0] = vr_view[1];
f[viewMatrixIndex + 1][1] = vr_view[5];
f[viewMatrixIndex + 1][2] = vr_view[9];
f[viewMatrixIndex + 2][0] = vr_view[2];
f[viewMatrixIndex + 2][1] = vr_view[6];
f[viewMatrixIndex + 2][2] = vr_view[10];
}
else if (matrixMode >= 3)
{
float v[16], v2[16], v3[16];
MatrixTranspose(v, &f[viewMatrixIndex].x);
std::memcpy(v2, vr_view, sizeof(float) * 16);
v2[12] = v2[13] = v2[14] = 0.f;
MatrixMultiply(v3, v2, v);
MatrixTranspose(&f[viewMatrixIndex].x, v3);
}
f[viewMatrixIndex][3] += vr_view[12];
f[viewMatrixIndex + 1][3] += vr_view[13];
f[viewMatrixIndex + 2][3] += vr_view[14];
}
}
}
} // namespace VideoCore } // namespace VideoCore

View file

@ -31,6 +31,8 @@ public:
void SyncEntireState() override; void SyncEntireState() override;
void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16]) override;
protected: protected:
/// Sync fixed-function pipeline state /// Sync fixed-function pipeline state
virtual void SyncFixedState() = 0; virtual void SyncFixedState() = 0;
@ -169,6 +171,15 @@ protected:
std::array<Common::Vec2f, 128> proctex_alpha_map_data{}; std::array<Common::Vec2f, 128> proctex_alpha_map_data{};
std::array<Common::Vec4f, 256> proctex_lut_data{}; std::array<Common::Vec4f, 256> proctex_lut_data{};
std::array<Common::Vec4f, 256> proctex_diff_lut_data{}; std::array<Common::Vec4f, 256> proctex_diff_lut_data{};
//VR Stuff
u32 vr_uoffset = -1;
u32 vr_immersive_mode;
float vr_view[16] = {};
public:
void ApplyVRDataToPicaVSUniforms(Pica::Shader::Generator::VSPicaUniformData &vs_uniforms);
}; };
} // namespace VideoCore } // namespace VideoCore

View file

@ -82,5 +82,8 @@ public:
[[maybe_unused]] const DiskResourceLoadCallback& callback) {} [[maybe_unused]] const DiskResourceLoadCallback& callback) {}
virtual void SyncEntireState() {} virtual void SyncEntireState() {}
/// Set VR position data on the rasterizer
virtual void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16]) {}
}; };
} // namespace VideoCore } // namespace VideoCore

View file

@ -1129,6 +1129,9 @@ void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) {
if (sync_vs_pica) { if (sync_vs_pica) {
VSPicaUniformData vs_uniforms; VSPicaUniformData vs_uniforms;
vs_uniforms.uniforms.SetFromRegs(regs.vs, pica.vs_setup); vs_uniforms.uniforms.SetFromRegs(regs.vs, pica.vs_setup);
ApplyVRDataToPicaVSUniforms(vs_uniforms);
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSPicaData, glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSPicaData,
uniform_buffer.GetHandle(), offset + used_bytes, sizeof(vs_uniforms)); uniform_buffer.GetHandle(), offset + used_bytes, sizeof(vs_uniforms));

View file

@ -1119,6 +1119,9 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
if (sync_vs_pica) { if (sync_vs_pica) {
VSPicaUniformData vs_uniforms; VSPicaUniformData vs_uniforms;
vs_uniforms.uniforms.SetFromRegs(regs.vs, pica.vs_setup); vs_uniforms.uniforms.SetFromRegs(regs.vs, pica.vs_setup);
ApplyVRDataToPicaVSUniforms(vs_uniforms);
std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms));
pipeline_cache.SetBufferOffset(0, offset + used_bytes); pipeline_cache.SetBufferOffset(0, offset + used_bytes);

View file

@ -39,6 +39,7 @@ layout (binding = 1, std140) uniform vs_data {
#endif #endif
bool enable_clip1; bool enable_clip1;
vec4 clip_coef; vec4 clip_coef;
float vr_immersive_mode_factor;
}; };
const vec2 EPSILON_Z = vec2(0.000001f, -1.00001f); const vec2 EPSILON_Z = vec2(0.000001f, -1.00001f);
@ -126,12 +127,10 @@ void main() {
vec4 vtx_pos = SanitizeVertex(vert_position); vec4 vtx_pos = SanitizeVertex(vert_position);
)"; )";
if (!Settings::values.vr_use_immersive_mode.GetValue()) out += R"(
{ gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);
out+= "\ngl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);\n"; gl_Position.xy /= vr_immersive_mode_factor;
} else { )";
out+= "\ngl_Position = vec4(vtx_pos.x / 3.0, vtx_pos.y / 3.0, -vtx_pos.z, vtx_pos.w);\n";
}
if (use_clip_planes) { if (use_clip_planes) {
out += R"( out += R"(
@ -248,14 +247,10 @@ std::string GenerateVertexShader(const ShaderSetup& setup, const PicaVSConfig& c
semantic(VSOutputAttributes::POSITION_Z) + ", " + semantic(VSOutputAttributes::POSITION_Z) + ", " +
semantic(VSOutputAttributes::POSITION_W) + ");\n"; semantic(VSOutputAttributes::POSITION_W) + ");\n";
out += " vtx_pos = SanitizeVertex(vtx_pos);\n"; out += " vtx_pos = SanitizeVertex(vtx_pos);\n";
if (!Settings::values.vr_use_immersive_mode.GetValue()) out += R"(
{ gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);
out+= " gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);\n"; gl_Position.xy /= vr_immersive_mode_factor;
} )";
else
{
out+= " gl_Position = vec4(vtx_pos.x / 3.0, vtx_pos.y / 3.0, -vtx_pos.z, vtx_pos.w);\n";
}
if (config.state.use_clip_planes) { if (config.state.use_clip_planes) {
out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0 out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0
out += " if (enable_clip1) {\n"; out += " if (enable_clip1) {\n";
@ -349,11 +344,10 @@ struct Vertex {
semantic(VSOutputAttributes::POSITION_Z) + ", " + semantic(VSOutputAttributes::POSITION_Z) + ", " +
semantic(VSOutputAttributes::POSITION_W) + ");\n"; semantic(VSOutputAttributes::POSITION_W) + ");\n";
out += " vtx_pos = SanitizeVertex(vtx_pos);\n"; out += " vtx_pos = SanitizeVertex(vtx_pos);\n";
if (!Settings::values.vr_use_immersive_mode.GetValue()) { out += R"(
out+= " gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);\n"; gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);
} else { gl_Position.xy /= vr_immersive_mode_factor;
out+= " gl_Position = vec4(vtx_pos.x / 3.0, vtx_pos.y / 3.0, -vtx_pos.z, vtx_pos.w);\n"; )";
}
if (state.use_clip_planes) { if (state.use_clip_planes) {
out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0 out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0

View file

@ -35,8 +35,6 @@ void PicaGSConfigState::Init(const Pica::RegsInternal& regs, bool use_clip_plane
} }
} }
} }
use_vr_immersive_mode = Settings::values.vr_use_immersive_mode.GetValue();
} }
void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup, void PicaVSConfigState::Init(const Pica::RegsInternal& regs, Pica::ShaderSetup& setup,

View file

@ -58,8 +58,6 @@ struct PicaGSConfigState {
// semantic_maps[semantic name] -> GS output attribute index + component index // semantic_maps[semantic name] -> GS output attribute index + component index
std::array<SemanticMap, 24> semantic_maps; std::array<SemanticMap, 24> semantic_maps;
bool use_vr_immersive_mode;
}; };
/** /**

View file

@ -88,8 +88,11 @@ struct PicaUniformsData {
struct VSUniformData { struct VSUniformData {
u32 enable_clip1; u32 enable_clip1;
alignas(16) Common::Vec4f clip_coef; alignas(16) Common::Vec4f clip_coef;
//VR data at the end to ensure it doesn't interfere with existing uniforms' alignment
alignas(4) float vr_immersive_mode_factor;
}; };
static_assert(sizeof(VSUniformData) == 32, static_assert(sizeof(VSUniformData) == 48,
"The size of the VSUniformData does not match the structure in the shader"); "The size of the VSUniformData does not match the structure in the shader");
static_assert(sizeof(VSUniformData) < 16384, static_assert(sizeof(VSUniformData) < 16384,
"VSUniformData structure must be less than 16kb as per the OpenGL spec"); "VSUniformData structure must be less than 16kb as per the OpenGL spec");