Commit 666960b1 authored by Bill Orr's avatar Bill Orr Committed by Commit Bot

WebXR: Do not provide poses to frames without focus

This change plumbs through focus (both blink/frame and device) to XRSessions, so we
only call raf callbacks when focused.

BUG=813230

Change-Id: I0aefdce989f6dd60b4cd0acf1a1477fdb2e845b8
Reviewed-on: https://chromium-review.googlesource.com/1011288Reviewed-by: default avatarBrandon Jones <bajones@chromium.org>
Reviewed-by: default avatarDavid Dorwin <ddorwin@chromium.org>
Commit-Queue: Bill Orr <billorr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551842}
parent 3ac4f597
...@@ -34,6 +34,7 @@ const char kNoDevicesMessage[] = "No devices found."; ...@@ -34,6 +34,7 @@ const char kNoDevicesMessage[] = "No devices found.";
XR::XR(LocalFrame& frame) XR::XR(LocalFrame& frame)
: ContextLifecycleObserver(frame.GetDocument()), : ContextLifecycleObserver(frame.GetDocument()),
FocusChangedObserver(frame.GetPage()),
devices_synced_(false), devices_synced_(false),
binding_(this) { binding_(this) {
frame.GetInterfaceProvider().GetInterface(mojo::MakeRequest(&service_)); frame.GetInterfaceProvider().GetInterface(mojo::MakeRequest(&service_));
...@@ -49,6 +50,16 @@ XR::XR(LocalFrame& frame) ...@@ -49,6 +50,16 @@ XR::XR(LocalFrame& frame)
WTF::Bind(&XR::OnDevicesSynced, WrapPersistent(this))); WTF::Bind(&XR::OnDevicesSynced, WrapPersistent(this)));
} }
void XR::FocusedFrameChanged() {
// Tell devices that focus changed.
for (const auto& device : devices_)
device->OnFrameFocusChanged();
}
bool XR::IsFrameFocused() {
return FocusChangedObserver::IsFrameFocused(GetFrame());
}
ExecutionContext* XR::GetExecutionContext() const { ExecutionContext* XR::GetExecutionContext() const {
return ContextLifecycleObserver::GetExecutionContext(); return ContextLifecycleObserver::GetExecutionContext();
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h" #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h" #include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/core/page/focus_changed_observer.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/forward.h"
...@@ -21,7 +22,8 @@ class XRDevice; ...@@ -21,7 +22,8 @@ class XRDevice;
class XR final : public EventTargetWithInlineData, class XR final : public EventTargetWithInlineData,
public ContextLifecycleObserver, public ContextLifecycleObserver,
public device::mojom::blink::VRServiceClient { public device::mojom::blink::VRServiceClient,
public FocusChangedObserver {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
USING_GARBAGE_COLLECTED_MIXIN(XR); USING_GARBAGE_COLLECTED_MIXIN(XR);
...@@ -44,16 +46,18 @@ class XR final : public EventTargetWithInlineData, ...@@ -44,16 +46,18 @@ class XR final : public EventTargetWithInlineData,
// ContextLifecycleObserver overrides. // ContextLifecycleObserver overrides.
void ContextDestroyed(ExecutionContext*) override; void ContextDestroyed(ExecutionContext*) override;
void Dispose();
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
// FocusChangedObserver overrides.
void FocusedFrameChanged() override;
bool IsFrameFocused();
private: private:
explicit XR(LocalFrame& frame); explicit XR(LocalFrame& frame);
void OnDevicesSynced(); void OnDevicesSynced();
void ResolveRequestDevice(); void ResolveRequestDevice();
void Dispose();
bool devices_synced_; bool devices_synced_;
......
...@@ -128,6 +128,7 @@ ScriptPromise XRDevice::requestSession( ...@@ -128,6 +128,7 @@ ScriptPromise XRDevice::requestSession(
} }
XRSession* session = new XRSession(this, options.exclusive(), output_context); XRSession* session = new XRSession(this, options.exclusive(), output_context);
sessions_.insert(session);
if (options.exclusive()) { if (options.exclusive()) {
frameProvider()->BeginExclusiveSession(session, resolver); frameProvider()->BeginExclusiveSession(session, resolver);
...@@ -138,20 +139,48 @@ ScriptPromise XRDevice::requestSession( ...@@ -138,20 +139,48 @@ ScriptPromise XRDevice::requestSession(
return promise; return promise;
} }
void XRDevice::OnFrameFocusChanged() {
OnFocusChanged();
}
void XRDevice::OnFocusChanged() {
// Tell all sessions that focus changed.
for (const auto& session : sessions_) {
session->OnFocusChanged();
}
if (frame_provider_)
frame_provider_->OnFocusChanged();
}
bool XRDevice::IsFrameFocused() {
return xr_->IsFrameFocused();
}
// TODO: Forward these calls on to the sessions once they've been implemented. // TODO: Forward these calls on to the sessions once they've been implemented.
void XRDevice::OnChanged(device::mojom::blink::VRDisplayInfoPtr display_info) { void XRDevice::OnChanged(device::mojom::blink::VRDisplayInfoPtr display_info) {
SetXRDisplayInfo(std::move(display_info)); SetXRDisplayInfo(std::move(display_info));
} }
void XRDevice::OnExitPresent() {} void XRDevice::OnExitPresent() {}
void XRDevice::OnBlur() {} void XRDevice::OnBlur() {
void XRDevice::OnFocus() {} // The device is reporting to us that it is blurred. This could happen for a
// variety of reasons, such as browser UI, a different application using the
// headset, or another page entering an exclusive session.
has_device_focus_ = false;
OnFocusChanged();
}
void XRDevice::OnFocus() {
has_device_focus_ = true;
OnFocusChanged();
}
void XRDevice::OnActivate(device::mojom::blink::VRDisplayEventReason, void XRDevice::OnActivate(device::mojom::blink::VRDisplayEventReason,
OnActivateCallback on_handled) {} OnActivateCallback on_handled) {}
void XRDevice::OnDeactivate(device::mojom::blink::VRDisplayEventReason) {} void XRDevice::OnDeactivate(device::mojom::blink::VRDisplayEventReason) {}
XRFrameProvider* XRDevice::frameProvider() { XRFrameProvider* XRDevice::frameProvider() {
if (!frame_provider_) if (!frame_provider_) {
frame_provider_ = new XRFrameProvider(this); frame_provider_ = new XRFrameProvider(this);
}
return frame_provider_; return frame_provider_;
} }
...@@ -172,6 +201,7 @@ void XRDevice::SetXRDisplayInfo( ...@@ -172,6 +201,7 @@ void XRDevice::SetXRDisplayInfo(
void XRDevice::Trace(blink::Visitor* visitor) { void XRDevice::Trace(blink::Visitor* visitor) {
visitor->Trace(xr_); visitor->Trace(xr_);
visitor->Trace(frame_provider_); visitor->Trace(frame_provider_);
visitor->Trace(sessions_);
EventTargetWithInlineData::Trace(visitor); EventTargetWithInlineData::Trace(visitor);
} }
......
...@@ -18,6 +18,7 @@ namespace blink { ...@@ -18,6 +18,7 @@ namespace blink {
class XR; class XR;
class XRFrameProvider; class XRFrameProvider;
class XRSession;
class XRDevice final : public EventTargetWithInlineData, class XRDevice final : public EventTargetWithInlineData,
public device::mojom::blink::VRDisplayClient { public device::mojom::blink::VRDisplayClient {
...@@ -40,6 +41,7 @@ class XRDevice final : public EventTargetWithInlineData, ...@@ -40,6 +41,7 @@ class XRDevice final : public EventTargetWithInlineData,
// EventTarget overrides. // EventTarget overrides.
ExecutionContext* GetExecutionContext() const override; ExecutionContext* GetExecutionContext() const override;
const AtomicString& InterfaceName() const override; const AtomicString& InterfaceName() const override;
void Trace(blink::Visitor*) override;
// XRDisplayClient // XRDisplayClient
void OnChanged(device::mojom::blink::VRDisplayInfoPtr) override; void OnChanged(device::mojom::blink::VRDisplayInfoPtr) override;
...@@ -65,17 +67,31 @@ class XRDevice final : public EventTargetWithInlineData, ...@@ -65,17 +67,31 @@ class XRDevice final : public EventTargetWithInlineData,
return display_info_; return display_info_;
} }
void Trace(blink::Visitor*) override; void OnFrameFocusChanged();
// The device may report focus to us - for example if another application is
// using the headset, or some browsing UI is shown, we may not have device
// focus.
bool HasDeviceFocus() { return has_device_focus_; }
bool HasDeviceAndFrameFocus() { return IsFrameFocused() && HasDeviceFocus(); }
private: private:
void SetXRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr); void SetXRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr);
const char* checkSessionSupport(const XRSessionCreationOptions&) const; const char* checkSessionSupport(const XRSessionCreationOptions&) const;
// There are two components to focus - whether the frame itself has
// traditional focus and whether the device reports that we have focus. These
// are aggregated so we can hand out focus/blur events on sessions and
// determine when to call animation frame callbacks.
void OnFocusChanged();
bool IsFrameFocused();
Member<XR> xr_; Member<XR> xr_;
Member<XRFrameProvider> frame_provider_; Member<XRFrameProvider> frame_provider_;
HeapHashSet<WeakMember<XRSession>> sessions_;
bool is_external_; bool is_external_;
bool supports_exclusive_; bool supports_exclusive_;
bool has_device_focus_ = true;
device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider_; device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider_;
device::mojom::blink::VRDisplayHostPtr display_; device::mojom::blink::VRDisplayHostPtr display_;
......
...@@ -84,7 +84,8 @@ std::unique_ptr<TransformationMatrix> getPoseMatrix( ...@@ -84,7 +84,8 @@ std::unique_ptr<TransformationMatrix> getPoseMatrix(
} // namespace } // namespace
XRFrameProvider::XRFrameProvider(XRDevice* device) : device_(device) {} XRFrameProvider::XRFrameProvider(XRDevice* device)
: device_(device), last_has_focus_(device->HasDeviceAndFrameFocus()) {}
void XRFrameProvider::BeginExclusiveSession(XRSession* session, void XRFrameProvider::BeginExclusiveSession(XRSession* session,
ScriptPromiseResolver* resolver) { ScriptPromiseResolver* resolver) {
...@@ -147,6 +148,20 @@ void XRFrameProvider::OnPresentComplete( ...@@ -147,6 +148,20 @@ void XRFrameProvider::OnPresentComplete(
pending_exclusive_session_resolver_ = nullptr; pending_exclusive_session_resolver_ = nullptr;
} }
void XRFrameProvider::OnFocusChanged() {
bool focus = device_->HasDeviceAndFrameFocus();
// If we are gaining focus, schedule a frame.
if (focus && !last_has_focus_) {
if (exclusive_session_) {
ScheduleExclusiveFrame();
} else if (requesting_sessions_.size() > 0) {
ScheduleNonExclusiveFrame();
}
}
last_has_focus_ = focus;
}
void XRFrameProvider::OnPresentationProviderConnectionError() { void XRFrameProvider::OnPresentationProviderConnectionError() {
if (pending_exclusive_session_resolver_) { if (pending_exclusive_session_resolver_) {
DOMException* exception = DOMException::Create( DOMException* exception = DOMException::Create(
...@@ -297,6 +312,11 @@ void XRFrameProvider::ProcessScheduledFrame(double timestamp) { ...@@ -297,6 +312,11 @@ void XRFrameProvider::ProcessScheduledFrame(double timestamp) {
TRACE_EVENT1("gpu", "XRFrameProvider::ProcessScheduledFrame", "frame", TRACE_EVENT1("gpu", "XRFrameProvider::ProcessScheduledFrame", "frame",
frame_id_); frame_id_);
if (!device_->HasDeviceAndFrameFocus() && !exclusive_session_) {
return; // Not currently focused, so we won't expose poses (except to
// exclusive sessions).
}
if (exclusive_session_can_send_frames_) { if (exclusive_session_can_send_frames_) {
if (frame_pose_ && frame_pose_->input_state.has_value()) { if (frame_pose_ && frame_pose_->input_state.has_value()) {
exclusive_session_->OnInputStateChange(frame_id_, exclusive_session_->OnInputStateChange(frame_id_,
......
...@@ -39,6 +39,7 @@ class XRFrameProvider final ...@@ -39,6 +39,7 @@ class XRFrameProvider final
void UpdateWebGLLayerViewports(XRWebGLLayer*); void UpdateWebGLLayerViewports(XRWebGLLayer*);
void Dispose(); void Dispose();
void OnFocusChanged();
virtual void Trace(blink::Visitor*); virtual void Trace(blink::Visitor*);
...@@ -88,6 +89,7 @@ class XRFrameProvider final ...@@ -88,6 +89,7 @@ class XRFrameProvider final
bool vsync_connection_failed_ = false; bool vsync_connection_failed_ = false;
base::Optional<gpu::MailboxHolder> buffer_mailbox_holder_; base::Optional<gpu::MailboxHolder> buffer_mailbox_holder_;
bool last_has_focus_ = false;
}; };
} // namespace blink } // namespace blink
......
...@@ -90,6 +90,8 @@ XRSession::XRSession(XRDevice* device, ...@@ -90,6 +90,8 @@ XRSession::XRSession(XRDevice* device,
exclusive_(exclusive), exclusive_(exclusive),
output_context_(output_context), output_context_(output_context),
callback_collection_(device->GetExecutionContext()) { callback_collection_(device->GetExecutionContext()) {
blurred_ = !HasAppropriateFocus();
// When an output context is provided, monitor it for resize events. // When an output context is provided, monitor it for resize events.
if (output_context_) { if (output_context_) {
HTMLCanvasElement* canvas = outputContext()->canvas(); HTMLCanvasElement* canvas = outputContext()->canvas();
...@@ -300,6 +302,22 @@ void XRSession::OnBlur() { ...@@ -300,6 +302,22 @@ void XRSession::OnBlur() {
DispatchEvent(XRSessionEvent::Create(EventTypeNames::blur, this)); DispatchEvent(XRSessionEvent::Create(EventTypeNames::blur, this));
} }
// Exclusive sessions may still not be blurred in headset even if the page isn't
// focused. This prevents the in-headset experience from freezing on an
// external display headset when the user clicks on another tab.
bool XRSession::HasAppropriateFocus() {
return exclusive_ ? device_->HasDeviceFocus()
: device_->HasDeviceAndFrameFocus();
}
void XRSession::OnFocusChanged() {
if (HasAppropriateFocus()) {
OnFocus();
} else {
OnBlur();
}
}
void XRSession::OnFrame( void XRSession::OnFrame(
std::unique_ptr<TransformationMatrix> base_pose_matrix, std::unique_ptr<TransformationMatrix> base_pose_matrix,
const base::Optional<gpu::MailboxHolder>& buffer_mailbox_holder) { const base::Optional<gpu::MailboxHolder>& buffer_mailbox_holder) {
......
...@@ -99,8 +99,7 @@ class XRSession final : public EventTargetWithInlineData { ...@@ -99,8 +99,7 @@ class XRSession final : public EventTargetWithInlineData {
ExecutionContext* GetExecutionContext() const override; ExecutionContext* GetExecutionContext() const override;
const AtomicString& InterfaceName() const override; const AtomicString& InterfaceName() const override;
void OnFocus(); void OnFocusChanged();
void OnBlur();
void OnFrame(std::unique_ptr<TransformationMatrix>, void OnFrame(std::unique_ptr<TransformationMatrix>,
const base::Optional<gpu::MailboxHolder>&); const base::Optional<gpu::MailboxHolder>&);
void OnInputStateChange( void OnInputStateChange(
...@@ -128,6 +127,10 @@ class XRSession final : public EventTargetWithInlineData { ...@@ -128,6 +127,10 @@ class XRSession final : public EventTargetWithInlineData {
XRInputSourceEvent* CreateInputSourceEvent(const AtomicString&, XRInputSourceEvent* CreateInputSourceEvent(const AtomicString&,
XRInputSource*); XRInputSource*);
void OnFocus();
void OnBlur();
bool HasAppropriateFocus();
const Member<XRDevice> device_; const Member<XRDevice> device_;
const bool exclusive_; const bool exclusive_;
const Member<XRPresentationContext> output_context_; const Member<XRPresentationContext> output_context_;
...@@ -142,7 +145,7 @@ class XRSession final : public EventTargetWithInlineData { ...@@ -142,7 +145,7 @@ class XRSession final : public EventTargetWithInlineData {
double depth_near_ = 0.1; double depth_near_ = 0.1;
double depth_far_ = 1000.0; double depth_far_ = 1000.0;
bool blurred_ = false; bool blurred_;
bool ended_ = false; bool ended_ = false;
bool pending_frame_ = false; bool pending_frame_ = false;
bool resolving_frame_ = false; bool resolving_frame_ = false;
......
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