Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lubos VR180 immersive mode #32

Merged
merged 17 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
public final class CheckBoxSetting extends SettingsItem {
private boolean mDefaultValue;
private boolean mShowPerformanceWarning;
private boolean mShowGraphicsWarning;
private SettingsFragmentView mView;

public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
boolean defaultValue, Setting setting) {
super(key, section, setting, titleId, descriptionId);
mDefaultValue = defaultValue;
mShowPerformanceWarning = false;
mShowGraphicsWarning = false;
}

public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
Expand All @@ -27,6 +29,15 @@ public CheckBoxSetting(String key, String section, int titleId, int descriptionI
mShowPerformanceWarning = show_performance_warning;
}

public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
boolean defaultValue, Setting setting, boolean show_performance_warning, boolean show_graphics_warning, SettingsFragmentView view) {
super(key, section, setting, titleId, descriptionId);
mDefaultValue = defaultValue;
mView = view;
mShowPerformanceWarning = show_performance_warning;
mShowGraphicsWarning = show_graphics_warning;
}

public boolean isChecked() {
if (getSetting() == null) {
return mDefaultValue;
Expand Down Expand Up @@ -61,6 +72,9 @@ public IntSetting setChecked(boolean checked) {
if (mShowPerformanceWarning && !checked) {
mView.showToastMessage(CitraApplication.getAppContext().getString(R.string.performance_warning), true);
}
if (mShowGraphicsWarning && checked) {
mView.showToastMessage(CitraApplication.getAppContext().getString(R.string.vr_graphics_warning_short), true);
}

if (getSetting() == null) {
IntSetting setting = new IntSetting(getKey(), getSection(), checked ? 1 : 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,10 @@ private void addVRSettings(ArrayList<SettingsItem> sl) {
Setting vrEnvironment = vrSection.getSetting(SettingsFile.KEY_VR_ENVIRONMENT);
Setting vrExtraPerformanceMode = vrSection.getSetting(SettingsFile.KEY_VR_EXTRA_PERFORMANCE_MODE);
Setting vrCpuLevel = vrSection.getSetting(SettingsFile.KEY_VR_CPU_LEVEL);
Setting vrImmersiveMode = vrSection.getSetting(SettingsFile.KEY_VR_IMMERSIVE_MODE);
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));
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));
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));

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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ public final class SettingsFile {
public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip";
public static final String KEY_VR_ENVIRONMENT = "vr_environment";
public static final String KEY_VR_EXTRA_PERFORMANCE_MODE = "vr_extra_performance_mode";

public static final String KEY_VR_CPU_LEVEL = "vr_cpu_level";

public static final String KEY_VR_IMMERSIVE_MODE = "vr_immersive_mode";
public static final String KEY_LOG_FILTER = "log_filter";

private static BiMap<String, String> sectionsMap = new BiMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ protected void onCreate(Bundle savedInstanceState) {
requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// Request permission if it's not granted
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 0);
}
// Check for the TWO_INSTANCES string extra
if (getIntent().getBooleanExtra(VrActivity.EXTRA_ERROR_TWO_INSTANCES, false)) {
Log.error("Error: two instances of CitraVr::VrActivity were running at the same time!");
Expand Down Expand Up @@ -214,10 +218,6 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
@Override
protected void onResume() {
super.onResume();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// Request permission if it's not granted
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 0);
}
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));

ThemeUtil.setSystemBarMode(this, ThemeUtil.getIsLightMode(getResources()));
Expand Down
18 changes: 16 additions & 2 deletions src/android/app/src/main/jni/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,25 @@ void Config::ReadValues() {
"VR", "vr_environment",
static_cast<long>(VRSettings::values.hmd_type == VRSettings::HMDType::QUEST3 ?
VRSettings::VREnvironmentType::PASSTHROUGH : VRSettings::VREnvironmentType::VOID));

VRSettings::values.cpu_level =
VRSettings::values.extra_performance_mode_enabled ? XR_HIGHEST_CPU_PERF_LEVEL
: VRSettings::CPUPrefToPerfSettingsLevel(sdl2_config->GetInteger(
"VR", "vr_cpu_level", 3));
"VR", "vr_cpu_level", 3));
Settings::values.vr_use_immersive_mode = sdl2_config->GetBoolean(
"VR", "vr_immersive_mode", false);

if (Settings::values.vr_use_immersive_mode) {
LOG_INFO(Config, "VR immersive mode enabled");

// no point rendering passthrough in immersive mode
VRSettings::values.vr_environment =
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
ReadSetting("Miscellaneous", Settings::values.log_filter);
Expand Down
35 changes: 27 additions & 8 deletions src/android/app/src/main/jni/vr/layers/GameSurfaceLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ License : Licensed under GPLv3 or any later version.

#include "GameSurfaceLayer.h"

#include "../vr_settings.h"

#include "../utils/JniUtils.h"
#include "../utils/LogUtils.h"
#include "../utils/SyspropUtils.h"
Expand All @@ -29,6 +31,9 @@ License : Licensed under GPLv3 or any later version.
namespace {

constexpr float lowerPanelScaleFactor = 0.75f;

const std::vector<float> immersiveLevelFactor = {1.0f, 5.0f, 3.0f};

/** Used to translate texture coordinates into the corresponding coordinates
* on the Android Activity Window.
*
Expand Down Expand Up @@ -223,9 +228,16 @@ GameSurfaceLayer::GameSurfaceLayer(const XrVector3f&& position, JNIEnv* env, job
const XrSession& session, const uint32_t resolutionFactor)
: session_(session), topPanelFromWorld_(CreateTopPanelFromWorld(position)),
lowerPanelFromWorld_(CreateLowerPanelFromWorld(topPanelFromWorld_)),
resolutionFactor_(resolutionFactor), env_(env), activityObject_(activityObject)
resolutionFactor_(resolutionFactor), immersiveMode_(VRSettings::values.vr_immersive_mode),
env_(env), activityObject_(activityObject)

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

// Create the Top Display Panel (Curved display)
for (uint32_t eye = 0; eye < NUM_EYES; eye++) {
Expand Down Expand Up @@ -284,7 +296,9 @@ void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLaye
// scale of the texture.
const float radius = GetRadiusSysprop();
layer.radius = radius;
layer.centralAngle = GetCentralAngleSysprop() * MATH_FLOAT_PI / 180.0f;
layer.centralAngle = (!immersiveMode_ ? GetCentralAngleSysprop()
: 55.0f * immersiveLevelFactor[immersiveMode_]) *
MATH_FLOAT_PI / 180.0f;
layer.aspectRatio = -aspectRatio;
layers[layerCount++].mCylinder = layer;
}
Expand Down Expand Up @@ -343,10 +357,15 @@ void GameSurfaceLayer::Frame(const XrSpace& space, std::vector<XrCompositionLaye
layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
memset(&layer.subImage, 0, sizeof(XrSwapchainSubImage));
layer.subImage.swapchain = swapchain_.Handle;
layer.subImage.imageRect.offset.x = cropHoriz / 2;
layer.subImage.imageRect.offset.y = panelHeight + verticalBorderTex;
layer.subImage.imageRect.extent.width = panelWidth - cropHoriz;
layer.subImage.imageRect.extent.height = panelHeight;
layer.subImage.imageRect.offset.x =
(cropHoriz / 2) / immersiveLevelFactor[immersiveMode_] +
panelWidth * (0.5f - (0.5f / immersiveLevelFactor[immersiveMode_]));
layer.subImage.imageRect.offset.y =
panelHeight + verticalBorderTex +
panelHeight * (0.5f - (0.5f / immersiveLevelFactor[immersiveMode_]));
layer.subImage.imageRect.extent.width =
(panelWidth - cropHoriz) / immersiveLevelFactor[immersiveMode_];
layer.subImage.imageRect.extent.height = panelHeight / immersiveLevelFactor[immersiveMode_];
layer.subImage.imageArrayIndex = 0;
layer.pose = lowerPanelFromWorld_;
const auto scale = GetDensityScaleForSize(panelWidth - cropHoriz, -panelHeight,
Expand Down
12 changes: 12 additions & 0 deletions src/android/app/src/main/jni/vr/layers/GameSurfaceLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ class GameSurfaceLayer {
// resolution.
const uint32_t resolutionFactor_;

// EXPERIMENTAL: When true, the top screen + its extents
// (previously-unseen parts of the scene) are projected onto a 275-degree
// cylinder. Note that the perceived resolution is lower,
// as the output image is larger, both in texture size and perceived layer
// size.
//
// Rendering a higher-resolution image would likely require
// performance optimizations to avoid maxing out the GPU, e.g.:
// - Multiview (requires a merged Citra/CitraVR renderer)
// - Rendering the top-screen and bottom screen separately.
const uint32_t immersiveMode_;

//============================
// JNI objects
JNIEnv* env_ = nullptr;
Expand Down
9 changes: 7 additions & 2 deletions src/android/app/src/main/jni/vr/vr_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,14 @@ class VRApp {
const uint32_t defaultResolutionFactor =
GetDefaultGameResolutionFactorForHmd(VRSettings::values.hmd_type);
const uint32_t resolutionFactorFromPreferences = VRSettings::values.resolution_factor;
const uint32_t resolutionFactor = resolutionFactorFromPreferences > 0
// add a couple factors to resolution with immersive mode so users
// aren't resetting their default settings to get higher res. min
// resolution factor for immersive is 3x.
const uint32_t immersiveModeOffset = (VRSettings::values.vr_immersive_mode > 0) ? 2 : 0;
const uint32_t resolutionFactor = (resolutionFactorFromPreferences > 0
? resolutionFactorFromPreferences
: defaultResolutionFactor;
: defaultResolutionFactor) + immersiveModeOffset;

if (resolutionFactor != defaultResolutionFactor) {
ALOGI("Using resolution factor of {}x instead of HMD default {}x", resolutionFactor,
defaultResolutionFactor);
Expand Down
7 changes: 4 additions & 3 deletions src/android/app/src/main/jni/vr/vr_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ std::string GetHMDTypeStr();
HMDType HmdTypeFromStr(const std::string& hmdType);

struct Values {
bool extra_performance_mode_enabled = false;
int32_t vr_environment = 0;
XrPerfSettingsLevelEXT cpu_level = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT;
uint32_t resolution_factor = 0;
HMDType hmd_type = HMDType::UNKNOWN;
uint32_t resolution_factor = 0;
int32_t vr_environment = 0;
int32_t vr_immersive_mode = 0;
bool extra_performance_mode_enabled = false;
} extern values;

} // namespace VRSettings
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,8 @@
<string name="vr_extra_performance_mode">VR zusätzlicher Leistungsmodus</string>
<string name="vr_cpu_level">CPU -Ebene</string>
<string name="vr_cpu_level_description">Höhere Werte können die Audio-/Emulationsleistung verbessern, die Batterie jedoch schneller abtropfen lassen</string>
<string name="vr_immersive_mode_title">[Warnung: Grafikartefakte] Immersive Modus (experimentell)</string>
<string name="vr_immersive_mode_description">Erweitert den oberen Bildschirm um einen Zylinder für ein immersives Gameplay</string>
<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>
<string name="vr_graphics_warning_short">Warnung: Dies führt zu visuellen Artefakten in den meisten Inhalten</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,8 @@
<string name="vr_extra_performance_mode">Modo de rendimiento adicional VR</string>
<string name="vr_cpu_level">Nivel de la CPU</string>
<string name="vr_cpu_level_description">Los niveles más altos pueden mejorar el rendimiento de audio/emulación, pero drenarán la batería más rápido</string>
<string name="vr_immersive_mode_title">[Advertencia: artefactos gráficos] Modo inmersivo (experimental)</string>
<string name="vr_immersive_mode_description">Extiende la pantalla superior alrededor de un cilindro para un juego inmersivo</string>
<string name="vr_graphics_warning">Danger: No todos los juegos/niveles se ven bien en modo inmersivo. Los grandes artefactos visuales son comunes cuando se selecciona esta opción</string>
<string name="vr_graphics_warning_short">ADVERTENCIA: Esto causará artefactos visuales en la mayoría de los contenidos</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-fi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,8 @@
<string name="vr_extra_performance_mode">VR Extra Performance -tila</string>
<string name="vr_cpu_level">CPU -taso</string>
<string name="vr_cpu_level_description">Korkeammat tasot voivat parantaa audio/emulointia, mutta tyhjentää akun nopeammin</string>
<string name="vr_immersive_mode_title">[VAROITUS: Grafiikkaesineitä] Impersiivinen tila (kokeellinen)</string>
<string name="vr_immersive_mode_description">Laajentaa yläreunaa sylinterin ympärille syventävään pelattamiseen</string>
<string name="vr_graphics_warning">Vaara: Kaikki pelit/tasot eivät näytä hyvältä syventävässä tilassa. Suuret visuaaliset esineet ovat yleisiä, kun tämä vaihtoehto on valittu</string>
<string name="vr_graphics_warning_short">VAROITUS: Tämä aiheuttaa visuaalista esinettä useimmissa sisällöissä</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,8 @@
<string name="vr_extra_performance_mode">Mode de performance supplémentaire VR</string>
<string name="vr_cpu_level">Niveau du processeur</string>
<string name="vr_cpu_level_description">Des niveaux plus élevés peuvent améliorer les performances audio / émulation, mais draineront la batterie plus rapidement</string>
<string name="vr_immersive_mode_title">[AVERTISSEMENT: Artefacts graphiques] Mode immersif (expérimental)</string>
<string name="vr_immersive_mode_description">Étend l\'écran supérieur autour d\'un cylindre pour un gameplay immersif</string>
<string name="vr_graphics_warning">Danger: Tous les jeux / niveaux ne sont pas bons en mode immersif. Les grands artefacts visuels sont courants lorsque cette option est sélectionnée</string>
<string name="vr_graphics_warning_short">AVERTISSEMENT: Cela provoquera un artefact visuel dans la plupart des contenus</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,8 @@
<string name="vr_extra_performance_mode">Modalità di prestazione extra VR</string>
<string name="vr_cpu_level">Livello CPU</string>
<string name="vr_cpu_level_description">Livelli più alti possono migliorare le prestazioni audio/emulazione, ma prosciugheranno la batteria più velocemente</string>
<string name="vr_immersive_mode_title">[ATTENZIONE: artefatti grafici] Modalità immersiva (sperimentale)</string>
<string name="vr_immersive_mode_description">Estende lo schermo superiore attorno a un cilindro per il gameplay immersivo</string>
<string name="vr_graphics_warning">Pericolo: non tutti i giochi/livelli hanno un bell\'aspetto in modalità immersiva. I grandi manufatti visivi sono comuni quando viene selezionata questa opzione</string>
<string name="vr_graphics_warning_short">ATTENZIONE: questo causerà un artefatto visivo nella maggior parte dei contenuti</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,8 @@
<string name="vr_extra_performance_mode">VR追加パフォーマンスモード</string>
<string name="vr_cpu_level">CPUレベル</string>
<string name="vr_cpu_level_description">より高いレベルはオーディオ/エミュレーションのパフォーマンスを改善する可能性がありますが、バッテリーをより速く排出します</string>
<string name="vr_immersive_mode_title">[警告:グラフィックアーティファクト]没入型モード(実験)</string>
<string name="vr_immersive_mode_description">没入型ゲームプレイのために、シリンダーの周りのトップ画面を拡張します</string>
<string name="vr_graphics_warning">危険:すべてのゲーム/レベルが没入型モードでよく見えるわけではありません。このオプションが選択されている場合、大きな視覚アーティファクトが一般的です</string>
<string name="vr_graphics_warning_short">警告:これは、ほとんどのコンテンツで視覚的なアーティファクトを引き起こします</string>
</resources>
4 changes: 4 additions & 0 deletions src/android/app/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,8 @@
<string name="vr_extra_performance_mode">VR 추가 성능 모드</string>
<string name="vr_cpu_level">CPU 레벨</string>
<string name="vr_cpu_level_description">레벨이 높을수록 오디오/에뮬레이션 성능이 향상 될 수 있지만 배터리가 더 빨리 배터리를 배출합니다. </string>
<string name="vr_immersive_mode_title">[경고 : 그래픽 아티팩트] 몰입 모드 (실험)</string>
<string name="vr_immersive_mode_description">몰입 형 게임 플레이를 위해 실린더 주위에 상단 화면을 확장합니다. </string>
<string name="vr_graphics_warning">위험 : 모든 게임/레벨이 몰입 형 모드에서 좋아 보이는 것은 아닙니다. 이 옵션을 선택할 때 큰 시각적 아티팩트가 일반적입니다. </string>
<string name="vr_graphics_warning_short">경고 : 대부분의 콘텐츠에서 시각적 아티팩트가 발생합니다. </string>
</resources>