diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/modules/xr | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/xr')
69 files changed, 4546 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/modules/xr/BUILD.gn b/chromium/third_party/blink/renderer/modules/xr/BUILD.gn new file mode 100644 index 00000000000..0bd7b4656c6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/BUILD.gn @@ -0,0 +1,56 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/blink/renderer/modules/modules.gni") + +blink_modules_sources("xr") { + sources = [ + "xr.cc", + "xr.h", + "xr_canvas_input_provider.cc", + "xr_canvas_input_provider.h", + "xr_coordinate_system.cc", + "xr_coordinate_system.h", + "xr_device.cc", + "xr_device.h", + "xr_device_pose.cc", + "xr_device_pose.h", + "xr_frame_of_reference.cc", + "xr_frame_of_reference.h", + "xr_frame_provider.cc", + "xr_frame_provider.h", + "xr_frame_request_callback_collection.cc", + "xr_frame_request_callback_collection.h", + "xr_input_pose.cc", + "xr_input_pose.h", + "xr_input_source.cc", + "xr_input_source.h", + "xr_input_source_event.cc", + "xr_input_source_event.h", + "xr_layer.cc", + "xr_layer.h", + "xr_presentation_context.cc", + "xr_presentation_context.h", + "xr_presentation_frame.cc", + "xr_presentation_frame.h", + "xr_session.cc", + "xr_session.h", + "xr_session_event.cc", + "xr_session_event.h", + "xr_stage_bounds.cc", + "xr_stage_bounds.h", + "xr_stage_bounds_point.h", + "xr_utils.cc", + "xr_utils.h", + "xr_view.cc", + "xr_view.h", + "xr_viewport.h", + "xr_webgl_layer.cc", + "xr_webgl_layer.h", + ] + + deps = [ + "//device/vr/public/mojom:mojom_blink", + ] +} diff --git a/chromium/third_party/blink/renderer/modules/xr/DEPS b/chromium/third_party/blink/renderer/modules/xr/DEPS new file mode 100644 index 00000000000..c86cbe78e9a --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "+mojo/public/cpp/bindings/binding.h", + "+mojo/public/cpp/system/platform_handle.h", + "+device/vr/public/mojom/vr_service.mojom-blink.h", + "+gpu/command_buffer/client/gles2_interface.h", + "+gpu/command_buffer/common/mailbox_holder.h", + "+ui/gfx/geometry", +] diff --git a/chromium/third_party/blink/renderer/modules/xr/OWNERS b/chromium/third_party/blink/renderer/modules/xr/OWNERS new file mode 100644 index 00000000000..26c48a27959 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/OWNERS @@ -0,0 +1,4 @@ +bajones@chromium.org +klausw@chromium.org + +# COMPONENT: Blink>WebVR
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr.cc b/chromium/third_party/blink/renderer/modules/xr/xr.cc new file mode 100644 index 00000000000..245b2701529 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr.cc @@ -0,0 +1,193 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr.h" + +#include "services/service_manager/public/cpp/interface_provider.h" +#include "third_party/blink/public/platform/interface_provider.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/modules/event_modules.h" +#include "third_party/blink/renderer/modules/event_target_modules.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h" +namespace blink { + +namespace { + +const char kNavigatorDetachedError[] = + "The navigator.xr object is no longer associated with a document."; + +const char kFeaturePolicyBlocked[] = + "Access to the feature \"xr\" is disallowed by feature policy."; + +const char kCrossOriginSubframeBlocked[] = + "Blocked call to navigator.xr.requestDevice inside a cross-origin " + "iframe because the frame has never been activated by the user."; + +const char kNoDevicesMessage[] = "No devices found."; + +} // namespace + +XR::XR(LocalFrame& frame) + : ContextLifecycleObserver(frame.GetDocument()), + FocusChangedObserver(frame.GetPage()), + devices_synced_(false), + binding_(this) { + frame.GetInterfaceProvider().GetInterface(mojo::MakeRequest(&service_)); + service_.set_connection_error_handler( + WTF::Bind(&XR::Dispose, WrapWeakPersistent(this))); + + device::mojom::blink::VRServiceClientPtr client; + binding_.Bind(mojo::MakeRequest(&client)); + + // Setting the client kicks off a request for the details of any connected + // XRDevices. + service_->SetClient(std::move(client), + 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 { + return ContextLifecycleObserver::GetExecutionContext(); +} + +const AtomicString& XR::InterfaceName() const { + return EventTargetNames::XR; +} + +ScriptPromise XR::requestDevice(ScriptState* script_state) { + LocalFrame* frame = GetFrame(); + if (!frame) { + // Reject if the frame is inaccessible. + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kInvalidStateError, kNavigatorDetachedError)); + } + + if (IsSupportedInFeaturePolicy(mojom::FeaturePolicyFeature::kWebVr)) { + if (!frame->IsFeatureEnabled(mojom::FeaturePolicyFeature::kWebVr)) { + // Only allow the call to be made if the appropraite feature policy is in + // place. + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kSecurityError, kFeaturePolicyBlocked)); + } + } else if (!frame->HasBeenActivated() && frame->IsCrossOriginSubframe()) { + // Block calls from cross-origin iframes that have never had a user gesture. + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kSecurityError, kCrossOriginSubframeBlocked)); + } + + // If we're still waiting for a previous call to resolve return that promise + // again. + if (pending_devices_resolver_) { + return pending_devices_resolver_->Promise(); + } + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); + ScriptPromise promise = resolver->Promise(); + + // If we've previously synced the XRDevices or no longer have a valid service + // connection just use the current list. In the case of the service being + // disconnected this will be an empty array. + if (!service_ || devices_synced_) { + // TODO (offenwanger): When we have a prioritized order of devices, or some + // other method of getting the prefered device, insert that here. For now, + // just get the first device out of the list, if there is one. + if (devices_.size() == 0) { + resolver->Reject(DOMException::Create(kNotFoundError, kNoDevicesMessage)); + } else { + resolver->Resolve(devices_[0]); + } + + return promise; + } + + // Otherwise wait for the full list of devices to be populated and resolve + // when onDevicesSynced is called. + pending_devices_resolver_ = resolver; + + return promise; +} + +// Each time a new XRDevice is connected we'll recieve a XRDisplayPtr for it +// here. Upon calling SetClient in the constructor we should receive one call +// for each XRDevice that was already connected at the time. +void XR::OnDisplayConnected( + device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider, + device::mojom::blink::VRDisplayHostPtr display, + device::mojom::blink::VRDisplayClientRequest client_request, + device::mojom::blink::VRDisplayInfoPtr display_info) { + XRDevice* xr_device = + new XRDevice(this, std::move(magic_window_provider), std::move(display), + std::move(client_request), std::move(display_info)); + + devices_.push_back(xr_device); + + DispatchEvent(blink::Event::Create(EventTypeNames::devicechange)); +} + +// Called when the XRService has called OnDevicesConnected for all active +// XRDevices. +void XR::OnDevicesSynced() { + devices_synced_ = true; + ResolveRequestDevice(); +} + +// Called when details for every connected XRDevice has been received. +void XR::ResolveRequestDevice() { + if (pending_devices_resolver_) { + if (devices_.size() == 0) { + pending_devices_resolver_->Reject( + DOMException::Create(kNotFoundError, kNoDevicesMessage)); + } else { + pending_devices_resolver_->Resolve(devices_[0]); + } + + pending_devices_resolver_ = nullptr; + } +} + +void XR::ContextDestroyed(ExecutionContext*) { + Dispose(); +} + +void XR::Dispose() { + // If the document context was destroyed, shut down the client connection + // and never call the mojo service again. + service_.reset(); + binding_.Close(); + + // Shutdown all devices' message pipe + for (const auto& device : devices_) + device->Dispose(); + + devices_.clear(); + + // Ensure that any outstanding requestDevice promises are resolved. They will + // receive a null result. + ResolveRequestDevice(); +} + +void XR::Trace(blink::Visitor* visitor) { + visitor->Trace(devices_); + visitor->Trace(pending_devices_resolver_); + ContextLifecycleObserver::Trace(visitor); + EventTargetWithInlineData::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr.h b/chromium/third_party/blink/renderer/modules/xr/xr.h new file mode 100644 index 00000000000..777e54f98b2 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr.h @@ -0,0 +1,72 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_H_ + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/public/platform/web_callbacks.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/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/wtf/forward.h" + +namespace blink { + +class ScriptPromiseResolver; +class XRDevice; + +class XR final : public EventTargetWithInlineData, + public ContextLifecycleObserver, + public device::mojom::blink::VRServiceClient, + public FocusChangedObserver { + DEFINE_WRAPPERTYPEINFO(); + USING_GARBAGE_COLLECTED_MIXIN(XR); + + public: + static XR* Create(LocalFrame& frame) { return new XR(frame); } + + DEFINE_ATTRIBUTE_EVENT_LISTENER(devicechange); + + ScriptPromise requestDevice(ScriptState*); + + // XRServiceClient overrides. + void OnDisplayConnected(device::mojom::blink::VRMagicWindowProviderPtr, + device::mojom::blink::VRDisplayHostPtr, + device::mojom::blink::VRDisplayClientRequest, + device::mojom::blink::VRDisplayInfoPtr) override; + + // EventTarget overrides. + ExecutionContext* GetExecutionContext() const override; + const AtomicString& InterfaceName() const override; + + // ContextLifecycleObserver overrides. + void ContextDestroyed(ExecutionContext*) override; + void Trace(blink::Visitor*) override; + + // FocusChangedObserver overrides. + void FocusedFrameChanged() override; + bool IsFrameFocused(); + + private: + explicit XR(LocalFrame& frame); + + void OnDevicesSynced(); + void ResolveRequestDevice(); + void Dispose(); + + bool devices_synced_; + + HeapVector<Member<XRDevice>> devices_; + Member<ScriptPromiseResolver> pending_devices_resolver_; + device::mojom::blink::VRServicePtr service_; + mojo::Binding<device::mojom::blink::VRServiceClient> binding_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr.idl b/chromium/third_party/blink/renderer/modules/xr/xr.idl new file mode 100644 index 00000000000..f28b866153d --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr.idl @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xr-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XR : EventTarget { + attribute EventHandler ondevicechange; + [CallWith=ScriptState, MeasureAs=XRRequestDevice] Promise requestDevice(); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc b/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc new file mode 100644 index 00000000000..cf77afbb829 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.cc @@ -0,0 +1,125 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h" + +#include "third_party/blink/renderer/core/events/mouse_event.h" +#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" + +namespace blink { + +namespace { + +class XRCanvasInputEventListener : public EventListener { + public: + XRCanvasInputEventListener(XRCanvasInputProvider* input_provider) + : EventListener(kCPPEventListenerType), input_provider_(input_provider) {} + + bool operator==(const EventListener& that) const override { + return this == &that; + } + + void handleEvent(ExecutionContext* execution_context, Event* event) override { + if (!input_provider_->ShouldProcessEvents()) + return; + + if (event->type() == EventTypeNames::click) { + input_provider_->OnClick(ToMouseEvent(event)); + } + } + + void Trace(blink::Visitor* visitor) override { + visitor->Trace(input_provider_); + EventListener::Trace(visitor); + } + + private: + Member<XRCanvasInputProvider> input_provider_; +}; + +} // namespace + +XRCanvasInputProvider::XRCanvasInputProvider(XRSession* session, + HTMLCanvasElement* canvas) + : session_(session), canvas_(canvas) { + listener_ = new XRCanvasInputEventListener(this); + canvas->addEventListener(EventTypeNames::click, listener_); +} + +XRCanvasInputProvider::~XRCanvasInputProvider() { + Stop(); +} + +void XRCanvasInputProvider::Stop() { + if (!listener_) { + return; + } + canvas_->removeEventListener(EventTypeNames::click, listener_); + canvas_ = nullptr; + listener_ = nullptr; +} + +bool XRCanvasInputProvider::ShouldProcessEvents() { + // Don't process canvas gestures if there's an active exclusive session. + return !(session_->device()->frameProvider()->exclusive_session()); +} + +void XRCanvasInputProvider::OnClick(MouseEvent* event) { + UpdateInputSource(event); + session_->OnSelect(input_source_); + ClearInputSource(); +} + +XRInputSource* XRCanvasInputProvider::GetInputSource() { + return input_source_; +} + +void XRCanvasInputProvider::UpdateInputSource(MouseEvent* event) { + if (!canvas_) + return; + + if (!input_source_) { + input_source_ = new XRInputSource(session_, 0); + input_source_->SetPointerOrigin(XRInputSource::kOriginScreen); + } + + // Get the event location relative to the canvas element. + double element_x = event->pageX() - canvas_->OffsetLeft(); + double element_y = event->pageY() - canvas_->OffsetTop(); + + // Unproject the event location into a pointer matrix. This takes the 2D + // position of the screen interaction and shoves it backwards through the + // projection matrix to get a 3D point in space, which is then returned in + // matrix form so we can use it as an XRInputSource's pointerMatrix. + XRView* view = session_->views()[0]; + std::unique_ptr<TransformationMatrix> pointer_transform_matrix = + view->UnprojectPointer(element_x, element_y, canvas_->OffsetWidth(), + canvas_->OffsetHeight()); + + // Update the input source's pointer matrix. + input_source_->SetPointerTransformMatrix(std::move(pointer_transform_matrix)); +} + +void XRCanvasInputProvider::ClearInputSource() { + input_source_ = nullptr; +} + +void XRCanvasInputProvider::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + visitor->Trace(canvas_); + visitor->Trace(listener_); + visitor->Trace(input_source_); +} + +void XRCanvasInputProvider::TraceWrappers( + const blink::ScriptWrappableVisitor* visitor) const { + visitor->TraceWrappers(input_source_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h b/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h new file mode 100644 index 00000000000..1ad49c8b505 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h @@ -0,0 +1,59 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CANVAS_INPUT_PROVIDER_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CANVAS_INPUT_PROVIDER_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class EventListener; +class HTMLCanvasElement; +class MouseEvent; +class XRInputSource; +class XRSession; + +class XRCanvasInputProvider + : public GarbageCollectedFinalized<XRCanvasInputProvider>, + public TraceWrapperBase { + public: + XRCanvasInputProvider(XRSession*, HTMLCanvasElement*); + virtual ~XRCanvasInputProvider(); + + XRSession* session() const { return session_; } + + // Remove all event listeners. + void Stop(); + + bool ShouldProcessEvents(); + + void OnClick(MouseEvent*); + + XRInputSource* GetInputSource(); + + virtual void Trace(blink::Visitor*); + virtual void TraceWrappers(const blink::ScriptWrappableVisitor*) const; + const char* NameInHeapSnapshot() const override { + return "XRCanvasInputProvider"; + } + + private: + void UpdateInputSource(MouseEvent*); + void ClearInputSource(); + + const Member<XRSession> session_; + Member<HTMLCanvasElement> canvas_; + Member<EventListener> listener_; + TraceWrapperMember<XRInputSource> input_source_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_CANVAS_INPUT_PROVIDER_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.cc b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.cc new file mode 100644 index 00000000000..0d4bd264f26 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.cc @@ -0,0 +1,37 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_coordinate_system.h" + +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +namespace blink { + +XRCoordinateSystem::XRCoordinateSystem(XRSession* session) + : session_(session) {} + +XRCoordinateSystem::~XRCoordinateSystem() = default; + +// If possible, get the matrix required to transform between two coordinate +// systems. +DOMFloat32Array* XRCoordinateSystem::getTransformTo( + XRCoordinateSystem* other) const { + if (session_ != other->session()) { + // Cannot get relationships between coordinate systems that belong to + // different sessions. + return nullptr; + } + + // TODO(bajones): Track relationship to other coordinate systems and return + // the transforms here. In the meantime we're allowed to return null to + // indicate that the transform between the two coordinate systems is unknown. + return nullptr; +} + +void XRCoordinateSystem::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.h b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.h new file mode 100644 index 00000000000..cd6497cdad1 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_COORDINATE_SYSTEM_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_COORDINATE_SYSTEM_H_ + +#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class TransformationMatrix; +class XRSession; + +class XRCoordinateSystem : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + explicit XRCoordinateSystem(XRSession*); + virtual ~XRCoordinateSystem(); + + DOMFloat32Array* getTransformTo(XRCoordinateSystem*) const; + + XRSession* session() { return session_; } + + virtual std::unique_ptr<TransformationMatrix> TransformBasePose( + const TransformationMatrix& base_pose) = 0; + virtual std::unique_ptr<TransformationMatrix> TransformBaseInputPose( + const TransformationMatrix& base_input_pose, + const TransformationMatrix& base_pose) = 0; + + virtual void Trace(blink::Visitor*); + + private: + const Member<XRSession> session_; +}; + +} // namespace blink + +#endif // XRWebGLLayer_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.idl b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.idl new file mode 100644 index 00000000000..06fd42fe3c5 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_coordinate_system.idl @@ -0,0 +1,11 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrcoordinatesystem-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRCoordinateSystem { + Float32Array? getTransformTo(XRCoordinateSystem other); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device.cc b/chromium/third_party/blink/renderer/modules/xr/xr_device.cc new file mode 100644 index 00000000000..b360cf5b8b5 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device.cc @@ -0,0 +1,209 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_device.h" + +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/frame/frame.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/modules/event_target_modules.h" +#include "third_party/blink/renderer/modules/xr/xr.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +namespace blink { + +namespace { + +const char kActiveExclusiveSession[] = + "XRDevice already has an active, exclusive session"; + +const char kExclusiveNotSupported[] = + "XRDevice does not support the creation of exclusive sessions."; + +const char kNoOutputContext[] = + "Non-exclusive sessions must be created with an outputContext."; + +const char kRequestNotInUserGesture[] = + "Exclusive sessions can only be requested during a user gesture."; + +} // namespace + +XRDevice::XRDevice( + XR* xr, + device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider, + device::mojom::blink::VRDisplayHostPtr display, + device::mojom::blink::VRDisplayClientRequest client_request, + device::mojom::blink::VRDisplayInfoPtr display_info) + : xr_(xr), + magic_window_provider_(std::move(magic_window_provider)), + display_(std::move(display)), + display_client_binding_(this, std::move(client_request)) { + SetXRDisplayInfo(std::move(display_info)); +} + +ExecutionContext* XRDevice::GetExecutionContext() const { + return xr_->GetExecutionContext(); +} + +const AtomicString& XRDevice::InterfaceName() const { + return EventTargetNames::XRDevice; +} + +const char* XRDevice::checkSessionSupport( + const XRSessionCreationOptions& options) const { + if (options.exclusive()) { + // Validation for exclusive sessions. + if (!supports_exclusive_) { + return kExclusiveNotSupported; + } + } else { + // Validation for non-exclusive sessions. + if (!options.hasOutputContext()) { + return kNoOutputContext; + } + } + + return nullptr; +} + +ScriptPromise XRDevice::supportsSession( + ScriptState* script_state, + const XRSessionCreationOptions& options) const { + // Check to see if the device is capable of supporting the requested session + // options. Note that reporting support here does not guarantee that creating + // a session with those options will succeed, as other external and + // time-sensitve factors (focus state, existance of another exclusive session, + // etc.) may prevent the creation of a session as well. + const char* reject_reason = checkSessionSupport(options); + if (reject_reason) { + return ScriptPromise::RejectWithDOMException( + script_state, DOMException::Create(kNotSupportedError, reject_reason)); + } + + // If the above checks pass, resolve without a value. Future API iterations + // may specify a value to be returned here. + ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); + ScriptPromise promise = resolver->Promise(); + resolver->Resolve(); + return promise; +} + +ScriptPromise XRDevice::requestSession( + ScriptState* script_state, + const XRSessionCreationOptions& options) { + // Check first to see if the device is capable of supporting the requested + // options. + const char* reject_reason = checkSessionSupport(options); + if (reject_reason) { + return ScriptPromise::RejectWithDOMException( + script_state, DOMException::Create(kNotSupportedError, reject_reason)); + } + + // Check if the current page state prevents the requested session from being + // created. + if (options.exclusive()) { + if (frameProvider()->exclusive_session()) { + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kInvalidStateError, kActiveExclusiveSession)); + } + + Document* doc = ToDocumentOrNull(ExecutionContext::From(script_state)); + if (!Frame::HasTransientUserActivation(doc ? doc->GetFrame() : nullptr)) { + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kInvalidStateError, kRequestNotInUserGesture)); + } + } + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); + ScriptPromise promise = resolver->Promise(); + + XRPresentationContext* output_context = nullptr; + if (options.hasOutputContext()) { + output_context = options.outputContext(); + } + + XRSession* session = new XRSession(this, options.exclusive(), output_context); + sessions_.insert(session); + + if (options.exclusive()) { + frameProvider()->BeginExclusiveSession(session, resolver); + } else { + resolver->Resolve(session); + } + + 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. +void XRDevice::OnChanged(device::mojom::blink::VRDisplayInfoPtr display_info) { + SetXRDisplayInfo(std::move(display_info)); +} +void XRDevice::OnExitPresent() {} +void XRDevice::OnBlur() { + // 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, + OnActivateCallback on_handled) {} +void XRDevice::OnDeactivate(device::mojom::blink::VRDisplayEventReason) {} + +XRFrameProvider* XRDevice::frameProvider() { + if (!frame_provider_) { + frame_provider_ = new XRFrameProvider(this); + } + + return frame_provider_; +} + +void XRDevice::Dispose() { + display_client_binding_.Close(); + if (frame_provider_) + frame_provider_->Dispose(); +} + +void XRDevice::SetXRDisplayInfo( + device::mojom::blink::VRDisplayInfoPtr display_info) { + display_info_id_++; + display_info_ = std::move(display_info); + is_external_ = display_info_->capabilities->hasExternalDisplay; + supports_exclusive_ = (display_info_->capabilities->canPresent); +} + +void XRDevice::Trace(blink::Visitor* visitor) { + visitor->Trace(xr_); + visitor->Trace(frame_provider_); + visitor->Trace(sessions_); + EventTargetWithInlineData::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device.h b/chromium/third_party/blink/renderer/modules/xr/xr_device.h new file mode 100644 index 00000000000..d2266136b3f --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device.h @@ -0,0 +1,109 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEVICE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEVICE_H_ + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/core/dom/events/event_target.h" +#include "third_party/blink/renderer/modules/xr/xr_session_creation_options.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/text/wtf_string.h" + +namespace blink { + +class XR; +class XRFrameProvider; +class XRSession; + +class XRDevice final : public EventTargetWithInlineData, + public device::mojom::blink::VRDisplayClient { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRDevice(XR*, + device::mojom::blink::VRMagicWindowProviderPtr, + device::mojom::blink::VRDisplayHostPtr, + device::mojom::blink::VRDisplayClientRequest, + device::mojom::blink::VRDisplayInfoPtr); + XR* xr() const { return xr_; } + + bool external() const { return is_external_; } + + ScriptPromise supportsSession(ScriptState*, + const XRSessionCreationOptions&) const; + ScriptPromise requestSession(ScriptState*, const XRSessionCreationOptions&); + + // EventTarget overrides. + ExecutionContext* GetExecutionContext() const override; + const AtomicString& InterfaceName() const override; + void Trace(blink::Visitor*) override; + + // XRDisplayClient + void OnChanged(device::mojom::blink::VRDisplayInfoPtr) override; + void OnExitPresent() override; + void OnBlur() override; + void OnFocus() override; + void OnActivate(device::mojom::blink::VRDisplayEventReason, + OnActivateCallback on_handled) override; + void OnDeactivate(device::mojom::blink::VRDisplayEventReason) override; + + XRFrameProvider* frameProvider(); + + void Dispose(); + + const device::mojom::blink::VRDisplayHostPtr& xrDisplayHostPtr() const { + return display_; + } + const device::mojom::blink::VRMagicWindowProviderPtr& + xrMagicWindowProviderPtr() const { + return magic_window_provider_; + } + const device::mojom::blink::VRDisplayInfoPtr& xrDisplayInfoPtr() const { + return display_info_; + } + // Incremented every time display_info_ is changed, so that other objects that + // depend on it can know when they need to update. + unsigned int xrDisplayInfoPtrId() const { return display_info_id_; } + + 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: + void SetXRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr); + + 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<XRFrameProvider> frame_provider_; + HeapHashSet<WeakMember<XRSession>> sessions_; + bool is_external_; + bool supports_exclusive_; + bool has_device_focus_ = true; + + device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider_; + device::mojom::blink::VRDisplayHostPtr display_; + device::mojom::blink::VRDisplayInfoPtr display_info_; + unsigned int display_info_id_ = 0; + + mojo::Binding<device::mojom::blink::VRDisplayClient> display_client_binding_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEVICE_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device.idl b/chromium/third_party/blink/renderer/modules/xr/xr_device.idl new file mode 100644 index 00000000000..6466153b297 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device.idl @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrdevice-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRDevice : EventTarget { + [CallWith=ScriptState, MeasureAs=XRSupportsSession] Promise supportsSession([PermissiveDictionaryConversion] optional XRSessionCreationOptions options); + [CallWith=ScriptState, MeasureAs=XRRequestSession] Promise requestSession([PermissiveDictionaryConversion] optional XRSessionCreationOptions options); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.cc b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.cc new file mode 100644 index 00000000000..10e154755e6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.cc @@ -0,0 +1,47 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_device_pose.h" + +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_utils.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" + +namespace blink { + +XRDevicePose::XRDevicePose( + XRSession* session, + std::unique_ptr<TransformationMatrix> pose_model_matrix) + : session_(session), pose_model_matrix_(std::move(pose_model_matrix)) {} + +DOMFloat32Array* XRDevicePose::poseModelMatrix() const { + if (!pose_model_matrix_) + return nullptr; + return transformationMatrixToFloat32Array(*pose_model_matrix_); +} + +DOMFloat32Array* XRDevicePose::getViewMatrix(XRView* view) { + if (view->session() != session_) + return nullptr; + + if (!pose_model_matrix_->IsInvertible()) + return nullptr; + + TransformationMatrix view_matrix(pose_model_matrix_->Inverse()); + + // Transform by the negative offset, since we're operating on the inverted + // matrix + const FloatPoint3D& view_offset = view->offset(); + view_matrix.PostTranslate3d(-view_offset.X(), -view_offset.Y(), + -view_offset.Z()); + + return transformationMatrixToFloat32Array(view_matrix); +} + +void XRDevicePose::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.h b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.h new file mode 100644 index 00000000000..074e6e71acf --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.h @@ -0,0 +1,36 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEVICE_POSE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_DEVICE_POSE_H_ + +#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" + +namespace blink { + +class XRSession; +class XRView; + +class XRDevicePose final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRDevicePose(XRSession*, std::unique_ptr<TransformationMatrix>); + + DOMFloat32Array* poseModelMatrix() const; + DOMFloat32Array* getViewMatrix(XRView*); + + virtual void Trace(blink::Visitor*); + + private: + const Member<XRSession> session_; + std::unique_ptr<TransformationMatrix> pose_model_matrix_; +}; + +} // namespace blink + +#endif // XRWebGLLayer_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.idl b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.idl new file mode 100644 index 00000000000..abdb9c8f86a --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_device_pose.idl @@ -0,0 +1,13 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrdevicepose-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRDevicePose { + readonly attribute Float32Array poseModelMatrix; + + Float32Array getViewMatrix(XRView view); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.cc b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.cc new file mode 100644 index 00000000000..3bc1dc4813c --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.cc @@ -0,0 +1,158 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_frame_of_reference.h" + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_stage_bounds.h" + +namespace blink { + +// Rough estimate of avg human eye height in meters. +const double kDefaultEmulationHeight = 1.6; + +XRFrameOfReference::XRFrameOfReference(XRSession* session, Type type) + : XRCoordinateSystem(session), type_(type) {} + +XRFrameOfReference::~XRFrameOfReference() = default; + +void XRFrameOfReference::UpdatePoseTransform( + std::unique_ptr<TransformationMatrix> transform) { + pose_transform_ = std::move(transform); +} + +void XRFrameOfReference::UpdateStageBounds(XRStageBounds* bounds) { + bounds_ = bounds; + // TODO(bajones): Fire a boundschange event +} + +void XRFrameOfReference::UpdateStageTransform() { + const device::mojom::blink::VRDisplayInfoPtr& display_info = + session()->device()->xrDisplayInfoPtr(); + + if (display_info->stageParameters) { + // Use the transform given by xrDisplayInfo's stageParamters if available. + const WTF::Vector<float>& m = + display_info->stageParameters->standingTransform; + pose_transform_ = TransformationMatrix::Create( + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], + m[11], m[12], m[13], m[14], m[15]); + } else if (emulated_height_ != 0.0) { + // Otherwise, if this frame of reference has specified that an emulated + // height may be used, create a transform based on that. + pose_transform_ = TransformationMatrix::Create(); + pose_transform_->Translate3d(0, emulated_height_, 0); + } else { + // If stage parameters aren't available and emulation is disabled, set the + // transform to null, which will subsequently cause this frame of reference + // to return null poses. + pose_transform_.reset(); + } + + display_info_id_ = session()->device()->xrDisplayInfoPtrId(); +} + +// Enables emulated height when using a stage frame of reference, which should +// only be used if the sytem does not have a native concept of how far above the +// floor the XRDevice is at any given moment. This applies a static vertical +// offset to the coordinate system so that the user feels approximately like +// they are standing on a floor plane located at Y = 0. An explicit offset in +// meters can be given if the page has specific needs. +void XRFrameOfReference::UseEmulatedHeight(double value) { + if (value == 0.0) + value = kDefaultEmulationHeight; + + emulated_height_ = value; + UpdateStageTransform(); +} + +// Transforms a given pose from a "base" coordinate system used by the XR +// service to the frame of reference's coordinate system. This model is a bit +// over-simplified and will need to be made more robust when we start dealing +// with world-scale 6DoF tracking. +std::unique_ptr<TransformationMatrix> XRFrameOfReference::TransformBasePose( + const TransformationMatrix& base_pose) { + switch (type_) { + case kTypeHeadModel: { + // TODO(bajones): Detect if base pose is already neck modeled and return + // it unchanged if so for better performance. + + // Strip out translation component. + std::unique_ptr<TransformationMatrix> pose( + TransformationMatrix::Create(base_pose)); + pose->SetM41(0.0); + pose->SetM42(0.0); + pose->SetM43(0.0); + // TODO(bajones): Apply our own neck model + return pose; + } break; + case kTypeEyeLevel: + // For now we assume that all base poses are delivered as eye-level poses. + // Thus in this case we just return the pose without transformation. + return TransformationMatrix::Create(base_pose); + break; + case kTypeStage: + // Check first to see if the xrDisplayInfo has updated since the last + // call. If so, update the pose transform. + if (display_info_id_ != session()->device()->xrDisplayInfoPtrId()) + UpdateStageTransform(); + + // If the stage has a transform apply it to the base pose and return that, + // otherwise return null. + if (pose_transform_) { + std::unique_ptr<TransformationMatrix> pose( + TransformationMatrix::Create(*pose_transform_)); + pose->Multiply(base_pose); + return pose; + } + break; + } + + return nullptr; +} + +// Serves the same purpose as TransformBasePose, but for input poses. Needs to +// know the head pose so that cases like the headModel frame of reference can +// properly adjust the input's relative position. +std::unique_ptr<TransformationMatrix> +XRFrameOfReference::TransformBaseInputPose( + const TransformationMatrix& base_input_pose, + const TransformationMatrix& base_pose) { + switch (type_) { + case kTypeHeadModel: { + std::unique_ptr<TransformationMatrix> head_model_pose( + TransformBasePose(base_pose)); + + // Get the positional delta between the base pose and the head model pose. + float dx = head_model_pose->M41() - base_pose.M41(); + float dy = head_model_pose->M42() - base_pose.M42(); + float dz = head_model_pose->M43() - base_pose.M43(); + + // Translate the controller by the same delta so that it shows up in the + // right relative position. + std::unique_ptr<TransformationMatrix> pose( + TransformationMatrix::Create(base_input_pose)); + pose->SetM41(pose->M41() + dx); + pose->SetM42(pose->M42() + dy); + pose->SetM43(pose->M43() + dz); + + return pose; + } break; + case kTypeEyeLevel: + case kTypeStage: + return TransformBasePose(base_input_pose); + break; + } + + return nullptr; +} + +void XRFrameOfReference::Trace(blink::Visitor* visitor) { + visitor->Trace(bounds_); + XRCoordinateSystem::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.h b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.h new file mode 100644 index 00000000000..eca778a2208 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.h @@ -0,0 +1,53 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_OF_REFERENCE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_OF_REFERENCE_H_ + +#include "third_party/blink/renderer/modules/xr/xr_coordinate_system.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" + +namespace blink { + +class XRStageBounds; + +class XRFrameOfReference final : public XRCoordinateSystem { + DEFINE_WRAPPERTYPEINFO(); + + public: + enum Type { kTypeHeadModel, kTypeEyeLevel, kTypeStage }; + + XRFrameOfReference(XRSession*, Type); + ~XRFrameOfReference() override; + + void UpdatePoseTransform(std::unique_ptr<TransformationMatrix>); + void UpdateStageBounds(XRStageBounds*); + void UseEmulatedHeight(double value); + + std::unique_ptr<TransformationMatrix> TransformBasePose( + const TransformationMatrix& base_pose) override; + std::unique_ptr<TransformationMatrix> TransformBaseInputPose( + const TransformationMatrix& base_input_pose, + const TransformationMatrix& base_pose) override; + + XRStageBounds* bounds() const { return bounds_; } + double emulatedHeight() const { return emulated_height_; } + + Type type() const { return type_; } + + void Trace(blink::Visitor*) override; + + private: + void UpdateStageTransform(); + + Member<XRStageBounds> bounds_; + double emulated_height_ = 0.0; + Type type_; + std::unique_ptr<TransformationMatrix> pose_transform_; + unsigned int display_info_id_ = 0; +}; + +} // namespace blink + +#endif // XRWebGLLayer_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.idl b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.idl new file mode 100644 index 00000000000..e3906654d45 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference.idl @@ -0,0 +1,19 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrframeofreference-interface + +enum XRFrameOfReferenceType { + "headModel", + "eyeLevel", + "stage", +}; + +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRFrameOfReference : XRCoordinateSystem { + readonly attribute XRStageBounds? bounds; + readonly attribute double emulatedHeight; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference_options.idl b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference_options.idl new file mode 100644 index 00000000000..e1bf41d728f --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_of_reference_options.idl @@ -0,0 +1,11 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrframeofreference-interface +[ + SecureContext +] dictionary XRFrameOfReferenceOptions { + boolean disableStageEmulation = false; + double stageEmulationHeight = 0.0; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.cc new file mode 100644 index 00000000000..ee7b59ec2d5 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.cc @@ -0,0 +1,460 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" + +#include "build/build_config.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h" +#include "third_party/blink/renderer/modules/xr/xr.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_context.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_viewport.h" +#include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h" +#include "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h" +#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/time.h" + +namespace blink { + +namespace { + +class XRFrameProviderRequestCallback + : public FrameRequestCallbackCollection::FrameCallback { + public: + explicit XRFrameProviderRequestCallback(XRFrameProvider* frame_provider) + : frame_provider_(frame_provider) {} + ~XRFrameProviderRequestCallback() override = default; + void Invoke(double high_res_time_ms) override { + frame_provider_->OnNonExclusiveVSync(high_res_time_ms / 1000.0); + } + + virtual void Trace(blink::Visitor* visitor) { + visitor->Trace(frame_provider_); + + FrameRequestCallbackCollection::FrameCallback::Trace(visitor); + } + + Member<XRFrameProvider> frame_provider_; +}; + +std::unique_ptr<TransformationMatrix> getPoseMatrix( + const device::mojom::blink::VRPosePtr& pose) { + if (!pose) + return nullptr; + + std::unique_ptr<TransformationMatrix> pose_matrix = + TransformationMatrix::Create(); + + TransformationMatrix::DecomposedType decomp; + + memset(&decomp, 0, sizeof(decomp)); + decomp.perspective_w = 1; + decomp.scale_x = 1; + decomp.scale_y = 1; + decomp.scale_z = 1; + + if (pose->orientation) { + decomp.quaternion_x = -pose->orientation.value()[0]; + decomp.quaternion_y = -pose->orientation.value()[1]; + decomp.quaternion_z = -pose->orientation.value()[2]; + decomp.quaternion_w = pose->orientation.value()[3]; + } else { + decomp.quaternion_w = 1.0; + } + + if (pose->position) { + decomp.translate_x = pose->position.value()[0]; + decomp.translate_y = pose->position.value()[1]; + decomp.translate_z = pose->position.value()[2]; + } + + pose_matrix->Recompose(decomp); + + return pose_matrix; +} + +} // namespace + +XRFrameProvider::XRFrameProvider(XRDevice* device) + : device_(device), last_has_focus_(device->HasDeviceAndFrameFocus()) {} + +void XRFrameProvider::BeginExclusiveSession(XRSession* session, + ScriptPromiseResolver* resolver) { + // Make sure the session is indeed an exclusive one. + DCHECK(session && session->exclusive()); + + // Ensure we can only have one exclusive session at a time. + DCHECK(!exclusive_session_); + DCHECK(!exclusive_session_can_send_frames_); + + exclusive_session_ = session; + exclusive_session_can_send_frames_ = false; + + pending_exclusive_session_resolver_ = resolver; + + // Establish the connection with the VSyncProvider if needed. + if (!presentation_provider_.is_bound()) { + frame_transport_ = new XRFrameTransport(); + + // Set up RequestPresentOptions based on canvas properties. + device::mojom::blink::VRRequestPresentOptionsPtr options = + device::mojom::blink::VRRequestPresentOptions::New(); + options->preserve_drawing_buffer = false; + options->webxr_input = true; + options->shared_buffer_draw_supported = true; + + // TODO(offenwanger): Once device activation is sorted out for WebXR, either + // pass in the value for metrics, or remove it as soon as legacy API has + // been removed. + device_->xrDisplayHostPtr()->RequestPresent( + frame_transport_->GetSubmitFrameClient(), + mojo::MakeRequest(&presentation_provider_), std::move(options), false, + WTF::Bind(&XRFrameProvider::OnPresentComplete, WrapPersistent(this))); + + presentation_provider_.set_connection_error_handler( + WTF::Bind(&XRFrameProvider::OnPresentationProviderConnectionError, + WrapWeakPersistent(this))); + } +} + +void XRFrameProvider::OnPresentComplete( + bool success, + device::mojom::blink::VRDisplayFrameTransportOptionsPtr transport_options) { + if (success) { + frame_transport_->SetTransportOptions(std::move(transport_options)); + frame_transport_->PresentChange(); + pending_exclusive_session_resolver_->Resolve(exclusive_session_); + exclusive_session_can_send_frames_ = true; + } else { + exclusive_session_->ForceEnd(); + exclusive_session_can_send_frames_ = false; + + if (pending_exclusive_session_resolver_) { + DOMException* exception = DOMException::Create( + kNotAllowedError, "Request for exclusive XRSession was denied."); + pending_exclusive_session_resolver_->Reject(exception); + } + } + + pending_exclusive_session_resolver_ = nullptr; +} + +void XRFrameProvider::OnFocusChanged() { + bool focus = device_->HasDeviceAndFrameFocus(); + + // If we are gaining focus, schedule a frame for magic window. This accounts + // for skipping RAFs in ProcessScheduledFrame. Only do this when there are + // magic window sessions but no exclusive session. Note that exclusive + // sessions don't stop scheduling RAFs when focus is lost, so there is no need + // to schedule exclusive frames when focus is acquired. + if (focus && !last_has_focus_ && requesting_sessions_.size() > 0 && + !exclusive_session_) { + ScheduleNonExclusiveFrame(); + } + last_has_focus_ = focus; +} + +void XRFrameProvider::OnPresentationProviderConnectionError() { + if (pending_exclusive_session_resolver_) { + DOMException* exception = DOMException::Create( + kNotAllowedError, + "Error occured while requesting exclusive XRSession."); + pending_exclusive_session_resolver_->Reject(exception); + pending_exclusive_session_resolver_ = nullptr; + } + presentation_provider_.reset(); + if (vsync_connection_failed_) + return; + exclusive_session_->ForceEnd(); + exclusive_session_can_send_frames_ = false; + vsync_connection_failed_ = true; +} + +// Called by the exclusive session when it is ended. +void XRFrameProvider::OnExclusiveSessionEnded() { + if (!exclusive_session_) + return; + + device_->xrDisplayHostPtr()->ExitPresent(); + + exclusive_session_ = nullptr; + exclusive_session_can_send_frames_ = false; + pending_exclusive_vsync_ = false; + frame_id_ = -1; + + if (presentation_provider_.is_bound()) { + presentation_provider_.reset(); + } + + frame_transport_ = nullptr; + + // When we no longer have an active exclusive session schedule all the + // outstanding frames that were requested while the exclusive session was + // active. + if (requesting_sessions_.size() > 0) + ScheduleNonExclusiveFrame(); +} + +// Schedule a session to be notified when the next XR frame is available. +void XRFrameProvider::RequestFrame(XRSession* session) { + DVLOG(2) << __FUNCTION__; + + // If a previous session has already requested a frame don't fire off another + // request. All requests will be fullfilled at once when OnVSync is called. + if (session->exclusive()) { + ScheduleExclusiveFrame(); + } else { + requesting_sessions_.push_back(session); + + // If there's an active exclusive session save the request but suppress + // processing it until the exclusive session is no longer active. + if (exclusive_session_) + return; + + ScheduleNonExclusiveFrame(); + } +} + +void XRFrameProvider::ScheduleExclusiveFrame() { + if (pending_exclusive_vsync_) + return; + + pending_exclusive_vsync_ = true; + + presentation_provider_->GetVSync( + WTF::Bind(&XRFrameProvider::OnExclusiveVSync, WrapWeakPersistent(this))); +} + +void XRFrameProvider::ScheduleNonExclusiveFrame() { + if (pending_non_exclusive_vsync_) + return; + + LocalFrame* frame = device_->xr()->GetFrame(); + if (!frame) + return; + + Document* doc = frame->GetDocument(); + if (!doc) + return; + + pending_non_exclusive_vsync_ = true; + + device_->xrMagicWindowProviderPtr()->GetPose(WTF::Bind( + &XRFrameProvider::OnNonExclusivePose, WrapWeakPersistent(this))); + doc->RequestAnimationFrame(new XRFrameProviderRequestCallback(this)); +} + +void XRFrameProvider::OnExclusiveVSync( + device::mojom::blink::VRPosePtr pose, + WTF::TimeDelta time_delta, + int16_t frame_id, + device::mojom::blink::VRPresentationProvider::VSyncStatus status, + const base::Optional<gpu::MailboxHolder>& buffer_holder) { + DVLOG(2) << __FUNCTION__; + vsync_connection_failed_ = false; + switch (status) { + case device::mojom::blink::VRPresentationProvider::VSyncStatus::SUCCESS: + break; + case device::mojom::blink::VRPresentationProvider::VSyncStatus::CLOSING: + return; + } + + // We may have lost the exclusive session since the last VSync request. + if (!exclusive_session_) { + return; + } + + frame_pose_ = std::move(pose); + frame_id_ = frame_id; + buffer_mailbox_holder_ = buffer_holder; + + pending_exclusive_vsync_ = false; + + // Post a task to handle scheduled animations after the current + // execution context finishes, so that we yield to non-mojo tasks in + // between frames. Executing mojo tasks back to back within the same + // execution context caused extreme input delay due to processing + // multiple frames without yielding, see crbug.com/701444. + Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&XRFrameProvider::ProcessScheduledFrame, + WrapWeakPersistent(this), time_delta.InSecondsF())); +} + +void XRFrameProvider::OnNonExclusiveVSync(double timestamp) { + DVLOG(2) << __FUNCTION__; + + pending_non_exclusive_vsync_ = false; + + // Suppress non-exclusive vsyncs when there's an exclusive session active. + if (exclusive_session_) + return; + + Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&XRFrameProvider::ProcessScheduledFrame, + WrapWeakPersistent(this), timestamp)); +} + +void XRFrameProvider::OnNonExclusivePose(device::mojom::blink::VRPosePtr pose) { + frame_pose_ = std::move(pose); +} + +void XRFrameProvider::ProcessScheduledFrame(double timestamp) { + DVLOG(2) << __FUNCTION__; + + TRACE_EVENT1("gpu", "XRFrameProvider::ProcessScheduledFrame", "frame", + 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 (frame_pose_ && frame_pose_->input_state.has_value()) { + exclusive_session_->OnInputStateChange(frame_id_, + frame_pose_->input_state.value()); + } + + // If there's an exclusive session active only process its frame. + std::unique_ptr<TransformationMatrix> pose_matrix = + getPoseMatrix(frame_pose_); + // Sanity check: if drawing into a shared buffer, the optional mailbox + // holder must be present. + DCHECK(!frame_transport_->DrawingIntoSharedBuffer() || + buffer_mailbox_holder_); + exclusive_session_->OnFrame(std::move(pose_matrix), buffer_mailbox_holder_); + } else { + // In the process of fulfilling the frame requests for each session they are + // extremely likely to request another frame. Work off of a separate list + // from the requests to prevent infinite loops. + HeapVector<Member<XRSession>> processing_sessions; + swap(requesting_sessions_, processing_sessions); + + // Inform sessions with a pending request of the new frame + for (unsigned i = 0; i < processing_sessions.size(); ++i) { + XRSession* session = processing_sessions.at(i).Get(); + + if (frame_pose_ && frame_pose_->input_state.has_value()) { + session->OnInputStateChange(frame_id_, + frame_pose_->input_state.value()); + } + + std::unique_ptr<TransformationMatrix> pose_matrix = + getPoseMatrix(frame_pose_); + session->OnFrame(std::move(pose_matrix), base::nullopt); + } + } +} + +void XRFrameProvider::SubmitWebGLLayer(XRWebGLLayer* layer, bool was_changed) { + DCHECK(layer); + DCHECK(layer->session() == exclusive_session_); + if (!presentation_provider_.is_bound()) + return; + + TRACE_EVENT1("gpu", "XRFrameProvider::SubmitWebGLLayer", "frame", frame_id_); + + WebGLRenderingContextBase* webgl_context = layer->context(); + + if (!was_changed) { + // Just tell the device side that there was no submitted frame instead + // of executing the implicit end-of-frame submit. + frame_transport_->FrameSubmitMissing(presentation_provider_.get(), + webgl_context->ContextGL(), frame_id_); + return; + } + + frame_transport_->FramePreImage(webgl_context->ContextGL()); + + std::unique_ptr<viz::SingleReleaseCallback> image_release_callback; + + DCHECK(exclusive_session_can_send_frames_); + if (frame_transport_->DrawingIntoSharedBuffer()) { + // Image is written to shared buffer already. Just submit with a + // placeholder. + scoped_refptr<Image> image_ref = nullptr; + bool needs_copy = false; + DVLOG(3) << __FUNCTION__ << ": FrameSubmit for SharedBuffer mode"; + frame_transport_->FrameSubmit( + presentation_provider_.get(), webgl_context->ContextGL(), webgl_context, + std::move(image_ref), std::move(image_release_callback), frame_id_, + needs_copy); + return; + } + + scoped_refptr<StaticBitmapImage> image_ref = + layer->TransferToStaticBitmapImage(&image_release_callback); + + if (!image_ref) + return; + + // Hardware-accelerated rendering should always be texture backed. Ensure this + // is the case, don't attempt to render if using an unexpected drawing path. + if (!image_ref->IsTextureBacked()) { + NOTREACHED() << "WebXR requires hardware-accelerated rendering to texture"; + return; + } + + // TODO(bajones): Remove this when the Windows path has been updated to no + // longer require a texture copy. + bool needs_copy = device_->external(); + + frame_transport_->FrameSubmit( + presentation_provider_.get(), webgl_context->ContextGL(), webgl_context, + std::move(image_ref), std::move(image_release_callback), frame_id_, + needs_copy); + + // Reset our frame id, since anything we'd want to do (resizing/etc) can + // no-longer happen to this frame. + frame_id_ = -1; +} + +// TODO(bajones): This only works because we're restricted to a single layer at +// the moment. Will need an overhaul when we get more robust layering support. +void XRFrameProvider::UpdateWebGLLayerViewports(XRWebGLLayer* layer) { + DCHECK(layer->session() == exclusive_session_); + DCHECK(presentation_provider_); + + XRViewport* left = layer->GetViewportForEye(XRView::kEyeLeft); + XRViewport* right = layer->GetViewportForEye(XRView::kEyeRight); + float width = layer->framebufferWidth(); + float height = layer->framebufferHeight(); + + WebFloatRect left_coords( + static_cast<float>(left->x()) / width, + static_cast<float>(height - (left->y() + left->height())) / height, + static_cast<float>(left->width()) / width, + static_cast<float>(left->height()) / height); + WebFloatRect right_coords( + static_cast<float>(right->x()) / width, + static_cast<float>(height - (right->y() + right->height())) / height, + static_cast<float>(right->width()) / width, + static_cast<float>(right->height()) / height); + + presentation_provider_->UpdateLayerBounds( + frame_id_, left_coords, right_coords, WebSize(width, height)); +} + +void XRFrameProvider::Dispose() { + presentation_provider_.reset(); + // TODO(bajones): Do something for outstanding frame requests? +} + +void XRFrameProvider::Trace(blink::Visitor* visitor) { + visitor->Trace(device_); + visitor->Trace(pending_exclusive_session_resolver_); + visitor->Trace(frame_transport_); + visitor->Trace(exclusive_session_); + visitor->Trace(requesting_sessions_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.h new file mode 100644 index 00000000000..4aa8ea2cc3a --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_provider.h @@ -0,0 +1,97 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_PROVIDER_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_PROVIDER_H_ + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class ScriptPromiseResolver; +class XRDevice; +class XRSession; +class XRFrameTransport; +class XRWebGLLayer; + +// This class manages requesting and dispatching frame updates, which includes +// pose information for a given XRDevice. +class XRFrameProvider final + : public GarbageCollectedFinalized<XRFrameProvider> { + public: + explicit XRFrameProvider(XRDevice*); + + XRSession* exclusive_session() const { return exclusive_session_; } + + void BeginExclusiveSession(XRSession*, ScriptPromiseResolver*); + void OnExclusiveSessionEnded(); + + void RequestFrame(XRSession*); + + void OnNonExclusiveVSync(double timestamp); + + void SubmitWebGLLayer(XRWebGLLayer*, bool was_changed); + void UpdateWebGLLayerViewports(XRWebGLLayer*); + + void Dispose(); + void OnFocusChanged(); + + virtual void Trace(blink::Visitor*); + + private: + void OnExclusiveVSync( + device::mojom::blink::VRPosePtr, + WTF::TimeDelta, + int16_t frame_id, + device::mojom::blink::VRPresentationProvider::VSyncStatus, + const base::Optional<gpu::MailboxHolder>& buffer_holder); + void OnNonExclusivePose(device::mojom::blink::VRPosePtr); + + void ScheduleExclusiveFrame(); + void ScheduleNonExclusiveFrame(); + + void OnPresentComplete( + bool success, + device::mojom::blink::VRDisplayFrameTransportOptionsPtr); + void OnPresentationProviderConnectionError(); + void ProcessScheduledFrame(double timestamp); + + const Member<XRDevice> device_; + Member<XRSession> exclusive_session_; + Member<ScriptPromiseResolver> pending_exclusive_session_resolver_; + Member<XRFrameTransport> frame_transport_; + + // Careful, exclusive_session_ being true does not mean it's OK to send + // frames. The initialization handshake may not be complete yet. This boolean + // starts out false at the start of a session, becomes true after a + // successful OnPresentComplete(), and remains true for the lifetime of the + // exclusive session. + bool exclusive_session_can_send_frames_ = false; + + // Non-exclusive Sessions which have requested a frame update. + HeapVector<Member<XRSession>> requesting_sessions_; + + device::mojom::blink::VRPresentationProviderPtr presentation_provider_; + device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider_; + device::mojom::blink::VRPosePtr frame_pose_; + + // This frame ID is XR-specific and is used to track when frames arrive at the + // XR compositor so that it knows which poses to use, when to apply bounds + // updates, etc. + int16_t frame_id_ = -1; + bool pending_exclusive_vsync_ = false; + bool pending_non_exclusive_vsync_ = false; + bool vsync_connection_failed_ = false; + + base::Optional<gpu::MailboxHolder> buffer_mailbox_holder_; + bool last_has_focus_ = false; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_PROVIDER_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback.idl b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback.idl new file mode 100644 index 00000000000..fc182d9b396 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback.idl @@ -0,0 +1,6 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#callbackdef-xrframerequestcallback +callback XRFrameRequestCallback = void (DOMHighResTimeStamp time, XRPresentationFrame frame); diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.cc b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.cc new file mode 100644 index 00000000000..c66b5e2ec9d --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.cc @@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h" + +#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_frame_request_callback.h" +#include "third_party/blink/renderer/core/inspector/InspectorTraceEvents.h" +#include "third_party/blink/renderer/core/probe/core_probes.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_frame.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +namespace blink { + +XRFrameRequestCallbackCollection::XRFrameRequestCallbackCollection( + ExecutionContext* context) + : context_(context) {} + +XRFrameRequestCallbackCollection::CallbackId +XRFrameRequestCallbackCollection::RegisterCallback( + V8XRFrameRequestCallback* callback) { + CallbackId id = ++next_callback_id_; + callbacks_.Set(id, callback); + pending_callbacks_.push_back(id); + + probe::AsyncTaskScheduledBreakable(context_, "XRRequestFrame", callback); + return id; +} + +void XRFrameRequestCallbackCollection::CancelCallback(CallbackId id) { + if (IsValidCallbackId(id)) { + callbacks_.erase(id); + } +} + +void XRFrameRequestCallbackCollection::ExecuteCallbacks( + XRSession* session, + XRPresentationFrame* frame) { + // First, generate a list of callbacks to consider. Callbacks registered from + // this point on are considered only for the "next" frame, not this one. + DCHECK(callbacks_to_invoke_.IsEmpty()); + callbacks_to_invoke_.swap(pending_callbacks_); + + for (const auto& id : callbacks_to_invoke_) { + V8XRFrameRequestCallback* callback = callbacks_.Take(id); + + // Callback won't be found if it was cancelled. + if (!callback) + continue; + + probe::AsyncTask async_task(context_, callback); + probe::UserCallback probe(context_, "XRRequestFrame", AtomicString(), true); + callback->InvokeAndReportException(session, 0, frame); + } + + callbacks_to_invoke_.clear(); +} + +void XRFrameRequestCallbackCollection::Trace(blink::Visitor* visitor) { + visitor->Trace(callbacks_); + visitor->Trace(context_); +} + +void XRFrameRequestCallbackCollection::TraceWrappers( + const blink::ScriptWrappableVisitor* visitor) const { + for (const auto& callback : callbacks_.Values()) { + visitor->TraceWrappers(callback); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h new file mode 100644 index 00000000000..4bc51f240b4 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_REQUEST_CALLBACK_COLLECTION_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_FRAME_REQUEST_CALLBACK_COLLECTION_H_ + +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" +#include "third_party/blink/renderer/platform/heap/handle.h" + +namespace blink { + +class ExecutionContext; +class V8XRFrameRequestCallback; +class XRPresentationFrame; +class XRSession; + +class XRFrameRequestCallbackCollection final : public TraceWrapperBase { + DISALLOW_NEW(); + + public: + explicit XRFrameRequestCallbackCollection(ExecutionContext*); + + using CallbackId = int; + CallbackId RegisterCallback(V8XRFrameRequestCallback*); + void CancelCallback(CallbackId); + void ExecuteCallbacks(XRSession*, XRPresentationFrame*); + + bool IsEmpty() const { return !callbacks_.size(); } + + void Trace(blink::Visitor*); + void TraceWrappers(const blink::ScriptWrappableVisitor*) const override; + const char* NameInHeapSnapshot() const override { + return "XRFrameRequestCallbackCollection"; + } + + private: + bool IsValidCallbackId(int id) { + using Traits = HashTraits<CallbackId>; + return !Traits::IsDeletedValue(id) && + !WTF::IsHashTraitsEmptyValue<Traits, CallbackId>(id); + } + + using CallbackMap = + HeapHashMap<CallbackId, TraceWrapperMember<V8XRFrameRequestCallback>>; + CallbackMap callbacks_; + Vector<CallbackId> pending_callbacks_; + // Only non-empty while inside executeCallbacks. + Vector<CallbackId> callbacks_to_invoke_; + + CallbackId next_callback_id_ = 0; + + Member<ExecutionContext> context_; +}; + +} // namespace blink + +#endif // FrameRequestCallbackCollection_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.cc b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.cc new file mode 100644 index 00000000000..c40afa39796 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.cc @@ -0,0 +1,36 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_input_pose.h" + +#include "third_party/blink/renderer/modules/xr/xr_utils.h" + +namespace blink { + +XRInputPose::XRInputPose(std::unique_ptr<TransformationMatrix> pointer_matrix, + std::unique_ptr<TransformationMatrix> grip_matrix, + bool emulated_position) + : pointer_matrix_(std::move(pointer_matrix)), + grip_matrix_(std::move(grip_matrix)), + emulated_position_(emulated_position) {} + +XRInputPose::~XRInputPose() {} + +DOMFloat32Array* XRInputPose::pointerMatrix() const { + if (!pointer_matrix_) + return nullptr; + return transformationMatrixToFloat32Array(*pointer_matrix_); +} + +DOMFloat32Array* XRInputPose::gripMatrix() const { + if (!grip_matrix_) + return nullptr; + return transformationMatrixToFloat32Array(*grip_matrix_); +} + +void XRInputPose::Trace(blink::Visitor* visitor) { + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.h b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.h new file mode 100644 index 00000000000..ec5d269eb85 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.h @@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_POSE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_POSE_H_ + +#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRInputPose final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRInputPose(std::unique_ptr<TransformationMatrix> pointer_matrix, + std::unique_ptr<TransformationMatrix> grip_matrix, + bool emulated_position = false); + ~XRInputPose(); + + DOMFloat32Array* pointerMatrix() const; + DOMFloat32Array* gripMatrix() const; + bool emulatedPosition() const { return emulated_position_; } + + virtual void Trace(blink::Visitor*); + + private: + const std::unique_ptr<TransformationMatrix> pointer_matrix_; + const std::unique_ptr<TransformationMatrix> grip_matrix_; + const bool emulated_position_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_POSE_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.idl b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.idl new file mode 100644 index 00000000000..6113cc72548 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_pose.idl @@ -0,0 +1,12 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRInputPose { + readonly attribute Float32Array pointerMatrix; + readonly attribute Float32Array? gripMatrix; + readonly attribute boolean emulatedPosition; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source.cc b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.cc new file mode 100644 index 00000000000..761a46d50c6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.cc @@ -0,0 +1,77 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_input_source.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +namespace blink { + +XRInputSource::XRInputSource(XRSession* session, uint32_t source_id) + : session_(session), source_id_(source_id) { + SetPointerOrigin(kOriginHead); + SetHandedness(kHandNone); +} + +void XRInputSource::SetPointerOrigin(PointerOrigin pointer_origin) { + if (pointer_origin_ == pointer_origin) + return; + + pointer_origin_ = pointer_origin; + + switch (pointer_origin_) { + case kOriginHead: + pointer_origin_string_ = "head"; + break; + case kOriginHand: + pointer_origin_string_ = "hand"; + break; + case kOriginScreen: + pointer_origin_string_ = "screen"; + break; + default: + NOTREACHED() << "Unknown pointer origin: " << pointer_origin_; + } +} + +void XRInputSource::SetHandedness(Handedness handedness) { + if (handedness_ == handedness) + return; + + handedness_ = handedness; + + switch (handedness_) { + case kHandNone: + handedness_string_ = ""; + break; + case kHandLeft: + handedness_string_ = "left"; + break; + case kHandRight: + handedness_string_ = "right"; + break; + default: + NOTREACHED() << "Unknown handedness: " << handedness_; + } +} + +void XRInputSource::SetEmulatedPosition(bool emulated_position) { + emulated_position_ = emulated_position; +} + +void XRInputSource::SetBasePoseMatrix( + std::unique_ptr<TransformationMatrix> base_pose_matrix) { + base_pose_matrix_ = std::move(base_pose_matrix); +} + +void XRInputSource::SetPointerTransformMatrix( + std::unique_ptr<TransformationMatrix> pointer_transform_matrix) { + pointer_transform_matrix_ = std::move(pointer_transform_matrix); +} + +void XRInputSource::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source.h b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.h new file mode 100644 index 00000000000..f1786bfb098 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.h @@ -0,0 +1,71 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class XRSession; + +class XRInputSource : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + enum Handedness { kHandNone = 0, kHandLeft = 1, kHandRight = 2 }; + enum PointerOrigin { kOriginHead = 1, kOriginHand = 2, kOriginScreen = 3 }; + + XRInputSource(XRSession*, uint32_t source_id); + virtual ~XRInputSource() = default; + + XRSession* session() const { return session_; } + + const String& handedness() const { return handedness_string_; } + const String& pointerOrigin() const { return pointer_origin_string_; } + bool emulatedPosition() const { return emulated_position_; } + + uint32_t source_id() const { return source_id_; } + + void SetPointerOrigin(PointerOrigin); + void SetHandedness(Handedness); + void SetEmulatedPosition(bool emulated_position); + void SetBasePoseMatrix(std::unique_ptr<TransformationMatrix>); + void SetPointerTransformMatrix(std::unique_ptr<TransformationMatrix>); + + virtual void Trace(blink::Visitor*); + + int16_t active_frame_id = -1; + bool primary_input_pressed = false; + bool selection_cancelled = false; + + private: + friend class XRPresentationFrame; + + const Member<XRSession> session_; + const uint32_t source_id_; + + Handedness handedness_; + String handedness_string_; + + PointerOrigin pointer_origin_; + String pointer_origin_string_; + + bool emulated_position_ = false; + + std::unique_ptr<TransformationMatrix> base_pose_matrix_; + + // This is the transform to apply to the base_pose_matrix_ to get the pointer + // matrix. In most cases it should be static. + std::unique_ptr<TransformationMatrix> pointer_transform_matrix_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source.idl b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.idl new file mode 100644 index 00000000000..bc084732141 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source.idl @@ -0,0 +1,22 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +enum XRHandedness { + "left", + "right" +}; + +enum XRPointerOrigin { + "head", + "grip", + "screen" +}; + +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRInputSource { + readonly attribute XRHandedness handedness; + readonly attribute XRPointerOrigin pointerOrigin; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.cc b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.cc new file mode 100644 index 00000000000..ce47b89c169 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.cc @@ -0,0 +1,40 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_input_source_event.h" + +namespace blink { + +XRInputSourceEvent::XRInputSourceEvent() {} + +XRInputSourceEvent::XRInputSourceEvent(const AtomicString& type, + XRPresentationFrame* frame, + XRInputSource* input_source) + : Event(type, Bubbles::kYes, Cancelable::kNo), + frame_(frame), + input_source_(input_source) {} + +XRInputSourceEvent::XRInputSourceEvent( + const AtomicString& type, + const XRInputSourceEventInit& initializer) + : Event(type, initializer) { + if (initializer.hasFrame()) + frame_ = initializer.frame(); + if (initializer.hasInputSource()) + input_source_ = initializer.inputSource(); +} + +XRInputSourceEvent::~XRInputSourceEvent() {} + +const AtomicString& XRInputSourceEvent::InterfaceName() const { + return EventNames::XRInputSourceEvent; +} + +void XRInputSourceEvent::Trace(blink::Visitor* visitor) { + visitor->Trace(frame_); + visitor->Trace(input_source_); + Event::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.h b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.h new file mode 100644 index 00000000000..bd9649588d9 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.h @@ -0,0 +1,53 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_EVENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_EVENT_H_ + +#include "third_party/blink/renderer/modules/event_modules.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source_event_init.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_frame.h" + +namespace blink { + +class XRInputSourceEvent final : public Event { + DEFINE_WRAPPERTYPEINFO(); + + public: + static XRInputSourceEvent* Create() { return new XRInputSourceEvent; } + static XRInputSourceEvent* Create(const AtomicString& type, + XRPresentationFrame* frame, + XRInputSource* input_source) { + return new XRInputSourceEvent(type, frame, input_source); + } + + static XRInputSourceEvent* Create(const AtomicString& type, + const XRInputSourceEventInit& initializer) { + return new XRInputSourceEvent(type, initializer); + } + + ~XRInputSourceEvent() override; + + XRPresentationFrame* frame() const { return frame_.Get(); } + XRInputSource* inputSource() const { return input_source_.Get(); } + + const AtomicString& InterfaceName() const override; + + virtual void Trace(blink::Visitor*); + + private: + XRInputSourceEvent(); + XRInputSourceEvent(const AtomicString& type, + XRPresentationFrame*, + XRInputSource*); + XRInputSourceEvent(const AtomicString&, const XRInputSourceEventInit&); + + Member<XRPresentationFrame> frame_; + Member<XRInputSource> input_source_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCE_EVENT_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.idl b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.idl new file mode 100644 index 00000000000..cbcb28c9150 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event.idl @@ -0,0 +1,12 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + SecureContext, + OriginTrialEnabled=WebXR, + Constructor(DOMString type, XRInputSourceEventInit eventInitDict) +] interface XRInputSourceEvent : Event { + readonly attribute XRPresentationFrame frame; + readonly attribute XRInputSource inputSource; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event_init.idl b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event_init.idl new file mode 100644 index 00000000000..b24af31a375 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_input_source_event_init.idl @@ -0,0 +1,10 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + SecureContext +] dictionary XRInputSourceEventInit : EventInit { + required XRPresentationFrame frame; + required XRInputSource inputSource; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_layer.cc b/chromium/third_party/blink/renderer/modules/xr/xr_layer.cc new file mode 100644 index 00000000000..6dd1aeaa952 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_layer.cc @@ -0,0 +1,24 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_layer.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" + +namespace blink { + +XRLayer::XRLayer(XRSession* session, XRLayerType layer_type) + : session_(session), layer_type_(layer_type) {} + +void XRLayer::OnFrameStart(const base::Optional<gpu::MailboxHolder>&) {} +void XRLayer::OnFrameEnd() {} +void XRLayer::OnResize() {} + +void XRLayer::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_layer.h b/chromium/third_party/blink/renderer/modules/xr/xr_layer.h new file mode 100644 index 00000000000..d6368242885 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_layer.h @@ -0,0 +1,41 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRSession; + +enum XRLayerType { kXRWebGLLayerType }; + +class XRLayer : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRLayer(XRSession*, XRLayerType); + + XRSession* session() const { return session_; } + XRLayerType layerType() const { return layer_type_; } + + virtual void OnFrameStart(const base::Optional<gpu::MailboxHolder>&); + virtual void OnFrameEnd(); + virtual void OnResize(); + + virtual void Trace(blink::Visitor*); + + private: + const Member<XRSession> session_; + XRLayerType layer_type_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_layer.idl b/chromium/third_party/blink/renderer/modules/xr/xr_layer.idl new file mode 100644 index 00000000000..1b411d681ab --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_layer.idl @@ -0,0 +1,9 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrlayer-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRLayer {}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.cc b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.cc new file mode 100644 index 00000000000..ba46309b911 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.cc @@ -0,0 +1,32 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_presentation_context.h" + +#include "third_party/blink/renderer/bindings/modules/v8/rendering_context.h" +#include "third_party/blink/renderer/core/origin_trials/origin_trials.h" + +namespace blink { + +XRPresentationContext::XRPresentationContext( + CanvasRenderingContextHost* host, + const CanvasContextCreationAttributesCore& attrs) + : ImageBitmapRenderingContextBase(host, attrs) {} + +XRPresentationContext::~XRPresentationContext() {} + +void XRPresentationContext::SetCanvasGetContextResult( + RenderingContext& result) { + result.SetXRPresentationContext(this); +} + +CanvasRenderingContext* XRPresentationContext::Factory::Create( + CanvasRenderingContextHost* host, + const CanvasContextCreationAttributesCore& attrs) { + if (!OriginTrials::webXREnabled(host->GetTopExecutionContext())) + return nullptr; + return new XRPresentationContext(host, attrs); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.h b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.h new file mode 100644 index 00000000000..0cace4708a2 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.h @@ -0,0 +1,58 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PRESENTATION_CONTEXT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PRESENTATION_CONTEXT_H_ + +#include "base/memory/scoped_refptr.h" +#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h" +#include "third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h" +#include "third_party/blink/renderer/modules/modules_export.h" + +namespace blink { + +class MODULES_EXPORT XRPresentationContext final + : public ImageBitmapRenderingContextBase { + DEFINE_WRAPPERTYPEINFO(); + + public: + class Factory : public CanvasRenderingContextFactory { + WTF_MAKE_NONCOPYABLE(Factory); + + public: + Factory() {} + ~Factory() override {} + + CanvasRenderingContext* Create( + CanvasRenderingContextHost*, + const CanvasContextCreationAttributesCore&) override; + CanvasRenderingContext::ContextType GetContextType() const override { + return CanvasRenderingContext::kContextXRPresent; + } + }; + + // CanvasRenderingContext implementation + ContextType GetContextType() const override { + return CanvasRenderingContext::kContextXRPresent; + } + void SetCanvasGetContextResult(RenderingContext&) final; + + virtual ~XRPresentationContext(); + + private: + XRPresentationContext(CanvasRenderingContextHost*, + const CanvasContextCreationAttributesCore&); +}; + +DEFINE_TYPE_CASTS(XRPresentationContext, + CanvasRenderingContext, + context, + context->GetContextType() == + CanvasRenderingContext::kContextXRPresent, + context.GetContextType() == + CanvasRenderingContext::kContextXRPresent); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PRESENTATION_CONTEXT_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.idl b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.idl new file mode 100644 index 00000000000..c490176a130 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_context.idl @@ -0,0 +1,12 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrpresentationcontext-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRPresentationContext { + // back-reference to the canvas + readonly attribute HTMLCanvasElement canvas; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.cc b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.cc new file mode 100644 index 00000000000..cb1aa151016 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.cc @@ -0,0 +1,129 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_presentation_frame.h" + +#include "third_party/blink/renderer/modules/xr/xr_coordinate_system.h" +#include "third_party/blink/renderer/modules/xr/xr_device_pose.h" +#include "third_party/blink/renderer/modules/xr/xr_input_pose.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" + +namespace blink { + +XRPresentationFrame::XRPresentationFrame(XRSession* session) + : session_(session) {} + +const HeapVector<Member<XRView>>& XRPresentationFrame::views() const { + return session_->views(); +} + +XRDevicePose* XRPresentationFrame::getDevicePose( + XRCoordinateSystem* coordinate_system) const { + // If we don't have a valid base pose return null. Most common when tracking + // is lost. + if (!base_pose_matrix_ || !coordinate_system) { + return nullptr; + } + + // Must use a coordinate system created from the same session. + if (coordinate_system->session() != session_) { + return nullptr; + } + + std::unique_ptr<TransformationMatrix> pose = + coordinate_system->TransformBasePose(*base_pose_matrix_); + + if (!pose) { + return nullptr; + } + + return new XRDevicePose(session(), std::move(pose)); +} + +XRInputPose* XRPresentationFrame::getInputPose( + XRInputSource* input_source, + XRCoordinateSystem* coordinate_system) const { + if (!input_source || !coordinate_system) { + return nullptr; + } + + // Must use an input source and coordinate system from the same session. + if (input_source->session() != session_ || + coordinate_system->session() != session_) { + return nullptr; + } + + switch (input_source->pointer_origin_) { + case XRInputSource::kOriginScreen: { + // If the pointer origin is the screen we need the head's base pose and + // the pointer transform matrix to continue. The pointer transform will + // represent the point the canvas was clicked as an offset from the view. + if (!base_pose_matrix_ || !input_source->pointer_transform_matrix_) { + return nullptr; + } + + // Multiply the head pose and pointer transform to get the final pointer. + std::unique_ptr<TransformationMatrix> pointer_pose = + coordinate_system->TransformBasePose(*base_pose_matrix_); + pointer_pose->Multiply(*(input_source->pointer_transform_matrix_)); + + return new XRInputPose(std::move(pointer_pose), nullptr); + } + case XRInputSource::kOriginHead: { + // If the pointer origin is the users head, this is a gaze cursor and the + // returned pointer is based on the device pose. If we don't have a valid + // base pose (most common when tracking is lost) return null. + if (!base_pose_matrix_) { + return nullptr; + } + + // Just return the head pose as the pointer pose. + std::unique_ptr<TransformationMatrix> pointer_pose = + coordinate_system->TransformBasePose(*base_pose_matrix_); + + return new XRInputPose(std::move(pointer_pose), nullptr, + input_source->emulatedPosition()); + } + case XRInputSource::kOriginHand: { + // If the input source doesn't have a base pose return null; + if (!input_source->base_pose_matrix_) { + return nullptr; + } + + std::unique_ptr<TransformationMatrix> grip_pose = + coordinate_system->TransformBaseInputPose( + *(input_source->base_pose_matrix_), *base_pose_matrix_); + + if (!grip_pose) { + return nullptr; + } + + std::unique_ptr<TransformationMatrix> pointer_pose( + TransformationMatrix::Create(*grip_pose)); + + if (input_source->pointer_transform_matrix_) { + pointer_pose->Multiply(*(input_source->pointer_transform_matrix_)); + } + + return new XRInputPose(std::move(pointer_pose), std::move(grip_pose), + input_source->emulatedPosition()); + } + } + + return nullptr; +} + +void XRPresentationFrame::SetBasePoseMatrix( + const TransformationMatrix& base_pose_matrix) { + base_pose_matrix_ = TransformationMatrix::Create(base_pose_matrix); +} + +void XRPresentationFrame::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.h b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.h new file mode 100644 index 00000000000..efbfa207eb3 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.h @@ -0,0 +1,47 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PRESENTATION_FRAME_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PRESENTATION_FRAME_H_ + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRCoordinateSystem; +class XRDevicePose; +class XRInputPose; +class XRInputSource; +class XRSession; +class XRView; + +class XRPresentationFrame final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + explicit XRPresentationFrame(XRSession*); + + XRSession* session() const { return session_; } + + const HeapVector<Member<XRView>>& views() const; + XRDevicePose* getDevicePose(XRCoordinateSystem*) const; + XRInputPose* getInputPose(XRInputSource*, XRCoordinateSystem*) const; + + void SetBasePoseMatrix(const TransformationMatrix&); + + virtual void Trace(blink::Visitor*); + + private: + const Member<XRSession> session_; + std::unique_ptr<TransformationMatrix> base_pose_matrix_; +}; + +} // namespace blink + +#endif // XRWebGLLayer_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.idl b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.idl new file mode 100644 index 00000000000..51bb3b3df6a --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_presentation_frame.idl @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrpresentationframe-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRPresentationFrame { + readonly attribute XRSession session; + readonly attribute FrozenArray<XRView> views; + + XRDevicePose? getDevicePose(XRCoordinateSystem coordinateSystem); + XRInputPose? getInputPose(XRInputSource inputSource, + XRCoordinateSystem coordinateSystem); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session.cc b/chromium/third_party/blink/renderer/modules/xr/xr_session.cc new file mode 100644 index 00000000000..8217f882cc7 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session.cc @@ -0,0 +1,616 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_session.h" + +#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_frame_request_callback.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/dom/element.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/resize_observer/resize_observer.h" +#include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h" +#include "third_party/blink/renderer/modules/event_target_modules.h" +#include "third_party/blink/renderer/modules/xr/xr.h" +#include "third_party/blink/renderer/modules/xr/xr_canvas_input_provider.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_of_reference.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_of_reference_options.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source_event.h" +#include "third_party/blink/renderer/modules/xr/xr_layer.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_context.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_frame.h" +#include "third_party/blink/renderer/modules/xr/xr_session_event.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" +#include "third_party/blink/renderer/platform/wtf/auto_reset.h" + +namespace blink { + +namespace { + +const char kSessionEnded[] = "XRSession has already ended."; + +const char kUnknownFrameOfReference[] = "Unknown frame of reference type."; + +const char kNonEmulatedStageNotSupported[] = + "This device does not support a non-emulated 'stage' frame of reference."; + +const double kDegToRad = M_PI / 180.0; + +// TODO(bajones): This is something that we probably want to make configurable. +const double kMagicWindowVerticalFieldOfView = 75.0f * M_PI / 180.0f; + +void UpdateViewFromEyeParameters( + XRView* view, + const device::mojom::blink::VREyeParametersPtr& eye, + double depth_near, + double depth_far) { + const device::mojom::blink::VRFieldOfViewPtr& fov = eye->fieldOfView; + + view->UpdateProjectionMatrixFromFoV( + fov->upDegrees * kDegToRad, fov->downDegrees * kDegToRad, + fov->leftDegrees * kDegToRad, fov->rightDegrees * kDegToRad, depth_near, + depth_far); + + view->UpdateOffset(eye->offset[0], eye->offset[1], eye->offset[2]); +} + +} // namespace + +class XRSession::XRSessionResizeObserverDelegate final + : public ResizeObserver::Delegate { + public: + explicit XRSessionResizeObserverDelegate(XRSession* session) + : session_(session) { + DCHECK(session); + } + ~XRSessionResizeObserverDelegate() override = default; + + void OnResize( + const HeapVector<Member<ResizeObserverEntry>>& entries) override { + DCHECK_EQ(1u, entries.size()); + session_->UpdateCanvasDimensions(entries[0]->target()); + } + + void Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + ResizeObserver::Delegate::Trace(visitor); + } + + private: + Member<XRSession> session_; +}; + +XRSession::XRSession(XRDevice* device, + bool exclusive, + XRPresentationContext* output_context) + : device_(device), + exclusive_(exclusive), + output_context_(output_context), + callback_collection_(device->GetExecutionContext()) { + blurred_ = !HasAppropriateFocus(); + + // When an output context is provided, monitor it for resize events. + if (output_context_) { + HTMLCanvasElement* canvas = outputContext()->canvas(); + if (canvas) { + resize_observer_ = ResizeObserver::Create( + canvas->GetDocument(), new XRSessionResizeObserverDelegate(this)); + resize_observer_->observe(canvas); + + // Begin processing input events on the output context's canvas. + if (!exclusive_) { + canvas_input_provider_ = new XRCanvasInputProvider(this, canvas); + } + + // Get the initial canvas dimensions + UpdateCanvasDimensions(canvas); + } + } +} + +void XRSession::setDepthNear(double value) { + if (depth_near_ != value) { + update_views_next_frame_ = true; + depth_near_ = value; + } +} + +void XRSession::setDepthFar(double value) { + if (depth_far_ != value) { + update_views_next_frame_ = true; + depth_far_ = value; + } +} + +void XRSession::setBaseLayer(XRLayer* value) { + base_layer_ = value; + // Make sure that the layer's drawing buffer is updated to the right size + // if this is a non-exclusive session. + if (!exclusive_ && base_layer_) { + base_layer_->OnResize(); + } +} + +ExecutionContext* XRSession::GetExecutionContext() const { + return device_->GetExecutionContext(); +} + +const AtomicString& XRSession::InterfaceName() const { + return EventTargetNames::XRSession; +} + +ScriptPromise XRSession::requestFrameOfReference( + ScriptState* script_state, + const String& type, + const XRFrameOfReferenceOptions& options) { + if (ended_) { + return ScriptPromise::RejectWithDOMException( + script_state, DOMException::Create(kInvalidStateError, kSessionEnded)); + } + + XRFrameOfReference* frameOfRef = nullptr; + if (type == "headModel") { + frameOfRef = + new XRFrameOfReference(this, XRFrameOfReference::kTypeHeadModel); + } else if (type == "eyeLevel") { + frameOfRef = + new XRFrameOfReference(this, XRFrameOfReference::kTypeEyeLevel); + } else if (type == "stage") { + if (!options.disableStageEmulation()) { + frameOfRef = new XRFrameOfReference(this, XRFrameOfReference::kTypeStage); + frameOfRef->UseEmulatedHeight(options.stageEmulationHeight()); + } else if (device_->xrDisplayInfoPtr()->stageParameters) { + frameOfRef = new XRFrameOfReference(this, XRFrameOfReference::kTypeStage); + } else { + return ScriptPromise::RejectWithDOMException( + script_state, DOMException::Create(kNotSupportedError, + kNonEmulatedStageNotSupported)); + } + } + + if (!frameOfRef) { + return ScriptPromise::RejectWithDOMException( + script_state, + DOMException::Create(kNotSupportedError, kUnknownFrameOfReference)); + } + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); + ScriptPromise promise = resolver->Promise(); + resolver->Resolve(frameOfRef); + + return promise; +} + +int XRSession::requestAnimationFrame(V8XRFrameRequestCallback* callback) { + // Don't allow any new frame requests once the session is ended. + if (ended_) + return 0; + + // Don't allow frames to be scheduled if there's no layers attached to the + // session. That would allow tracking with no associated visuals. + if (!base_layer_) + return 0; + + int id = callback_collection_.RegisterCallback(callback); + if (!pending_frame_) { + // Kick off a request for a new XR frame. + device_->frameProvider()->RequestFrame(this); + pending_frame_ = true; + } + return id; +} + +void XRSession::cancelAnimationFrame(int id) { + callback_collection_.CancelCallback(id); +} + +HeapVector<Member<XRInputSource>> XRSession::getInputSources() const { + HeapVector<Member<XRInputSource>> source_array; + for (const auto& input_source : input_sources_.Values()) { + source_array.push_back(input_source); + } + + if (canvas_input_provider_) { + XRInputSource* input_source = canvas_input_provider_->GetInputSource(); + if (input_source) { + source_array.push_back(input_source); + } + } + + return source_array; +} + +ScriptPromise XRSession::end(ScriptState* script_state) { + // Don't allow a session to end twice. + if (ended_) { + return ScriptPromise::RejectWithDOMException( + script_state, DOMException::Create(kInvalidStateError, kSessionEnded)); + } + + ForceEnd(); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); + ScriptPromise promise = resolver->Promise(); + + // TODO(bajones): If there's any work that needs to be done asynchronously on + // session end it should be completed before this promise is resolved. + + resolver->Resolve(); + return promise; +} + +void XRSession::ForceEnd() { + // Detach this session from the device. + ended_ = true; + pending_frame_ = false; + + if (canvas_input_provider_) { + canvas_input_provider_->Stop(); + canvas_input_provider_ = nullptr; + } + + // If this session is the active exclusive session for the device, notify the + // frameProvider that it's ended. + if (device_->frameProvider()->exclusive_session() == this) { + device_->frameProvider()->OnExclusiveSessionEnded(); + } + + DispatchEvent(XRSessionEvent::Create(EventTypeNames::end, this)); +} + +double XRSession::DefaultFramebufferScale() const { + if (exclusive_) + return device_->xrDisplayInfoPtr()->webxr_default_framebuffer_scale; + return 1.0; +} + +DoubleSize XRSession::IdealFramebufferSize() const { + if (!exclusive_) { + return OutputCanvasSize(); + } + + double width = device_->xrDisplayInfoPtr()->leftEye->renderWidth + + device_->xrDisplayInfoPtr()->rightEye->renderWidth; + double height = std::max(device_->xrDisplayInfoPtr()->leftEye->renderHeight, + device_->xrDisplayInfoPtr()->rightEye->renderHeight); + return DoubleSize(width, height); +} + +DoubleSize XRSession::OutputCanvasSize() const { + if (!output_context_) { + return DoubleSize(); + } + + return DoubleSize(output_width_, output_height_); +} + +void XRSession::OnFocus() { + if (!blurred_) + return; + + blurred_ = false; + DispatchEvent(XRSessionEvent::Create(EventTypeNames::focus, this)); +} + +void XRSession::OnBlur() { + if (blurred_) + return; + + blurred_ = true; + 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( + std::unique_ptr<TransformationMatrix> base_pose_matrix, + const base::Optional<gpu::MailboxHolder>& buffer_mailbox_holder) { + DVLOG(2) << __FUNCTION__; + // Don't process any outstanding frames once the session is ended. + if (ended_) + return; + + base_pose_matrix_ = std::move(base_pose_matrix); + + // Don't allow frames to be processed if there's no layers attached to the + // session. That would allow tracking with no associated visuals. + if (!base_layer_) + return; + + XRPresentationFrame* presentation_frame = CreatePresentationFrame(); + + if (pending_frame_) { + pending_frame_ = false; + + // Make sure that any frame-bounded changed to the views array take effect. + if (update_views_next_frame_) { + views_dirty_ = true; + update_views_next_frame_ = false; + } + + // Cache the base layer, since it could change during the frame callback. + XRLayer* frame_base_layer = base_layer_; + frame_base_layer->OnFrameStart(buffer_mailbox_holder); + + // Resolve the queued requestAnimationFrame callbacks. All XR rendering will + // happen within these calls. resolving_frame_ will be true for the duration + // of the callbacks. + AutoReset<bool> resolving(&resolving_frame_, true); + callback_collection_.ExecuteCallbacks(this, presentation_frame); + + // The session might have ended in the middle of the frame. Only call + // OnFrameEnd if it's still valid. + if (!ended_) + frame_base_layer->OnFrameEnd(); + } +} + +XRPresentationFrame* XRSession::CreatePresentationFrame() { + XRPresentationFrame* presentation_frame = new XRPresentationFrame(this); + if (base_pose_matrix_) { + presentation_frame->SetBasePoseMatrix(*base_pose_matrix_); + } + return presentation_frame; +} + +// Called when the canvas element for this session's output context is resized. +void XRSession::UpdateCanvasDimensions(Element* element) { + DCHECK(element); + + double devicePixelRatio = 1.0; + LocalFrame* frame = device_->xr()->GetFrame(); + if (frame) { + devicePixelRatio = frame->DevicePixelRatio(); + } + + update_views_next_frame_ = true; + output_width_ = element->OffsetWidth() * devicePixelRatio; + output_height_ = element->OffsetHeight() * devicePixelRatio; + + if (base_layer_) { + base_layer_->OnResize(); + } +} + +void XRSession::OnInputStateChange( + int16_t frame_id, + const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>& + input_states) { + bool devices_changed = false; + + // Update any input sources with new state information. Any updated input + // sources are marked as active. + for (const auto& input_state : input_states) { + XRInputSource* input_source = input_sources_.at(input_state->source_id); + if (!input_source) { + input_source = new XRInputSource(this, input_state->source_id); + input_sources_.Set(input_state->source_id, input_source); + devices_changed = true; + } + input_source->active_frame_id = frame_id; + UpdateInputSourceState(input_source, input_state); + } + + // Remove any input sources that are inactive.. + std::vector<uint32_t> inactive_sources; + for (const auto& input_source : input_sources_.Values()) { + if (input_source->active_frame_id != frame_id) { + inactive_sources.push_back(input_source->source_id()); + devices_changed = true; + } + } + + if (inactive_sources.size()) { + for (uint32_t source_id : inactive_sources) { + input_sources_.erase(source_id); + } + } + + if (devices_changed) { + DispatchEvent( + XRSessionEvent::Create(EventTypeNames::inputsourceschange, this)); + } +} + +void XRSession::OnSelectStart(XRInputSource* input_source) { + // Discard duplicate events + if (input_source->primary_input_pressed) + return; + + input_source->primary_input_pressed = true; + input_source->selection_cancelled = false; + + XRInputSourceEvent* event = + CreateInputSourceEvent(EventTypeNames::selectstart, input_source); + DispatchEvent(event); + + if (event->defaultPrevented()) + input_source->selection_cancelled = true; +} + +void XRSession::OnSelectEnd(XRInputSource* input_source) { + // Discard duplicate events + if (!input_source->primary_input_pressed) + return; + + input_source->primary_input_pressed = false; + + LocalFrame* frame = device_->xr()->GetFrame(); + if (!frame) + return; + + std::unique_ptr<UserGestureIndicator> gesture_indicator = + LocalFrame::CreateUserGesture(frame); + + XRInputSourceEvent* event = + CreateInputSourceEvent(EventTypeNames::selectend, input_source); + DispatchEvent(event); + + if (event->defaultPrevented()) + input_source->selection_cancelled = true; +} + +void XRSession::OnSelect(XRInputSource* input_source) { + // If a select was fired but we had not previously started the selection it + // indictes a sub-frame or instantanous select event, and we should fire a + // selectstart prior to the selectend. + if (!input_source->primary_input_pressed) { + OnSelectStart(input_source); + } + + // Make sure we end the selection prior to firing the select event. + OnSelectEnd(input_source); + + if (!input_source->selection_cancelled) { + XRInputSourceEvent* event = + CreateInputSourceEvent(EventTypeNames::select, input_source); + DispatchEvent(event); + } +} + +void XRSession::UpdateInputSourceState( + XRInputSource* input_source, + const device::mojom::blink::XRInputSourceStatePtr& state) { + if (!input_source || !state) + return; + + // Update the input source's description if this state update + // includes them. + if (state->description) { + const device::mojom::blink::XRInputSourceDescriptionPtr& desc = + state->description; + + input_source->SetPointerOrigin( + static_cast<XRInputSource::PointerOrigin>(desc->pointer_origin)); + + input_source->SetHandedness( + static_cast<XRInputSource::Handedness>(desc->handedness)); + + input_source->SetEmulatedPosition(desc->emulated_position); + + if (desc->pointer_offset && desc->pointer_offset->matrix.has_value()) { + const WTF::Vector<float>& m = desc->pointer_offset->matrix.value(); + std::unique_ptr<TransformationMatrix> pointer_matrix = + TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6], + m[7], m[8], m[9], m[10], m[11], m[12], + m[13], m[14], m[15]); + input_source->SetPointerTransformMatrix(std::move(pointer_matrix)); + } + } + + if (state->grip && state->grip->matrix.has_value()) { + const Vector<float>& m = state->grip->matrix.value(); + std::unique_ptr<TransformationMatrix> grip_matrix = + TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6], + m[7], m[8], m[9], m[10], m[11], m[12], + m[13], m[14], m[15]); + input_source->SetBasePoseMatrix(std::move(grip_matrix)); + } + + // Handle state change of the primary input, which may fire events + if (state->primary_input_clicked) + OnSelect(input_source); + + if (state->primary_input_pressed) { + OnSelectStart(input_source); + } else if (input_source->primary_input_pressed) { + // May get here if the input source was previously pressed but now isn't, + // but the input source did not set primary_input_clicked to true. We will + // treat this as a cancelled selection, firing the selectend event so the + // page stays in sync with the controller state but won't fire the + // usual select event. + OnSelectEnd(input_source); + } +} + +XRInputSourceEvent* XRSession::CreateInputSourceEvent( + const AtomicString& type, + XRInputSource* input_source) { + XRPresentationFrame* presentation_frame = CreatePresentationFrame(); + return XRInputSourceEvent::Create(type, presentation_frame, input_source); +} + +const HeapVector<Member<XRView>>& XRSession::views() { + // TODO(bajones): For now we assume that exclusive sessions render a stereo + // pair of views and non-exclusive sessions render a single view. That doesn't + // always hold true, however, so the view configuration should ultimately come + // from the backing service. + if (views_dirty_) { + if (exclusive_) { + // If we don't already have the views allocated, do so now. + if (views_.IsEmpty()) { + views_.push_back(new XRView(this, XRView::kEyeLeft)); + views_.push_back(new XRView(this, XRView::kEyeRight)); + } + + // In exclusive mode the projection and view matrices must be aligned with + // the device's physical optics. + UpdateViewFromEyeParameters(views_[XRView::kEyeLeft], + device_->xrDisplayInfoPtr()->leftEye, + depth_near_, depth_far_); + UpdateViewFromEyeParameters(views_[XRView::kEyeRight], + device_->xrDisplayInfoPtr()->rightEye, + depth_near_, depth_far_); + } else { + if (views_.IsEmpty()) { + views_.push_back(new XRView(this, XRView::kEyeLeft)); + views_[XRView::kEyeLeft]->UpdateOffset(0, 0, 0); + } + + float aspect = 1.0f; + if (output_width_ && output_height_) { + aspect = static_cast<float>(output_width_) / + static_cast<float>(output_height_); + } + + // In non-exclusive mode the projection matrix must be aligned with the + // output canvas dimensions. + views_[XRView::kEyeLeft]->UpdateProjectionMatrixFromAspect( + kMagicWindowVerticalFieldOfView, aspect, depth_near_, depth_far_); + } + + views_dirty_ = false; + } + + return views_; +} + +void XRSession::Trace(blink::Visitor* visitor) { + visitor->Trace(device_); + visitor->Trace(output_context_); + visitor->Trace(base_layer_); + visitor->Trace(views_); + visitor->Trace(input_sources_); + visitor->Trace(resize_observer_); + visitor->Trace(canvas_input_provider_); + visitor->Trace(callback_collection_); + EventTargetWithInlineData::Trace(visitor); +} + +void XRSession::TraceWrappers( + const blink::ScriptWrappableVisitor* visitor) const { + for (const auto& input_source : input_sources_.Values()) + visitor->TraceWrappers(input_source); + + visitor->TraceWrappers(callback_collection_); + EventTargetWithInlineData::TraceWrappers(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session.h b/chromium/third_party/blink/renderer/modules/xr/xr_session.h new file mode 100644 index 00000000000..d2e45423ccb --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session.h @@ -0,0 +1,162 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_H_ + +#include "device/vr/public/mojom/vr_service.mojom-blink.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/core/dom/events/event_target.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_request_callback_collection.h" +#include "third_party/blink/renderer/modules/xr/xr_input_source.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" +#include "third_party/blink/renderer/platform/geometry/double_size.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class Element; +class ResizeObserver; +class V8XRFrameRequestCallback; +class XRCanvasInputProvider; +class XRDevice; +class XRFrameOfReferenceOptions; +class XRInputSourceEvent; +class XRLayer; +class XRPresentationContext; +class XRView; + +class XRSession final : public EventTargetWithInlineData { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRSession(XRDevice*, bool exclusive, XRPresentationContext* output_context); + ~XRSession() override = default; + + XRDevice* device() const { return device_; } + bool exclusive() const { return exclusive_; } + XRPresentationContext* outputContext() const { return output_context_; } + + // Near and far depths are used when computing projection matrices for this + // Session's views. Changes will propegate to the appropriate matrices on the + // next frame after these values are updated. + double depthNear() const { return depth_near_; } + void setDepthNear(double value); + double depthFar() const { return depth_far_; } + void setDepthFar(double value); + + XRLayer* baseLayer() const { return base_layer_; } + void setBaseLayer(XRLayer* value); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(blur); + DEFINE_ATTRIBUTE_EVENT_LISTENER(focus); + DEFINE_ATTRIBUTE_EVENT_LISTENER(resetpose); + DEFINE_ATTRIBUTE_EVENT_LISTENER(end); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(selectend); + DEFINE_ATTRIBUTE_EVENT_LISTENER(select); + + ScriptPromise requestFrameOfReference(ScriptState*, + const String& type, + const XRFrameOfReferenceOptions&); + + int requestAnimationFrame(V8XRFrameRequestCallback*); + void cancelAnimationFrame(int id); + + using InputSourceMap = + HeapHashMap<uint32_t, TraceWrapperMember<XRInputSource>>; + + HeapVector<Member<XRInputSource>> getInputSources() const; + + // Called by JavaScript to manually end the session. + ScriptPromise end(ScriptState*); + + bool ended() { return ended_; } + + // Called when the session is ended, either via calling the "end" function or + // when the presentation service connection is closed. + void ForceEnd(); + + // Describes the default scalar to be applied to the ideal framebuffer + // dimensions when the developer does not specify one. Should be a value that + // provides a good balance between quality and performance. + double DefaultFramebufferScale() const; + + // Describes the ideal dimensions of layer framebuffers, preferrably defined + // as the size which gives 1:1 pixel ratio at the center of the user's view. + DoubleSize IdealFramebufferSize() const; + + // Reports the size of the output context's, if one is available. If not + // reports (0, 0); + DoubleSize OutputCanvasSize() const; + + // EventTarget overrides. + ExecutionContext* GetExecutionContext() const override; + const AtomicString& InterfaceName() const override; + + void OnFocusChanged(); + void OnFrame(std::unique_ptr<TransformationMatrix>, + const base::Optional<gpu::MailboxHolder>&); + void OnInputStateChange( + int16_t frame_id, + const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&); + + const HeapVector<Member<XRView>>& views(); + + void OnSelectStart(XRInputSource*); + void OnSelectEnd(XRInputSource*); + void OnSelect(XRInputSource*); + + void Trace(blink::Visitor*) override; + virtual void TraceWrappers(const blink::ScriptWrappableVisitor*) const; + + private: + class XRSessionResizeObserverDelegate; + + XRPresentationFrame* CreatePresentationFrame(); + void UpdateCanvasDimensions(Element*); + + void UpdateInputSourceState( + XRInputSource*, + const device::mojom::blink::XRInputSourceStatePtr&); + XRInputSourceEvent* CreateInputSourceEvent(const AtomicString&, + XRInputSource*); + + void OnFocus(); + void OnBlur(); + bool HasAppropriateFocus(); + + const Member<XRDevice> device_; + const bool exclusive_; + const Member<XRPresentationContext> output_context_; + Member<XRLayer> base_layer_; + HeapVector<Member<XRView>> views_; + InputSourceMap input_sources_; + Member<ResizeObserver> resize_observer_; + Member<XRCanvasInputProvider> canvas_input_provider_; + + XRFrameRequestCallbackCollection callback_collection_; + std::unique_ptr<TransformationMatrix> base_pose_matrix_; + + double depth_near_ = 0.1; + double depth_far_ = 1000.0; + bool blurred_; + bool ended_ = false; + bool pending_frame_ = false; + bool resolving_frame_ = false; + bool update_views_next_frame_ = false; + bool views_dirty_ = true; + + // Dimensions of the output canvas. + int output_width_ = 1; + int output_height_ = 1; +}; + +} // namespace blink + +#endif // XRWebGLLayer_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session.idl b/chromium/third_party/blink/renderer/modules/xr/xr_session.idl new file mode 100644 index 00000000000..7a07c8efe00 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session.idl @@ -0,0 +1,32 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrsession-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRSession : EventTarget { + readonly attribute XRDevice device; + readonly attribute boolean exclusive; + readonly attribute XRPresentationContext outputContext; + + attribute double depthNear; + attribute double depthFar; + + attribute XRLayer baseLayer; + + attribute EventHandler onblur; + attribute EventHandler onfocus; + attribute EventHandler onresetpose; + attribute EventHandler onend; + + [CallWith=ScriptState] Promise<XRFrameOfReference> requestFrameOfReference(XRFrameOfReferenceType type, [PermissiveDictionaryConversion] optional XRFrameOfReferenceOptions options); + + long requestAnimationFrame(XRFrameRequestCallback callback); + void cancelAnimationFrame(long handle); + + [MeasureAs=XRSessionGetInputSources] FrozenArray<XRInputSource> getInputSources(); + + [CallWith=ScriptState] Promise<void> end(); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session_creation_options.idl b/chromium/third_party/blink/renderer/modules/xr/xr_session_creation_options.idl new file mode 100644 index 00000000000..960e60d3ea2 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session_creation_options.idl @@ -0,0 +1,11 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrsessioncreationoptions-interface +[ + SecureContext +] dictionary XRSessionCreationOptions { + boolean exclusive = false; + XRPresentationContext outputContext; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session_event.cc b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.cc new file mode 100644 index 00000000000..bb7fdbd862e --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.cc @@ -0,0 +1,32 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_session_event.h" + +namespace blink { + +XRSessionEvent::XRSessionEvent() = default; + +XRSessionEvent::XRSessionEvent(const AtomicString& type, XRSession* session) + : Event(type, Bubbles::kNo, Cancelable::kYes), session_(session) {} + +XRSessionEvent::XRSessionEvent(const AtomicString& type, + const XRSessionEventInit& initializer) + : Event(type, initializer) { + if (initializer.hasSession()) + session_ = initializer.session(); +} + +XRSessionEvent::~XRSessionEvent() = default; + +const AtomicString& XRSessionEvent::InterfaceName() const { + return EventNames::XRSessionEvent; +} + +void XRSessionEvent::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + Event::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session_event.h b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.h new file mode 100644 index 00000000000..fa99848c3e4 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.h @@ -0,0 +1,46 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_EVENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SESSION_EVENT_H_ + +#include "third_party/blink/renderer/modules/event_modules.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_session_event_init.h" + +namespace blink { + +class XRSessionEvent final : public Event { + DEFINE_WRAPPERTYPEINFO(); + + public: + static XRSessionEvent* Create() { return new XRSessionEvent; } + static XRSessionEvent* Create(const AtomicString& type, XRSession* session) { + return new XRSessionEvent(type, session); + } + + static XRSessionEvent* Create(const AtomicString& type, + const XRSessionEventInit& initializer) { + return new XRSessionEvent(type, initializer); + } + + ~XRSessionEvent() override; + + XRSession* session() const { return session_.Get(); } + + const AtomicString& InterfaceName() const override; + + void Trace(blink::Visitor*) override; + + private: + XRSessionEvent(); + XRSessionEvent(const AtomicString& type, XRSession*); + XRSessionEvent(const AtomicString& type, const XRSessionEventInit&); + + Member<XRSession> session_; +}; + +} // namespace blink + +#endif // XRDisplayEvent_h diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session_event.idl b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.idl new file mode 100644 index 00000000000..aa6b1d9e4a1 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session_event.idl @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrsessionevent-interface +[ + SecureContext, + OriginTrialEnabled=WebXR, + Constructor(DOMString type, XRSessionEventInit eventInitDict) +] interface XRSessionEvent : Event { + readonly attribute XRSession session; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_session_event_init.idl b/chromium/third_party/blink/renderer/modules/xr/xr_session_event_init.idl new file mode 100644 index 00000000000..cb2d268f844 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_session_event_init.idl @@ -0,0 +1,10 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrsessionevent-interface +[ + SecureContext +] dictionary XRSessionEventInit : EventInit { + required XRSession session; +};
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.cc b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.cc new file mode 100644 index 00000000000..a85149eb32b --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.cc @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_stage_bounds.h" + +namespace blink { + +void XRStageBounds::Trace(blink::Visitor* visitor) { + visitor->Trace(geometry_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.h b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.h new file mode 100644 index 00000000000..0250f810766 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.h @@ -0,0 +1,31 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_H_ + +#include "third_party/blink/renderer/modules/xr/xr_stage_bounds_point.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRStageBounds final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRStageBounds() = default; + + HeapVector<Member<XRStageBoundsPoint>> geometry() const { return geometry_; } + + virtual void Trace(blink::Visitor*); + + private: + HeapVector<Member<XRStageBoundsPoint>> geometry_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.idl b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.idl new file mode 100644 index 00000000000..e924eea3ffa --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds.idl @@ -0,0 +1,11 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrstagebounds-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRStageBounds { + readonly attribute FrozenArray<XRStageBoundsPoint> geometry; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.h b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.h new file mode 100644 index 00000000000..a29055b11b4 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.h @@ -0,0 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_POINT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_POINT_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRStageBoundsPoint final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRStageBoundsPoint(double x, double z) : x_(x), z_(z) {} + + double x() const { return x_; } + double z() const { return z_; } + + private: + double x_; + double z_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_STAGE_BOUNDS_POINT_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.idl b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.idl new file mode 100644 index 00000000000..96c169e3f88 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_stage_bounds_point.idl @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrstageboundspoint-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRStageBoundsPoint { + readonly attribute double x; + readonly attribute double z; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_utils.cc b/chromium/third_party/blink/renderer/modules/xr/xr_utils.cc new file mode 100644 index 00000000000..34ef2243b7d --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_utils.cc @@ -0,0 +1,24 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_utils.h" + +namespace blink { + +DOMFloat32Array* transformationMatrixToFloat32Array( + const TransformationMatrix& matrix) { + float array[] = { + static_cast<float>(matrix.M11()), static_cast<float>(matrix.M12()), + static_cast<float>(matrix.M13()), static_cast<float>(matrix.M14()), + static_cast<float>(matrix.M21()), static_cast<float>(matrix.M22()), + static_cast<float>(matrix.M23()), static_cast<float>(matrix.M24()), + static_cast<float>(matrix.M31()), static_cast<float>(matrix.M32()), + static_cast<float>(matrix.M33()), static_cast<float>(matrix.M34()), + static_cast<float>(matrix.M41()), static_cast<float>(matrix.M42()), + static_cast<float>(matrix.M43()), static_cast<float>(matrix.M44())}; + + return DOMFloat32Array::Create(array, 16); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_utils.h b/chromium/third_party/blink/renderer/modules/xr/xr_utils.h new file mode 100644 index 00000000000..d484e5bc914 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_utils.h @@ -0,0 +1,18 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_UTILS_H_ + +#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" + +namespace blink { + +DOMFloat32Array* transformationMatrixToFloat32Array( + const TransformationMatrix&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_view.cc b/chromium/third_party/blink/renderer/modules/xr/xr_view.cc new file mode 100644 index 00000000000..6880f24d07f --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_view.cc @@ -0,0 +1,151 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_view.h" + +#include "third_party/blink/renderer/modules/xr/xr_presentation_frame.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/platform/geometry/float_point_3d.h" + +namespace blink { + +XRView::XRView(XRSession* session, Eye eye) + : eye_(eye), + session_(session), + projection_matrix_(DOMFloat32Array::Create(16)) { + eye_string_ = (eye_ == kEyeLeft ? "left" : "right"); +} + +XRSession* XRView::session() const { + return session_; +} + +void XRView::UpdateProjectionMatrixFromFoV(float up_rad, + float down_rad, + float left_rad, + float right_rad, + float near_depth, + float far_depth) { + float up_tan = tanf(up_rad); + float down_tan = tanf(down_rad); + float left_tan = tanf(left_rad); + float right_tan = tanf(right_rad); + float x_scale = 2.0f / (left_tan + right_tan); + float y_scale = 2.0f / (up_tan + down_tan); + float inv_nf = 1.0f / (near_depth - far_depth); + + float* out = projection_matrix_->Data(); + out[0] = x_scale; + out[1] = 0.0f; + out[2] = 0.0f; + out[3] = 0.0f; + out[4] = 0.0f; + out[5] = y_scale; + out[6] = 0.0f; + out[7] = 0.0f; + out[8] = -((left_tan - right_tan) * x_scale * 0.5); + out[9] = ((up_tan - down_tan) * y_scale * 0.5); + out[10] = (near_depth + far_depth) * inv_nf; + out[11] = -1.0f; + out[12] = 0.0f; + out[13] = 0.0f; + out[14] = (2.0f * far_depth * near_depth) * inv_nf; + out[15] = 0.0f; + + inv_projection_dirty_ = true; +} + +void XRView::UpdateProjectionMatrixFromAspect(float fovy, + float aspect, + float near_depth, + float far_depth) { + float f = 1.0f / tanf(fovy / 2); + float inv_nf = 1.0f / (near_depth - far_depth); + + float* out = projection_matrix_->Data(); + out[0] = f / aspect; + out[1] = 0.0f; + out[2] = 0.0f; + out[3] = 0.0f; + out[4] = 0.0f; + out[5] = f; + out[6] = 0.0f; + out[7] = 0.0f; + out[8] = 0.0f; + out[9] = 0.0f; + out[10] = (far_depth + near_depth) * inv_nf; + out[11] = -1.0f; + out[12] = 0.0f; + out[13] = 0.0f; + out[14] = (2.0f * far_depth * near_depth) * inv_nf; + out[15] = 0.0f; + + inv_projection_dirty_ = true; +} + +void XRView::UpdateOffset(float x, float y, float z) { + offset_.Set(x, y, z); +} + +std::unique_ptr<TransformationMatrix> XRView::UnprojectPointer( + double x, + double y, + double canvas_width, + double canvas_height) { + // Recompute the inverse projection matrix if needed. + if (inv_projection_dirty_) { + float* m = projection_matrix_->Data(); + std::unique_ptr<TransformationMatrix> projection = + TransformationMatrix::Create(m[0], m[1], m[2], m[3], m[4], m[5], m[6], + m[7], m[8], m[9], m[10], m[11], m[12], + m[13], m[14], m[15]); + inv_projection_ = TransformationMatrix::Create(projection->Inverse()); + inv_projection_dirty_ = false; + } + + // Transform the x/y coordinate into WebGL normalized device coordinates. + // Z coordinate of -1 means the point will be projected onto the projection + // matrix near plane. + FloatPoint3D point_in_projection_space( + x / canvas_width * 2.0 - 1.0, + (canvas_height - y) / canvas_height * 2.0 - 1.0, -1.0); + + FloatPoint3D point_in_view_space = + inv_projection_->MapPoint(point_in_projection_space); + + const FloatPoint3D kOrigin(0.0, 0.0, 0.0); + const FloatPoint3D kUp(0.0, 1.0, 0.0); + + // Generate a "Look At" matrix + FloatPoint3D z_axis = kOrigin - point_in_view_space; + z_axis.Normalize(); + + FloatPoint3D x_axis = kUp.Cross(z_axis); + x_axis.Normalize(); + + FloatPoint3D y_axis = z_axis.Cross(x_axis); + y_axis.Normalize(); + + // TODO(bajones): There's probably a more efficent way to do this? + TransformationMatrix inv_pointer(x_axis.X(), y_axis.X(), z_axis.X(), 0.0, + x_axis.Y(), y_axis.Y(), z_axis.Y(), 0.0, + x_axis.Z(), y_axis.Z(), z_axis.Z(), 0.0, 0.0, + 0.0, 0.0, 1.0); + inv_pointer.Translate3d(-point_in_view_space.X(), -point_in_view_space.Y(), + -point_in_view_space.Z()); + + // LookAt matrices are view matrices (inverted), so invert before returning. + std::unique_ptr<TransformationMatrix> pointer = + TransformationMatrix::Create(inv_pointer.Inverse()); + + return pointer; +} + +void XRView::Trace(blink::Visitor* visitor) { + visitor->Trace(session_); + visitor->Trace(projection_matrix_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_view.h b/chromium/third_party/blink/renderer/modules/xr/xr_view.h new file mode 100644 index 00000000000..93b51598d62 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_view.h @@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEW_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEW_H_ + +#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/geometry/float_point_3d.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class XRSession; + +class XRView final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + enum Eye { kEyeLeft = 0, kEyeRight = 1 }; + + XRView(XRSession*, Eye); + + const String& eye() const { return eye_string_; } + Eye EyeValue() const { return eye_; } + + XRSession* session() const; + DOMFloat32Array* projectionMatrix() const { return projection_matrix_; } + + void UpdateProjectionMatrixFromFoV(float up_rad, + float down_rad, + float left_rad, + float right_rad, + float near_depth, + float far_depth); + + void UpdateProjectionMatrixFromAspect(float fovy, + float aspect, + float near_depth, + float far_depth); + + std::unique_ptr<TransformationMatrix> UnprojectPointer(double x, + double y, + double canvas_width, + double canvas_height); + + // TODO(bajones): Should eventually represent this as a full transform. + const FloatPoint3D& offset() const { return offset_; } + void UpdateOffset(float x, float y, float z); + + virtual void Trace(blink::Visitor*); + + private: + const Eye eye_; + String eye_string_; + Member<XRSession> session_; + Member<DOMFloat32Array> projection_matrix_; + FloatPoint3D offset_; + std::unique_ptr<TransformationMatrix> inv_projection_; + bool inv_projection_dirty_ = true; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEW_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_view.idl b/chromium/third_party/blink/renderer/modules/xr/xr_view.idl new file mode 100644 index 00000000000..6a5f325c6b6 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_view.idl @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrview-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRView { + readonly attribute VREye? eye; + readonly attribute Float32Array projectionMatrix; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_viewport.h b/chromium/third_party/blink/renderer/modules/xr/xr_viewport.h new file mode 100644 index 00000000000..aa5ff93041b --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_viewport.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEWPORT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEWPORT_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class XRViewport final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + XRViewport(int x, int y, int width, int height) + : x_(x), y_(y), width_(width), height_(height) {} + + int x() const { return x_; } + int y() const { return y_; } + int width() const { return width_; } + int height() const { return height_; } + + private: + int x_; + int y_; + int width_; + int height_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_VIEWPORT_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_viewport.idl b/chromium/third_party/blink/renderer/modules/xr/xr_viewport.idl new file mode 100644 index 00000000000..6ce2abdae15 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_viewport.idl @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrviewport-interface +[ + SecureContext, + OriginTrialEnabled=WebXR +] interface XRViewport { + readonly attribute long x; + readonly attribute long y; + readonly attribute long width; + readonly attribute long height; +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc new file mode 100644 index 00000000000..84b1201ccc2 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc @@ -0,0 +1,335 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h" + +#include "third_party/blink/renderer/bindings/core/v8/exception_messages.h" +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h" +#include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context.h" +#include "third_party/blink/renderer/modules/webgl/webgl_framebuffer.h" +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context.h" +#include "third_party/blink/renderer/modules/xr/xr_device.h" +#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" +#include "third_party/blink/renderer/modules/xr/xr_presentation_context.h" +#include "third_party/blink/renderer/modules/xr/xr_session.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" +#include "third_party/blink/renderer/modules/xr/xr_viewport.h" +#include "third_party/blink/renderer/platform/geometry/double_size.h" +#include "third_party/blink/renderer/platform/geometry/float_point.h" +#include "third_party/blink/renderer/platform/geometry/int_size.h" + +namespace blink { + +namespace { + +const double kFramebufferMinScale = 0.2; +const double kFramebufferMaxScale = 1.0; + +const double kViewportMinScale = 0.2; +const double kViewportMaxScale = 1.0; + +// Because including base::ClampToRange would be a dependency violation +double ClampToRange(const double value, const double min, const double max) { + return std::min(std::max(value, min), max); +} + +} // namespace + +XRWebGLLayer* XRWebGLLayer::Create( + XRSession* session, + const WebGLRenderingContextOrWebGL2RenderingContext& context, + const XRWebGLLayerInit& initializer, + ExceptionState& exception_state) { + if (session->ended()) { + exception_state.ThrowDOMException(kInvalidStateError, + "Cannot create an XRWebGLLayer for an " + "XRSession which has already ended."); + return nullptr; + } + + WebGLRenderingContextBase* webgl_context; + if (context.IsWebGL2RenderingContext()) { + webgl_context = context.GetAsWebGL2RenderingContext(); + } else { + webgl_context = context.GetAsWebGLRenderingContext(); + } + + if (webgl_context->isContextLost()) { + exception_state.ThrowDOMException(kInvalidStateError, + "Cannot create an XRWebGLLayer with a " + "lost WebGL context."); + return nullptr; + } + + if (!webgl_context->IsXRDeviceCompatible(session->device())) { + exception_state.ThrowDOMException( + kInvalidStateError, + "The session's device is not the compatible device for this context."); + return nullptr; + } + + bool want_antialiasing = initializer.antialias(); + bool want_depth_buffer = initializer.depth(); + bool want_stencil_buffer = initializer.stencil(); + bool want_alpha_channel = initializer.alpha(); + bool want_multiview = initializer.multiview(); + + double framebuffer_scale = session->DefaultFramebufferScale(); + + if (initializer.hasFramebufferScaleFactor() && + initializer.framebufferScaleFactor() != 0.0) { + // Clamp the developer-requested framebuffer size to ensure it's not too + // small to see or unreasonably large. + framebuffer_scale = + ClampToRange(initializer.framebufferScaleFactor(), kFramebufferMinScale, + kFramebufferMaxScale); + } + + DoubleSize framebuffers_size = session->IdealFramebufferSize(); + + IntSize desired_size(framebuffers_size.Width() * framebuffer_scale, + framebuffers_size.Height() * framebuffer_scale); + + // Create an opaque WebGL Framebuffer + WebGLFramebuffer* framebuffer = WebGLFramebuffer::CreateOpaque(webgl_context); + + scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer = + XRWebGLDrawingBuffer::Create( + webgl_context->GetDrawingBuffer(), framebuffer->Object(), + desired_size, want_alpha_channel, want_depth_buffer, + want_stencil_buffer, want_antialiasing, want_multiview); + + if (!drawing_buffer) { + exception_state.ThrowDOMException(kOperationError, + "Unable to create a framebuffer."); + return nullptr; + } + + return new XRWebGLLayer(session, webgl_context, std::move(drawing_buffer), + framebuffer, framebuffer_scale); +} + +XRWebGLLayer::XRWebGLLayer(XRSession* session, + WebGLRenderingContextBase* webgl_context, + scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer, + WebGLFramebuffer* framebuffer, + double framebuffer_scale) + : XRLayer(session, kXRWebGLLayerType), + webgl_context_(webgl_context), + drawing_buffer_(drawing_buffer), + framebuffer_(framebuffer), + framebuffer_scale_(framebuffer_scale) { + DCHECK(drawing_buffer); + // If the contents need mirroring, indicate that to the drawing buffer. + if (session->exclusive() && session->outputContext() && + session->device()->external()) { + mirroring_ = true; + drawing_buffer_->SetMirrorClient(this); + } + UpdateViewports(); +} + +XRWebGLLayer::~XRWebGLLayer() { + if (mirroring_) + drawing_buffer_->SetMirrorClient(nullptr); +} + +void XRWebGLLayer::getXRWebGLRenderingContext( + WebGLRenderingContextOrWebGL2RenderingContext& result) const { + if (webgl_context_->Version() == 2) { + result.SetWebGL2RenderingContext( + static_cast<WebGL2RenderingContext*>(webgl_context_.Get())); + } else { + result.SetWebGLRenderingContext( + static_cast<WebGLRenderingContext*>(webgl_context_.Get())); + } +} + +XRViewport* XRWebGLLayer::getViewport(XRView* view) { + if (!view || view->session() != session()) + return nullptr; + + return GetViewportForEye(view->EyeValue()); +} + +XRViewport* XRWebGLLayer::GetViewportForEye(XRView::Eye eye) { + if (viewports_dirty_) + UpdateViewports(); + + if (eye == XRView::kEyeLeft) + return left_viewport_; + + return right_viewport_; +} + +void XRWebGLLayer::requestViewportScaling(double scale_factor) { + if (!session()->exclusive()) { + // TODO(bajones): For the moment we're just going to ignore viewport changes + // in non-exclusive mode. This is legal, but probably not what developers + // would like to see. Look into making viewport scale apply properly. + scale_factor = 1.0; + } else { + // Clamp the developer-requested viewport size to ensure it's not too + // small to see or larger than the framebuffer. + scale_factor = + ClampToRange(scale_factor, kViewportMinScale, kViewportMaxScale); + } + + // Don't set this as the viewport_scale_ directly, since that would allow the + // viewports to change mid-frame. + requested_viewport_scale_ = scale_factor; +} + +void XRWebGLLayer::UpdateViewports() { + long framebuffer_width = framebufferWidth(); + long framebuffer_height = framebufferHeight(); + + viewports_dirty_ = false; + + if (session()->exclusive()) { + left_viewport_ = + new XRViewport(0, 0, framebuffer_width * 0.5 * viewport_scale_, + framebuffer_height * viewport_scale_); + right_viewport_ = + new XRViewport(framebuffer_width * 0.5 * viewport_scale_, 0, + framebuffer_width * 0.5 * viewport_scale_, + framebuffer_height * viewport_scale_); + + session()->device()->frameProvider()->UpdateWebGLLayerViewports(this); + + // When mirroring make sure to also update the mirrored canvas UVs so it + // only shows a single eye's data, cropped to display proportionally. + if (session()->outputContext()) { + float left = 0; + float top = 0; + float right = static_cast<float>(left_viewport_->width()) / + static_cast<float>(framebuffer_width); + float bottom = static_cast<float>(left_viewport_->height()) / + static_cast<float>(framebuffer_height); + + // Adjust the UVs so that the mirrored content always fills the canvas + // and is centered while staying proportional. + DoubleSize output_size = session()->OutputCanvasSize(); + double output_aspect = output_size.Width() / output_size.Height(); + double viewport_aspect = static_cast<float>(left_viewport_->width()) / + static_cast<float>(left_viewport_->height()); + + if (output_aspect > viewport_aspect) { + float viewport_scale = bottom; + output_aspect = viewport_aspect / output_aspect; + top = 0.5 - (output_aspect * 0.5); + bottom = top + output_aspect; + top *= viewport_scale; + bottom *= viewport_scale; + } else { + float viewport_scale = right; + output_aspect = output_aspect / viewport_aspect; + left = 0.5 - (output_aspect * 0.5); + right = left + output_aspect; + left *= viewport_scale; + right *= viewport_scale; + } + + session()->outputContext()->SetUV(FloatPoint(left, top), + FloatPoint(right, bottom)); + } + } else { + left_viewport_ = new XRViewport(0, 0, framebuffer_width * viewport_scale_, + framebuffer_height * viewport_scale_); + } +} + +void XRWebGLLayer::OnFrameStart( + const base::Optional<gpu::MailboxHolder>& buffer_mailbox_holder) { + // If the requested scale has changed since the last from, update it now. + if (viewport_scale_ != requested_viewport_scale_) { + viewport_scale_ = requested_viewport_scale_; + viewports_dirty_ = true; + } + + framebuffer_->MarkOpaqueBufferComplete(true); + framebuffer_->SetContentsChanged(false); + if (buffer_mailbox_holder) { + drawing_buffer_->UseSharedBuffer(buffer_mailbox_holder.value()); + is_direct_draw_frame = true; + } else { + is_direct_draw_frame = false; + } +} + +void XRWebGLLayer::OnFrameEnd() { + framebuffer_->MarkOpaqueBufferComplete(false); + if (is_direct_draw_frame) { + drawing_buffer_->DoneWithSharedBuffer(); + is_direct_draw_frame = false; + } + + // Submit the frame to the XR compositor. + if (session()->exclusive()) { + // Always call submit, but notify if the contents were changed or not. + session()->device()->frameProvider()->SubmitWebGLLayer( + this, framebuffer_->HaveContentsChanged()); + } else if (session()->outputContext()) { + // Nothing to do if the framebuffer contents have not changed. + if (framebuffer_->HaveContentsChanged()) { + ImageBitmap* image_bitmap = + ImageBitmap::Create(TransferToStaticBitmapImage(nullptr)); + session()->outputContext()->SetImage(image_bitmap); + } + } +} + +void XRWebGLLayer::OnResize() { + if (!session()->exclusive()) { + // For non-exclusive sessions a resize indicates we should adjust the + // drawing buffer size to match the canvas. + DoubleSize framebuffers_size = session()->IdealFramebufferSize(); + + IntSize desired_size(framebuffers_size.Width() * framebuffer_scale_, + framebuffers_size.Height() * framebuffer_scale_); + drawing_buffer_->Resize(desired_size); + } + + // With both exclusive and non-exclusive session the viewports should be + // recomputed when the output canvas resizes. + viewports_dirty_ = true; +} + +scoped_refptr<StaticBitmapImage> XRWebGLLayer::TransferToStaticBitmapImage( + std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { + return drawing_buffer_->TransferToStaticBitmapImage(out_release_callback); +} + +void XRWebGLLayer::OnMirrorImageAvailable( + scoped_refptr<StaticBitmapImage> image, + std::unique_ptr<viz::SingleReleaseCallback> release_callback) { + ImageBitmap* image_bitmap = ImageBitmap::Create(std::move(image)); + + session()->outputContext()->SetImage(image_bitmap); + + if (mirror_release_callback_) { + // TODO(bajones): We should probably have the compositor report to us when + // it's done with the image, rather than reporting back that it's usable as + // soon as we receive a new one. + mirror_release_callback_->Run(gpu::SyncToken(), false /* lost_resource */); + } + + mirror_release_callback_ = std::move(release_callback); +} + +void XRWebGLLayer::Trace(blink::Visitor* visitor) { + visitor->Trace(left_viewport_); + visitor->Trace(right_viewport_); + visitor->Trace(webgl_context_); + visitor->Trace(framebuffer_); + XRLayer::Trace(visitor); +} + +void XRWebGLLayer::TraceWrappers(const ScriptWrappableVisitor* visitor) const { + visitor->TraceWrappers(webgl_context_); + XRLayer::TraceWrappers(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.h new file mode 100644 index 00000000000..862cdbb7981 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.h @@ -0,0 +1,108 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_LAYER_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_LAYER_H_ + +#include "third_party/blink/renderer/bindings/modules/v8/webgl_rendering_context_or_webgl2_rendering_context.h" +#include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context.h" +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context.h" +#include "third_party/blink/renderer/modules/xr/xr_layer.h" +#include "third_party/blink/renderer/modules/xr/xr_view.h" +#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_init.h" +#include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" + +namespace viz { +class SingleReleaseCallback; +} + +namespace blink { + +class ExceptionState; +class WebGLFramebuffer; +class WebGLRenderingContextBase; +class XRSession; +class XRViewport; + +class XRWebGLLayer final : public XRLayer, + public XRWebGLDrawingBuffer::MirrorClient { + DEFINE_WRAPPERTYPEINFO(); + + public: + virtual ~XRWebGLLayer(); + + static XRWebGLLayer* Create( + XRSession*, + const WebGLRenderingContextOrWebGL2RenderingContext&, + const XRWebGLLayerInit&, + ExceptionState&); + + WebGLRenderingContextBase* context() const { return webgl_context_; } + void getXRWebGLRenderingContext( + WebGLRenderingContextOrWebGL2RenderingContext&) const; + + WebGLFramebuffer* framebuffer() const { return framebuffer_; } + unsigned long framebufferWidth() const { + return drawing_buffer_->size().Width(); + } + unsigned long framebufferHeight() const { + return drawing_buffer_->size().Height(); + } + + bool antialias() const { return drawing_buffer_->antialias(); } + bool depth() const { return drawing_buffer_->depth(); } + bool stencil() const { return drawing_buffer_->stencil(); } + bool alpha() const { return drawing_buffer_->alpha(); } + bool multiview() const { return drawing_buffer_->multiview(); } + + XRViewport* getViewport(XRView*); + void requestViewportScaling(double scale_factor); + + XRViewport* GetViewportForEye(XRView::Eye); + + void UpdateViewports(); + + void OnFrameStart(const base::Optional<gpu::MailboxHolder>&) override; + void OnFrameEnd() override; + void OnResize() override; + + scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage( + std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback); + + // XRWebGLDrawingBuffer::MirrorClient impementation + void OnMirrorImageAvailable( + scoped_refptr<StaticBitmapImage>, + std::unique_ptr<viz::SingleReleaseCallback>) override; + + virtual void Trace(blink::Visitor*); + virtual void TraceWrappers(const ScriptWrappableVisitor*) const; + + private: + XRWebGLLayer(XRSession*, + WebGLRenderingContextBase*, + scoped_refptr<XRWebGLDrawingBuffer>, + WebGLFramebuffer*, + double framebuffer_scale); + + Member<XRViewport> left_viewport_; + Member<XRViewport> right_viewport_; + + TraceWrapperMember<WebGLRenderingContextBase> webgl_context_; + scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer_; + Member<WebGLFramebuffer> framebuffer_; + + std::unique_ptr<viz::SingleReleaseCallback> mirror_release_callback_; + + double framebuffer_scale_ = 1.0; + double requested_viewport_scale_ = 1.0; + double viewport_scale_ = 1.0; + bool viewports_dirty_ = true; + bool mirroring_ = false; + bool is_direct_draw_frame = false; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_LAYER_H_ diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl new file mode 100644 index 00000000000..18a9ef9576a --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +typedef (WebGLRenderingContext or WebGL2RenderingContext) XRWebGLRenderingContext; + +// https://immersive-web.github.io/webxr/spec/latest/#xrwebgllayer-interface +[ + SecureContext, + OriginTrialEnabled=WebXR, + Constructor(XRSession session, XRWebGLRenderingContext context, optional XRWebGLLayerInit layerInit), + RaisesException=Constructor +] interface XRWebGLLayer : XRLayer { + [ImplementedAs=getXRWebGLRenderingContext] readonly attribute XRWebGLRenderingContext context; + readonly attribute boolean antialias; + readonly attribute boolean depth; + readonly attribute boolean stencil; + readonly attribute boolean alpha; + readonly attribute boolean multiview; + + readonly attribute unsigned long framebufferWidth; + readonly attribute unsigned long framebufferHeight; + readonly attribute WebGLFramebuffer framebuffer; + + XRViewport? getViewport(XRView view); + void requestViewportScaling(double viewportScaleFactor); +}; diff --git a/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer_init.idl b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer_init.idl new file mode 100644 index 00000000000..0071cc51571 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/xr/xr_webgl_layer_init.idl @@ -0,0 +1,15 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://immersive-web.github.io/webxr/spec/latest/#xrwebgllayerinit-dictionary +[ + SecureContext +] dictionary XRWebGLLayerInit { + boolean antialias = true; + boolean depth = true; + boolean stencil = false; + boolean alpha = true; + boolean multiview = false; + double framebufferScaleFactor; +}; |