Commit af87185e authored by Alex Cooper's avatar Alex Cooper Committed by Commit Bot

Ensure that XRSessions only request a runtime if needed

If the only features requested for an XRSession can be fully supported
by a sensorless session, then just create the sensorless session rather
than trying to get a fully device backed session.  There were cases
where the only requested features were "viewer", but the session was
still being backed by the orientation_sensors device.

This change introduces an enum that indicates whether sensor data, and
therefore a runtime, is needed, optional, or not required. If sensor
data is not required, then a sensorless session is created rather than
querying for device support.

This also refactors "CanRequestSensorlessInlineSession" to an enum, to
better consolidate and support this expanded logic.

Fixed: 1018794
Change-Id: I0b930a733ccab85275fcc965969a17313cb3cf9e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1884827
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: default avatarKlaus Weidner <klausw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710608}
parent 940af895
...@@ -26,12 +26,13 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestDeviceServiceDisconnect) { ...@@ -26,12 +26,13 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestDeviceServiceDisconnect) {
t->LoadUrlAndAwaitInitialization( t->LoadUrlAndAwaitInitialization(
t->GetFileUrlForHtmlTestFile("test_isolated_device_service_disconnect")); t->GetFileUrlForHtmlTestFile("test_isolated_device_service_disconnect"));
// We expect one change from the initial device being available.
t->PollJavaScriptBooleanOrFail("deviceChanges === 1",
WebXrVrBrowserTestBase::kPollTimeoutMedium);
t->EnterSessionWithUserGestureOrFail(); t->EnterSessionWithUserGestureOrFail();
// We don't care how many device changes we've received prior to this point.
// We should now be at a steady state, so what we really care about is the
// number of device changes after this point.
t->RunJavaScriptOrFail("resetDeviceChanges()");
device_hook->TerminateDeviceServiceProcessForTesting(); device_hook->TerminateDeviceServiceProcessForTesting();
// Ensure that we've actually exited the session. // Ensure that we've actually exited the session.
...@@ -41,7 +42,7 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestDeviceServiceDisconnect) { ...@@ -41,7 +42,7 @@ WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestDeviceServiceDisconnect) {
// We expect one change indicating the device was disconnected, and then // We expect one change indicating the device was disconnected, and then
// one more indicating that the device was re-connected. // one more indicating that the device was re-connected.
t->PollJavaScriptBooleanOrFail("deviceChanges === 3", t->PollJavaScriptBooleanOrFail("deviceChanges === 2",
WebXrVrBrowserTestBase::kPollTimeoutMedium); WebXrVrBrowserTestBase::kPollTimeoutMedium);
// One last check now that we have the device change that we can actually // One last check now that we have the device change that we can actually
......
...@@ -14,6 +14,10 @@ WebXR page without any code specific to one test ...@@ -14,6 +14,10 @@ WebXR page without any code specific to one test
<script> <script>
var deviceChanges = 0; var deviceChanges = 0;
navigator.xr.ondevicechange = () => { deviceChanges++; }; navigator.xr.ondevicechange = () => { deviceChanges++; };
function resetDeviceChanges() {
deviceChanges = 0;
}
</script> </script>
</body> </body>
</html> </html>
...@@ -313,7 +313,9 @@ XR::PendingRequestSessionQuery::PendingRequestSessionQuery( ...@@ -313,7 +313,9 @@ XR::PendingRequestSessionQuery::PendingRequestSessionQuery(
mode_(session_mode), mode_(session_mode),
required_features_(std::move(required_features)), required_features_(std::move(required_features)),
optional_features_(std::move(optional_features)), optional_features_(std::move(optional_features)),
ukm_source_id_(ukm_source_id) {} ukm_source_id_(ukm_source_id) {
ParseSensorRequirement();
}
void XR::PendingRequestSessionQuery::Resolve(XRSession* session) { void XR::PendingRequestSessionQuery::Resolve(XRSession* session) {
resolver_->Resolve(session); resolver_->Resolve(session);
...@@ -404,6 +406,33 @@ ScriptState* XR::PendingRequestSessionQuery::GetScriptState() const { ...@@ -404,6 +406,33 @@ ScriptState* XR::PendingRequestSessionQuery::GetScriptState() const {
return resolver_->GetScriptState(); return resolver_->GetScriptState();
} }
void XR::PendingRequestSessionQuery::ParseSensorRequirement() {
// All modes other than inline require sensors.
if (mode_ != XRSession::kModeInline) {
sensor_requirement_ = SensorRequirement::kRequired;
return;
}
// If any required features require sensors, then sensors are required.
for (const auto& feature : RequiredFeatures()) {
if (feature != device::mojom::XRSessionFeature::REF_SPACE_VIEWER) {
sensor_requirement_ = SensorRequirement::kRequired;
return;
}
}
// If any optional features require sensors, then sensors are optional.
for (const auto& feature : OptionalFeatures()) {
if (feature != device::mojom::XRSessionFeature::REF_SPACE_VIEWER) {
sensor_requirement_ = SensorRequirement::kOptional;
return;
}
}
// By this point any situation that requires sensors should have returned.
sensor_requirement_ = kNone;
}
void XR::PendingRequestSessionQuery::Trace(blink::Visitor* visitor) { void XR::PendingRequestSessionQuery::Trace(blink::Visitor* visitor) {
visitor->Trace(resolver_); visitor->Trace(resolver_);
} }
...@@ -672,21 +701,27 @@ void XR::RequestInlineSession(LocalFrame* frame, ...@@ -672,21 +701,27 @@ void XR::RequestInlineSession(LocalFrame* frame,
return; return;
} }
if (!service_) { // Reject session if any of the required features were invalid.
// If we don't have a service by the time we reach this call, there is no XR if (query->InvalidRequiredFeatures()) {
// hardware. Create a sensorless session if possible. query->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
if (CanCreateSensorlessInlineSession(query)) { kSessionNotSupported, exception_state);
XRSession* session = CreateSensorlessInlineSession();
query->Resolve(session);
} else {
query->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
kSessionNotSupported, exception_state);
}
return; return;
} }
// Reject session if any of the required features were invalid. auto sensor_requirement = query->GetSensorRequirement();
if (query->InvalidRequiredFeatures()) {
// If no sensors are requested, or if we don't have a service and sensors are
// not required, then just create a sensorless session.
if (sensor_requirement == SensorRequirement::kNone ||
(!service_ && sensor_requirement != SensorRequirement::kRequired)) {
query->Resolve(CreateSensorlessInlineSession());
return;
}
// If we don't have a service, then we don't have any WebXR hardware.
// If we didn't already create a sensorless session, we can't create a session
// without hardware, so just reject now.
if (!service_) {
query->RejectWithDOMException(DOMExceptionCode::kNotSupportedError, query->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
kSessionNotSupported, exception_state); kSessionNotSupported, exception_state);
return; return;
...@@ -883,7 +918,7 @@ void XR::OnRequestSessionReturned( ...@@ -883,7 +918,7 @@ void XR::OnRequestSessionReturned(
if (!session_ptr) { if (!session_ptr) {
// |service_| does not support the requested mode. Attempt to create a // |service_| does not support the requested mode. Attempt to create a
// sensorless session. // sensorless session.
if (CanCreateSensorlessInlineSession(query)) { if (query->GetSensorRequirement() != SensorRequirement::kRequired) {
XRSession* session = CreateSensorlessInlineSession(); XRSession* session = CreateSensorlessInlineSession();
query->Resolve(session); query->Resolve(session);
return; return;
...@@ -1051,23 +1086,6 @@ XRSession* XR::CreateSession( ...@@ -1051,23 +1086,6 @@ XRSession* XR::CreateSession(
return session; return session;
} }
bool XR::CanCreateSensorlessInlineSession(
const PendingRequestSessionQuery* query) const {
// Sensorless can only support an inline mode
if (query->mode() != XRSession::kModeInline)
return false;
// Sensorless can only be supported if the only required feature is the
// viewer reference space.
for (const auto& feature : query->RequiredFeatures()) {
if (feature != device::mojom::XRSessionFeature::REF_SPACE_VIEWER) {
return false;
}
}
return true;
}
XRSession* XR::CreateSensorlessInlineSession() { XRSession* XR::CreateSensorlessInlineSession() {
// TODO(https://crbug.com/944936): The blend mode could be "additive". // TODO(https://crbug.com/944936): The blend mode could be "additive".
XRSession::EnvironmentBlendMode blend_mode = XRSession::kBlendModeOpaque; XRSession::EnvironmentBlendMode blend_mode = XRSession::kBlendModeOpaque;
......
...@@ -94,6 +94,12 @@ class XR final : public EventTargetWithInlineData, ...@@ -94,6 +94,12 @@ class XR final : public EventTargetWithInlineData,
base::TimeTicks NavigationStart() const { return navigation_start_; } base::TimeTicks NavigationStart() const { return navigation_start_; }
private: private:
enum SensorRequirement {
kNone,
kOptional,
kRequired,
};
// These values are persisted to logs. Entries should not be renumbered and // These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. // numeric values should never be reused.
enum class SessionRequestStatus : int { enum class SessionRequestStatus : int {
...@@ -155,6 +161,10 @@ class XR final : public EventTargetWithInlineData, ...@@ -155,6 +161,10 @@ class XR final : public EventTargetWithInlineData,
bool InvalidRequiredFeatures() const; bool InvalidRequiredFeatures() const;
bool InvalidOptionalFeatures() const; bool InvalidOptionalFeatures() const;
SensorRequirement GetSensorRequirement() const {
return sensor_requirement_;
}
// Returns underlying resolver's script state. // Returns underlying resolver's script state.
ScriptState* GetScriptState() const; ScriptState* GetScriptState() const;
...@@ -162,11 +172,13 @@ class XR final : public EventTargetWithInlineData, ...@@ -162,11 +172,13 @@ class XR final : public EventTargetWithInlineData,
private: private:
void ReportRequestSessionResult(SessionRequestStatus status); void ReportRequestSessionResult(SessionRequestStatus status);
void ParseSensorRequirement();
Member<ScriptPromiseResolver> resolver_; Member<ScriptPromiseResolver> resolver_;
const XRSession::SessionMode mode_; const XRSession::SessionMode mode_;
RequestedXRSessionFeatureSet required_features_; RequestedXRSessionFeatureSet required_features_;
RequestedXRSessionFeatureSet optional_features_; RequestedXRSessionFeatureSet optional_features_;
SensorRequirement sensor_requirement_ = SensorRequirement::kNone;
const int64_t ukm_source_id_; const int64_t ukm_source_id_;
...@@ -273,8 +285,6 @@ class XR final : public EventTargetWithInlineData, ...@@ -273,8 +285,6 @@ class XR final : public EventTargetWithInlineData,
XRSessionFeatureSet enabled_features, XRSessionFeatureSet enabled_features,
bool sensorless_session = false); bool sensorless_session = false);
bool CanCreateSensorlessInlineSession(
const PendingRequestSessionQuery* query) const;
XRSession* CreateSensorlessInlineSession(); XRSession* CreateSensorlessInlineSession();
void Dispose(); void Dispose();
......
...@@ -26,7 +26,10 @@ let testFunction = function(session, fakeDeviceController, t) { ...@@ -26,7 +26,10 @@ let testFunction = function(session, fakeDeviceController, t) {
}); });
} }
// Inline that solely relies on the implicit viewer reference space ignores the
// data provider, so we need to make sure that we get a session that supports
// the local reference space.
xr_session_promise_test( xr_session_promise_test(
testName, testFunction, fakeDeviceInitParams, 'inline'); testName, testFunction, fakeDeviceInitParams, 'inline', { requiredFeatures: ["local"] });
</script> </script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment