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;