Skip to content

Commit 5cab348

Browse files
authored
Merge pull request #32 from amwatson/lubos_immersive_mode
Lubos VR180 immersive mode
2 parents 350f964 + 0719680 commit 5cab348

File tree

27 files changed

+164
-24
lines changed

27 files changed

+164
-24
lines changed

src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/CheckBoxSetting.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
public final class CheckBoxSetting extends SettingsItem {
1111
private boolean mDefaultValue;
1212
private boolean mShowPerformanceWarning;
13+
private boolean mShowGraphicsWarning;
1314
private SettingsFragmentView mView;
1415

1516
public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
1617
boolean defaultValue, Setting setting) {
1718
super(key, section, setting, titleId, descriptionId);
1819
mDefaultValue = defaultValue;
1920
mShowPerformanceWarning = false;
21+
mShowGraphicsWarning = false;
2022
}
2123

2224
public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
@@ -27,6 +29,15 @@ public CheckBoxSetting(String key, String section, int titleId, int descriptionI
2729
mShowPerformanceWarning = show_performance_warning;
2830
}
2931

32+
public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
33+
boolean defaultValue, Setting setting, boolean show_performance_warning, boolean show_graphics_warning, SettingsFragmentView view) {
34+
super(key, section, setting, titleId, descriptionId);
35+
mDefaultValue = defaultValue;
36+
mView = view;
37+
mShowPerformanceWarning = show_performance_warning;
38+
mShowGraphicsWarning = show_graphics_warning;
39+
}
40+
3041
public boolean isChecked() {
3142
if (getSetting() == null) {
3243
return mDefaultValue;
@@ -61,6 +72,9 @@ public IntSetting setChecked(boolean checked) {
6172
if (mShowPerformanceWarning && !checked) {
6273
mView.showToastMessage(CitraApplication.getAppContext().getString(R.string.performance_warning), true);
6374
}
75+
if (mShowGraphicsWarning && checked) {
76+
mView.showToastMessage(CitraApplication.getAppContext().getString(R.string.vr_graphics_warning_short), true);
77+
}
6478

6579
if (getSetting() == null) {
6680
IntSetting setting = new IntSetting(getKey(), getSection(), checked ? 1 : 0);

src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,10 @@ private void addVRSettings(ArrayList<SettingsItem> sl) {
432432
Setting vrEnvironment = vrSection.getSetting(SettingsFile.KEY_VR_ENVIRONMENT);
433433
Setting vrExtraPerformanceMode = vrSection.getSetting(SettingsFile.KEY_VR_EXTRA_PERFORMANCE_MODE);
434434
Setting vrCpuLevel = vrSection.getSetting(SettingsFile.KEY_VR_CPU_LEVEL);
435+
Setting vrImmersiveMode = vrSection.getSetting(SettingsFile.KEY_VR_IMMERSIVE_MODE);
435436
sl.add(new SingleChoiceSetting(SettingsFile.KEY_VR_ENVIRONMENT, Settings.SECTION_VR, R.string.vr_background, 0, R.array.vrBackgroundNames, R.array.vrBackgroundValues, VRUtils.getHMDType() == VRUtils.HMDType.QUEST3.getValue() ? 1 : 2, vrEnvironment));
436437
sl.add(new CheckBoxSetting(SettingsFile.KEY_VR_EXTRA_PERFORMANCE_MODE, Settings.SECTION_VR, R.string.vr_extra_performance_mode, R.string.vr_extra_performance_mode_description, false, vrExtraPerformanceMode));
437438
sl.add(new SingleChoiceSetting(SettingsFile.KEY_VR_CPU_LEVEL, Settings.SECTION_VR, R.string.vr_cpu_level, R.string.vr_cpu_level_description, R.array.vrCpuLevelNames, R.array.vrCpuLevelValues, 3, vrCpuLevel));
438-
439+
sl.add(new CheckBoxSetting(SettingsFile.KEY_VR_IMMERSIVE_MODE, Settings.SECTION_VR, R.string.vr_immersive_mode_title, R.string.vr_immersive_mode_description, false, vrImmersiveMode, false, true, mView));
439440
}
440441
}

src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ public final class SettingsFile {
133133
public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip";
134134
public static final String KEY_VR_ENVIRONMENT = "vr_environment";
135135
public static final String KEY_VR_EXTRA_PERFORMANCE_MODE = "vr_extra_performance_mode";
136-
137136
public static final String KEY_VR_CPU_LEVEL = "vr_cpu_level";
138137

138+
public static final String KEY_VR_IMMERSIVE_MODE = "vr_immersive_mode";
139139
public static final String KEY_LOG_FILTER = "log_filter";
140140

141141
private static BiMap<String, String> sectionsMap = new BiMap<>();

src/android/app/src/main/java/org/citra/citra_emu/ui/main/MainActivity.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ protected void onCreate(Bundle savedInstanceState) {
184184
requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
185185
}
186186
}
187+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
188+
// Request permission if it's not granted
189+
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 0);
190+
}
187191
// Check for the TWO_INSTANCES string extra
188192
if (getIntent().getBooleanExtra(VrActivity.EXTRA_ERROR_TWO_INSTANCES, false)) {
189193
Log.error("Error: two instances of CitraVr::VrActivity were running at the same time!");
@@ -214,10 +218,6 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
214218
@Override
215219
protected void onResume() {
216220
super.onResume();
217-
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
218-
// Request permission if it's not granted
219-
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 0);
220-
}
221221
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
222222

223223
ThemeUtil.setSystemBarMode(this, ThemeUtil.getIsLightMode(getResources()));

src/android/app/src/main/jni/config.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,25 @@ void Config::ReadValues() {
301301
"VR", "vr_environment",
302302
static_cast<long>(VRSettings::values.hmd_type == VRSettings::HMDType::QUEST3 ?
303303
VRSettings::VREnvironmentType::PASSTHROUGH : VRSettings::VREnvironmentType::VOID));
304-
305304
VRSettings::values.cpu_level =
306305
VRSettings::values.extra_performance_mode_enabled ? XR_HIGHEST_CPU_PERF_LEVEL
307306
: VRSettings::CPUPrefToPerfSettingsLevel(sdl2_config->GetInteger(
308-
"VR", "vr_cpu_level", 3));
307+
"VR", "vr_cpu_level", 3));
308+
Settings::values.vr_use_immersive_mode = sdl2_config->GetBoolean(
309+
"VR", "vr_immersive_mode", false);
310+
311+
if (Settings::values.vr_use_immersive_mode) {
312+
LOG_INFO(Config, "VR immersive mode enabled");
313+
314+
// no point rendering passthrough in immersive mode
315+
VRSettings::values.vr_environment =
316+
static_cast<uint32_t>(VRSettings::VREnvironmentType::VOID);
317+
// We originally had two immersive modes, but I cut them down to fit in
318+
// the shader map's bitfield.
319+
VRSettings::values.vr_immersive_mode = 2;
320+
// When immersive mode is enabled, only OpenGL is supported.
321+
Settings::values.graphics_api = Settings::GraphicsAPI::OpenGL;
322+
}
309323

310324
// Miscellaneous
311325
ReadSetting("Miscellaneous", Settings::values.log_filter);

src/android/app/src/main/jni/vr/layers/GameSurfaceLayer.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ License : Licensed under GPLv3 or any later version.
1414

1515
#include "GameSurfaceLayer.h"
1616

17+
#include "../vr_settings.h"
18+
1719
#include "../utils/JniUtils.h"
1820
#include "../utils/LogUtils.h"
1921
#include "../utils/SyspropUtils.h"
@@ -29,6 +31,9 @@ License : Licensed under GPLv3 or any later version.
2931
namespace {
3032

3133
constexpr float lowerPanelScaleFactor = 0.75f;
34+
35+
const std::vector<float> immersiveLevelFactor = {1.0f, 5.0f, 3.0f};
36+
3237
/** Used to translate texture coordinates into the corresponding coordinates
3338
* on the Android Activity Window.
3439
*
@@ -223,9 +228,16 @@ GameSurfaceLayer::GameSurfaceLayer(const XrVector3f&& position, JNIEnv* env, job
223228
const XrSession& session, const uint32_t resolutionFactor)
224229
: session_(session), topPanelFromWorld_(CreateTopPanelFromWorld(position)),
225230
lowerPanelFromWorld_(CreateLowerPanelFromWorld(topPanelFromWorld_)),
226-
resolutionFactor_(resolutionFactor), env_(env), activityObject_(activityObject)
231+
resolutionFactor_(resolutionFactor), immersiveMode_(VRSettings::values.vr_immersive_mode),
232+
env_(env), activityObject_(activityObject)
227233

228234
{
235+
assert(immersiveMode_ == 0 || immersiveMode_ == 1);
236+
if (immersiveMode_ > 0) {
237+
ALOGI("Using VR immersive mode {}", immersiveMode_);
238+
topPanelFromWorld_.position.z = lowerPanelFromWorld_.position.z;
239+
lowerPanelFromWorld_.position.y = -1.0f - (0.5f * (immersiveMode_ - 1));
240+
}
229241
const int32_t initializationStatus = Init(activityObject, position, session);
230242
if (initializationStatus < 0) {
231243
FAIL("Could not initialize GameSurfaceLayer -- error '%d'", initializationStatus);
@@ -252,8 +264,8 @@ void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLaye
252264
static_cast<double>(2 * panelWidth) / static_cast<double>(panelHeight);
253265
// Prevent a seam between the top and bottom view
254266
constexpr uint32_t verticalBorderTex = 1;
255-
const int32_t useCylinder = GetCylinderSysprop();
256-
if (useCylinder == 1) {
267+
const bool useCylinder = (GetCylinderSysprop() != 0) || (immersiveMode_ > 0);
268+
if (useCylinder) {
257269

258270
// Create the Top Display Panel (Curved display)
259271
for (uint32_t eye = 0; eye < NUM_EYES; eye++) {
@@ -284,7 +296,9 @@ void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLaye
284296
// scale of the texture.
285297
const float radius = GetRadiusSysprop();
286298
layer.radius = radius;
287-
layer.centralAngle = GetCentralAngleSysprop() * MATH_FLOAT_PI / 180.0f;
299+
layer.centralAngle = (!immersiveMode_ ? GetCentralAngleSysprop()
300+
: 55.0f * immersiveLevelFactor[immersiveMode_]) *
301+
MATH_FLOAT_PI / 180.0f;
288302
layer.aspectRatio = -aspectRatio;
289303
layers[layerCount++].mCylinder = layer;
290304
}
@@ -343,10 +357,15 @@ void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLaye
343357
layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
344358
memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage));
345359
layer.subImage.swapchain = swapchain_.Handle;
346-
layer.subImage.imageRect.offset.x = cropHoriz / 2;
347-
layer.subImage.imageRect.offset.y = panelHeight + verticalBorderTex;
348-
layer.subImage.imageRect.extent.width = panelWidth - cropHoriz;
349-
layer.subImage.imageRect.extent.height = panelHeight;
360+
layer.subImage.imageRect.offset.x =
361+
(cropHoriz / 2) / immersiveLevelFactor[immersiveMode_] +
362+
panelWidth * (0.5f - (0.5f / immersiveLevelFactor[immersiveMode_]));
363+
layer.subImage.imageRect.offset.y =
364+
panelHeight + verticalBorderTex +
365+
panelHeight * (0.5f - (0.5f / immersiveLevelFactor[immersiveMode_]));
366+
layer.subImage.imageRect.extent.width =
367+
(panelWidth - cropHoriz) / immersiveLevelFactor[immersiveMode_];
368+
layer.subImage.imageRect.extent.height = panelHeight / immersiveLevelFactor[immersiveMode_];
350369
layer.subImage.imageArrayIndex = 0;
351370
layer.pose = lowerPanelFromWorld_;
352371
const auto scale = GetDensityScaleForSize(panelWidth - cropHoriz, -panelHeight,

src/android/app/src/main/jni/vr/layers/GameSurfaceLayer.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,18 @@ class GameSurfaceLayer {
158158
// resolution.
159159
const uint32_t resolutionFactor_;
160160

161+
// EXPERIMENTAL: When true, the top screen + its extents
162+
// (previously-unseen parts of the scene) are projected onto a 275-degree
163+
// cylinder. Note that the perceived resolution is lower,
164+
// as the output image is larger, both in texture size and perceived layer
165+
// size.
166+
//
167+
// Rendering a higher-resolution image would likely require
168+
// performance optimizations to avoid maxing out the GPU, e.g.:
169+
// - Multiview (requires a merged Citra/CitraVR renderer)
170+
// - Rendering the top-screen and bottom screen separately.
171+
const uint32_t immersiveMode_;
172+
161173
//============================
162174
// JNI objects
163175
JNIEnv* env_ = nullptr;

src/android/app/src/main/jni/vr/vr_main.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,14 @@ class VRApp {
206206
const uint32_t defaultResolutionFactor =
207207
GetDefaultGameResolutionFactorForHmd(VRSettings::values.hmd_type);
208208
const uint32_t resolutionFactorFromPreferences = VRSettings::values.resolution_factor;
209-
const uint32_t resolutionFactor = resolutionFactorFromPreferences > 0
209+
// add a couple factors to resolution with immersive mode so users
210+
// aren't resetting their default settings to get higher res. min
211+
// resolution factor for immersive is 3x.
212+
const uint32_t immersiveModeOffset = (VRSettings::values.vr_immersive_mode > 0) ? 2 : 0;
213+
const uint32_t resolutionFactor = (resolutionFactorFromPreferences > 0
210214
? resolutionFactorFromPreferences
211-
: defaultResolutionFactor;
215+
: defaultResolutionFactor) + immersiveModeOffset;
216+
212217
if (resolutionFactor != defaultResolutionFactor) {
213218
ALOGI("Using resolution factor of {}x instead of HMD default {}x", resolutionFactor,
214219
defaultResolutionFactor);

src/android/app/src/main/jni/vr/vr_settings.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ std::string GetHMDTypeStr();
3737
HMDType HmdTypeFromStr(const std::string& hmdType);
3838

3939
struct Values {
40-
bool extra_performance_mode_enabled = false;
41-
int32_t vr_environment = 0;
4240
XrPerfSettingsLevelEXT cpu_level = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT;
43-
uint32_t resolution_factor = 0;
4441
HMDType hmd_type = HMDType::UNKNOWN;
42+
uint32_t resolution_factor = 0;
43+
int32_t vr_environment = 0;
44+
int32_t vr_immersive_mode = 0;
45+
bool extra_performance_mode_enabled = false;
4546
} extern values;
4647

4748
} // namespace VRSettings

src/android/app/src/main/res/values-de/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,8 @@
173173
<string name="vr_extra_performance_mode">VR zusätzlicher Leistungsmodus</string>
174174
<string name="vr_cpu_level">CPU -Ebene</string>
175175
<string name="vr_cpu_level_description">Höhere Werte können die Audio-/Emulationsleistung verbessern, die Batterie jedoch schneller abtropfen lassen</string>
176+
<string name="vr_immersive_mode_title">[Warnung: Grafikartefakte] Immersive Modus (experimentell)</string>
177+
<string name="vr_immersive_mode_description">Erweitert den oberen Bildschirm um einen Zylinder für ein immersives Gameplay</string>
178+
<string name="vr_graphics_warning">Gefahr: Nicht alle Spiele/Levels sehen im beeindruckenden Modus gut aus. Große visuelle Artefakte sind häufig, wenn diese Option ausgewählt ist</string>
179+
<string name="vr_graphics_warning_short">Warnung: Dies führt zu visuellen Artefakten in den meisten Inhalten</string>
176180
</resources>

0 commit comments

Comments
 (0)