diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index b556bc34e..4885c7fac 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -191,6 +191,7 @@ protected void onCreate(Bundle savedInstanceState) { final String tempPath = getCacheDir().getAbsolutePath(); queueRunnable(() -> setTemporaryFilePath(tempPath)); setCylinderDensity(SettingsStore.getInstance(this).getCylinderDensity()); + updateFoveatedLevel(); initializeWorld(); // Setup the search engine @@ -961,6 +962,13 @@ public void updateEnvironment() { queueRunnable(() -> updateEnvironmentNative()); } + @Override + public void updateFoveatedLevel() { + final int appLevel = SettingsStore.getInstance(this).getFoveatedLevelApp(); + final int webVRLevel = SettingsStore.getInstance(this).getFoveatedLevelWebVR(); + queueRunnable(() -> updateFoveatedLevelNative(appLevel, webVRLevel)); + } + @Override public void updatePointerColor() { queueRunnable(() -> updatePointerColorNative()); @@ -1029,4 +1037,5 @@ public void setCylinderDensity(final float aDensity) { private native void runCallbackNative(long aCallback); private native void setCylinderDensityNative(float aDensity); private native void setIsServo(boolean aIsServo); + private native void updateFoveatedLevelNative(int appLevel, int webVRLevel); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java index ccb3e9e72..0c3c3341d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -58,6 +58,8 @@ SettingsStore getInstance(final @NonNull Context aContext) { public final static int MSAA_DEFAULT_LEVEL = 1; public final static boolean AUDIO_ENABLED = false; public final static float CYLINDER_DENSITY_ENABLED_DEFAULT = 4680.0f; + public final static int FOVEATED_APP_DEFAULT_LEVEL = 1; + public final static int FOVEATED_WEBVR_DEFAULT_LEVEL = 0; // Enable telemetry by default (opt-out). private final static boolean enableCrashReportingByDefault = false; @@ -360,7 +362,6 @@ public void setMSAALevel(int level) { editor.commit(); } - public boolean getLayersEnabled() { if (BuildConfig.FLAVOR_platform.equalsIgnoreCase("oculusvr")) { return true; @@ -404,6 +405,28 @@ public float getCylinderDensity() { public void setCylinderDensity(float aDensity) { SharedPreferences.Editor editor = mPrefs.edit(); editor.putFloat(mContext.getString(R.string.settings_key_cylinder_density), aDensity); + } + + public int getFoveatedLevelApp() { + return mPrefs.getInt( + mContext.getString(R.string.settings_key_foveated_app), FOVEATED_APP_DEFAULT_LEVEL); + } + + public int getFoveatedLevelWebVR() { + return mPrefs.getInt( + mContext.getString(R.string.settings_key_foveated_webvr), FOVEATED_WEBVR_DEFAULT_LEVEL); + } + + public void setFoveatedLevelApp(int level) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putInt(mContext.getString(R.string.settings_key_foveated_app), level); + editor.commit(); + } + + public void setFoveatedLevelWebVR(int level) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putInt(mContext.getString(R.string.settings_key_foveated_webvr), level); editor.commit(); } } + diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java index 2a13cf947..ea3d1e8d1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java @@ -121,6 +121,10 @@ public void setOnCheckedChangeListener(OnCheckedChangeListener aListener) { mRadioGroupListener = aListener; } + public OnCheckedChangeListener getOnCheckdChangeListener() { + return mRadioGroupListener; + } + public Object getValueForId(@IdRes int checkId) { return mValues[checkId]; } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java index 33bee3a7a..374ee5f71 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java @@ -42,6 +42,7 @@ interface WorldClickListener { void setIsServoSession(boolean aIsServo); void keyboardDismissed(); void updateEnvironment(); + void updateFoveatedLevel(); void updatePointerColor(); void showVRVideo(int aWindowHandle, @VideoProjectionMenuWidget.VideoProjectionFlags int aVideoProjection); void hideVRVideo(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DisplayOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DisplayOptionsWidget.java index 2ac2d31e8..05436df05 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DisplayOptionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DisplayOptionsWidget.java @@ -9,12 +9,15 @@ import android.util.AttributeSet; import android.view.View; import android.widget.CompoundButton; +import android.widget.RadioGroup; import android.widget.ScrollView; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.browser.SessionStore; import org.mozilla.vrbrowser.browser.SettingsStore; +import org.mozilla.vrbrowser.BuildConfig; +import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.views.settings.ButtonSetting; import org.mozilla.vrbrowser.ui.views.settings.DoubleEditSetting; @@ -36,6 +39,8 @@ public class DisplayOptionsWidget extends UIWidget implements private SwitchSetting mCurvedDisplaySwitch; private RadioGroupSetting mUaModeRadio; private RadioGroupSetting mMSAARadio; + private RadioGroupSetting mFoveatedAppRadio; + private RadioGroupSetting mFoveatedWebVRRadio; private SingleEditSetting mDensityEdit; private SingleEditSetting mDpiEdit; @@ -97,6 +102,21 @@ private void initialize(Context aContext) { mMSAARadio.setOnCheckedChangeListener(mMSSAChangeListener); setMSAAMode(mMSAARadio.getIdForValue(msaaLevel), false); + mFoveatedAppRadio = findViewById(R.id.foveated_app_radio); + mFoveatedWebVRRadio = findViewById(R.id.foveated_webvr_radio); + if (BuildConfig.FLAVOR_platform == "oculusvr") { + mFoveatedAppRadio.setVisibility(View.VISIBLE); + // Uncomment this when Foveated Rendering for WebVR makes more sense + //mFoveatedWebVRRadio.setVisibility(View.VISIBLE); + int level = SettingsStore.getInstance(getContext()).getFoveatedLevelApp(); + setFoveatedLevel(mFoveatedAppRadio, mFoveatedAppRadio.getIdForValue(level), false); + mFoveatedAppRadio.setOnCheckedChangeListener((compoundButton, checkedId, apply) -> setFoveatedLevel(mFoveatedAppRadio, checkedId, apply)); + + level = SettingsStore.getInstance(getContext()).getFoveatedLevelWebVR(); + setFoveatedLevel(mFoveatedWebVRRadio, mFoveatedWebVRRadio.getIdForValue(level), false); + mFoveatedWebVRRadio.setOnCheckedChangeListener((compoundButton, checkedId, apply) -> setFoveatedLevel(mFoveatedWebVRRadio, checkedId, apply)); + } + mDensityEdit = findViewById(R.id.density_edit); mDensityEdit.setHint1(String.valueOf(SettingsStore.DISPLAY_DENSITY_DEFAULT)); mDensityEdit.setDefaultFirstValue(String.valueOf(SettingsStore.DISPLAY_DENSITY_DEFAULT)); @@ -224,7 +244,6 @@ private void onRestartDialogDismissed() { show(); } - private RadioGroupSetting.OnCheckedChangeListener mUaModeListener = (radioGroup, checkedId, doApply) -> { setUaMode(checkedId, true); }; @@ -292,6 +311,14 @@ private void onRestartDialogDismissed() { if (!mMSAARadio.getValueForId(mMSAARadio.getCheckedRadioButtonId()).equals(SettingsStore.MSAA_DEFAULT_LEVEL)) { setMSAAMode(mMSAARadio.getIdForValue(SettingsStore.MSAA_DEFAULT_LEVEL), true); } + if (BuildConfig.FLAVOR_platform == "oculusvr") { + if (!mFoveatedAppRadio.getValueForId(mFoveatedAppRadio.getCheckedRadioButtonId()).equals(SettingsStore.FOVEATED_APP_DEFAULT_LEVEL)) { + setFoveatedLevel(mFoveatedAppRadio, mFoveatedAppRadio.getIdForValue(SettingsStore.FOVEATED_APP_DEFAULT_LEVEL), true); + } + if (!mFoveatedWebVRRadio.getValueForId(mFoveatedWebVRRadio.getCheckedRadioButtonId()).equals(SettingsStore.FOVEATED_WEBVR_DEFAULT_LEVEL)) { + setFoveatedLevel(mFoveatedWebVRRadio, mFoveatedWebVRRadio.getIdForValue(SettingsStore.FOVEATED_WEBVR_DEFAULT_LEVEL), true); + } + } restart = restart | setDisplayDensity(SettingsStore.DISPLAY_DENSITY_DEFAULT); restart = restart | setDisplayDpi(SettingsStore.DISPLAY_DPI_DEFAULT); @@ -332,6 +359,25 @@ private void setMSAAMode(int checkedId, boolean doApply) { } } + private void setFoveatedLevel(RadioGroupSetting aSetting, int checkedId, boolean doApply) { + RadioGroupSetting.OnCheckedChangeListener listener = aSetting.getOnCheckdChangeListener(); + aSetting.setOnCheckedChangeListener(null); + aSetting.setChecked(checkedId, doApply); + aSetting.setOnCheckedChangeListener(listener); + + int level = (Integer)aSetting.getValueForId(checkedId); + + if (aSetting == mFoveatedAppRadio) { + SettingsStore.getInstance(getContext()).setFoveatedLevelApp(level); + } else { + SettingsStore.getInstance(getContext()).setFoveatedLevelWebVR(level); + } + + if (doApply) { + mWidgetManager.updateFoveatedLevel(); + } + } + private boolean setDisplayDensity(float newDensity) { mDensityEdit.setOnClickListener(null); boolean restart = false; diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index 6f0a5c790..be913cb15 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -670,6 +670,12 @@ BrowserWorld::UpdateEnvironment() { CreateSkyBox(env.c_str(), ""); } +void +BrowserWorld::UpdateFoveatedLevel(const int aAppLevel, const int aWebVRLevel) { + ASSERT_ON_RENDER_THREAD(); + m.device->SetFoveatedLevel(aAppLevel, aWebVRLevel); +} + void BrowserWorld::UpdatePointerColor() { ASSERT_ON_RENDER_THREAD(); @@ -1297,6 +1303,11 @@ JNI_METHOD(void, updateEnvironmentNative) crow::BrowserWorld::Instance().UpdateEnvironment(); } +JNI_METHOD(void, updateFoveatedLevelNative) +(JNIEnv *aEnv, jobject, jint aAppLevel, jint aWebVRLevel) { + crow::BrowserWorld::Instance().UpdateFoveatedLevel(aAppLevel, aWebVRLevel); +} + JNI_METHOD(void, updatePointerColorNative) (JNIEnv* aEnv, jobject) { crow::BrowserWorld::Instance().UpdatePointerColor(); diff --git a/app/src/main/cpp/BrowserWorld.h b/app/src/main/cpp/BrowserWorld.h index 7eaadd421..b14f2236c 100644 --- a/app/src/main/cpp/BrowserWorld.h +++ b/app/src/main/cpp/BrowserWorld.h @@ -40,6 +40,7 @@ class BrowserWorld { void Draw(); void SetTemporaryFilePath(const std::string& aPath); void UpdateEnvironment(); + void UpdateFoveatedLevel(const int aAppLevel, const int aWebVRLevel); void UpdatePointerColor(); void SetSurfaceTexture(const std::string& aName, jobject& aSurface); void AddWidget(int32_t aHandle, const WidgetPlacementPtr& placement); diff --git a/app/src/main/cpp/DeviceDelegate.h b/app/src/main/cpp/DeviceDelegate.h index d8b98d0f8..c3025e462 100644 --- a/app/src/main/cpp/DeviceDelegate.h +++ b/app/src/main/cpp/DeviceDelegate.h @@ -56,6 +56,7 @@ class DeviceDelegate { virtual void SetClearColor(const vrb::Color& aColor) = 0; virtual void SetClipPlanes(const float aNear, const float aFar) = 0; virtual void SetControllerDelegate(ControllerDelegatePtr& aController) = 0; + virtual void SetFoveatedLevel(const int32_t aAppLevel, const int32_t aWebVRLevel) {}; virtual void ReleaseControllerDelegate() = 0; virtual int32_t GetControllerModelCount() const = 0; virtual const std::string GetControllerModelName(const int32_t aModelIndex) const = 0; diff --git a/app/src/main/res/layout/options_display.xml b/app/src/main/res/layout/options_display.xml index 2f818155d..57672e827 100644 --- a/app/src/main/res/layout/options_display.xml +++ b/app/src/main/res/layout/options_display.xml @@ -67,6 +67,24 @@ app:options="@array/developer_options_msaa" app:values="@array/developer_options_ua_mode_values" /> + + + + settings_audio settings_voice_search_language settings_cylinder_density + settings_foveated_app + settings_foveated_webvr https://support.mozilla.org/kb/private-mode-firefox-reality settings_browser_world_width settings_browser_world_height diff --git a/app/src/main/res/values/options_values.xml b/app/src/main/res/values/options_values.xml index b70fd1fc9..87e6d3d22 100644 --- a/app/src/main/res/values/options_values.xml +++ b/app/src/main/res/values/options_values.xml @@ -66,6 +66,21 @@ @string/developer_options_msaa_4 + + + @string/developer_options_foveated_disabled + @string/developer_options_foveated_1 + @string/developer_options_foveated_2 + @string/developer_options_foveated_3 + + + + 0 + 1 + 2 + 3 + + @string/language_en_US diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b46755ec6..d67fc6a43 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -254,6 +254,26 @@ VR + + Foveation Level (App) + + Foveation Level (WebVR) + + Disabled + + 1 + + 2 + + 3 + + Scroll Direction diff --git a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp index cb55edd60..5c396803c 100644 --- a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp +++ b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp @@ -609,6 +609,8 @@ struct DeviceDelegateOculusVR::State { ovrTracking2 predictedTracking = {}; uint32_t renderWidth = 0; uint32_t renderHeight = 0; + int32_t standaloneFoveatedLevel = 0; + int32_t immersiveFoveatedLevel = 0; vrb::Color clearColor; float near = 0.1f; float far = 100.f; @@ -688,6 +690,17 @@ struct DeviceDelegateOculusVR::State { } } + void UpdateFoveatedLevel() { + if (!ovr) { + return; + } + if (renderMode == device::RenderMode::StandAlone) { + vrapi_SetPropertyInt(&java, VRAPI_FOVEATION_LEVEL, standaloneFoveatedLevel); + } else { + vrapi_SetPropertyInt(&java, VRAPI_FOVEATION_LEVEL, immersiveFoveatedLevel); + } + } + void AddUILayer(const OculusLayerPtr& aLayer, VRLayerSurface::SurfaceType aSurfaceType) { if (ovr) { vrb::RenderContextPtr ctx = context.lock(); @@ -972,6 +985,7 @@ DeviceDelegateOculusVR::SetRenderMode(const device::RenderMode aMode) { } m.UpdateTrackingMode(); + m.UpdateFoveatedLevel(); // Reset reorient when exiting or entering immersive m.reorientMatrix = vrb::Matrix::Identity(); @@ -1048,6 +1062,13 @@ DeviceDelegateOculusVR::ReleaseControllerDelegate() { m.controller = nullptr; } +void +DeviceDelegateOculusVR::SetFoveatedLevel(const int32_t aAppLevel, const int32_t aWebVRLevel) { + m.standaloneFoveatedLevel = aAppLevel; + m.immersiveFoveatedLevel = aWebVRLevel; + m.UpdateFoveatedLevel(); +} + int32_t DeviceDelegateOculusVR::GetControllerModelCount() const { if (m.IsOculusQuest()) { @@ -1425,6 +1446,7 @@ DeviceDelegateOculusVR::EnterVR(const crow::BrowserEGLContext& aEGLContext) { vrapi_SetPerfThread(m.ovr, VRAPI_PERF_THREAD_TYPE_MAIN, gettid()); vrapi_SetPerfThread(m.ovr, VRAPI_PERF_THREAD_TYPE_RENDERER, gettid()); m.UpdateTrackingMode(); + m.UpdateFoveatedLevel(); } // Reset reorientation after Enter VR diff --git a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h index 41cd2ea5d..2f78c8981 100644 --- a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h +++ b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.h @@ -35,6 +35,7 @@ class DeviceDelegateOculusVR : public DeviceDelegate { void SetClipPlanes(const float aNear, const float aFar) override; void SetControllerDelegate(ControllerDelegatePtr& aController) override; void ReleaseControllerDelegate() override; + void SetFoveatedLevel(const int32_t aAppLevel, const int32_t aWebVRLevel) override; int32_t GetControllerModelCount() const override; const std::string GetControllerModelName(const int32_t aModelIndex) const override; void ProcessEvents() override;