1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "device/vr/openxr/openxr_device.h"
6 
7 #include <string>
8 
9 #include "base/callback_helpers.h"
10 #include "build/build_config.h"
11 #include "device/vr/openxr/openxr_api_wrapper.h"
12 #include "device/vr/openxr/openxr_render_loop.h"
13 #include "device/vr/openxr/openxr_statics.h"
14 #include "device/vr/util/transform_utils.h"
15 #include "mojo/public/cpp/bindings/pending_remote.h"
16 
17 namespace device {
18 
19 namespace {
20 
21 constexpr float kFov = 45.0f;
22 
23 constexpr unsigned int kRenderWidth = 1024;
24 constexpr unsigned int kRenderHeight = 1024;
25 
26 // OpenXR doesn't give out display info until you start a session.
27 // However our mojo interface expects display info right away to support WebVR.
28 // We create a fake display info to use, then notify the client that the display
29 // info changed when we get real data.
CreateFakeVRDisplayInfo()30 mojom::VRDisplayInfoPtr CreateFakeVRDisplayInfo() {
31   mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
32 
33   display_info->left_eye = mojom::VREyeParameters::New();
34   display_info->right_eye = mojom::VREyeParameters::New();
35 
36   display_info->left_eye->field_of_view =
37       mojom::VRFieldOfView::New(kFov, kFov, kFov, kFov);
38   display_info->right_eye->field_of_view =
39       display_info->left_eye->field_of_view.Clone();
40 
41   display_info->left_eye->head_from_eye =
42       vr_utils::DefaultHeadFromLeftEyeTransform();
43   display_info->right_eye->head_from_eye =
44       vr_utils::DefaultHeadFromRightEyeTransform();
45 
46   display_info->left_eye->render_width = kRenderWidth;
47   display_info->left_eye->render_height = kRenderHeight;
48   display_info->right_eye->render_width = kRenderWidth;
49   display_info->right_eye->render_height = kRenderHeight;
50 
51   return display_info;
52 }
53 
54 }  // namespace
55 
56 // OpenXrDevice must not take ownership of the OpenXrStatics passed in.
57 // The OpenXrStatics object is owned by IsolatedXRRuntimeProvider.
OpenXrDevice(OpenXrStatics * openxr_statics)58 OpenXrDevice::OpenXrDevice(OpenXrStatics* openxr_statics)
59     : VRDeviceBase(device::mojom::XRDeviceId::OPENXR_DEVICE_ID),
60       instance_(openxr_statics->GetXrInstance()),
61       extension_helper_(instance_, openxr_statics->GetExtensionEnumeration()),
62       weak_ptr_factory_(this) {
63   mojom::VRDisplayInfoPtr display_info = CreateFakeVRDisplayInfo();
64   SetVRDisplayInfo(std::move(display_info));
65   SetArBlendModeSupported(IsArBlendModeSupported(openxr_statics));
66 #if defined(OS_WIN)
67   SetLuid(openxr_statics->GetLuid(extension_helper_));
68 #endif
69 }
70 
~OpenXrDevice()71 OpenXrDevice::~OpenXrDevice() {
72   // Wait for the render loop to stop before completing destruction. This will
73   // ensure that the render loop doesn't get shutdown while it is processing
74   // any requests.
75   if (render_loop_ && render_loop_->IsRunning()) {
76     render_loop_->Stop();
77   }
78 }
79 
80 mojo::PendingRemote<mojom::XRCompositorHost>
BindCompositorHost()81 OpenXrDevice::BindCompositorHost() {
82   return compositor_host_receiver_.BindNewPipeAndPassRemote();
83 }
84 
EnsureRenderLoop()85 void OpenXrDevice::EnsureRenderLoop() {
86   if (!render_loop_) {
87     auto on_info_changed = base::BindRepeating(&OpenXrDevice::SetVRDisplayInfo,
88                                                weak_ptr_factory_.GetWeakPtr());
89     render_loop_ = std::make_unique<OpenXrRenderLoop>(
90         std::move(on_info_changed), instance_, extension_helper_);
91   }
92 }
93 
RequestSession(mojom::XRRuntimeSessionOptionsPtr options,mojom::XRRuntime::RequestSessionCallback callback)94 void OpenXrDevice::RequestSession(
95     mojom::XRRuntimeSessionOptionsPtr options,
96     mojom::XRRuntime::RequestSessionCallback callback) {
97   EnsureRenderLoop();
98 
99   if (!render_loop_->IsRunning()) {
100     render_loop_->Start();
101 
102     if (!render_loop_->IsRunning()) {
103       std::move(callback).Run(nullptr, mojo::NullRemote());
104       return;
105     }
106 
107     if (overlay_receiver_) {
108       render_loop_->task_runner()->PostTask(
109           FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
110                                     base::Unretained(render_loop_.get()),
111                                     std::move(overlay_receiver_)));
112     }
113   }
114 
115   auto my_callback =
116       base::BindOnce(&OpenXrDevice::OnRequestSessionResult,
117                      weak_ptr_factory_.GetWeakPtr(), std::move(callback));
118 
119   auto on_visibility_state_changed = base::BindRepeating(
120       &OpenXrDevice::OnVisibilityStateChanged, weak_ptr_factory_.GetWeakPtr());
121 
122   // OpenXr doesn't need to handle anything when presentation has ended, but
123   // the mojo interface to call to XRCompositorCommon::RequestSession requires
124   // a method and cannot take nullptr, so passing in base::DoNothing::Once()
125   // for on_presentation_ended
126   render_loop_->task_runner()->PostTask(
127       FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession,
128                                 base::Unretained(render_loop_.get()),
129                                 base::DoNothing::Once(),
130                                 std::move(on_visibility_state_changed),
131                                 std::move(options), std::move(my_callback)));
132 }
133 
OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,bool result,mojom::XRSessionPtr session)134 void OpenXrDevice::OnRequestSessionResult(
135     mojom::XRRuntime::RequestSessionCallback callback,
136     bool result,
137     mojom::XRSessionPtr session) {
138   if (!result) {
139     std::move(callback).Run(nullptr, mojo::NullRemote());
140     return;
141   }
142 
143   OnStartPresenting();
144 
145   session->display_info = display_info_.Clone();
146 
147   std::move(callback).Run(
148       std::move(session),
149       exclusive_controller_receiver_.BindNewPipeAndPassRemote());
150 
151   // Use of Unretained is safe because the callback will only occur if the
152   // binding is not destroyed.
153   exclusive_controller_receiver_.set_disconnect_handler(
154       base::BindOnce(&OpenXrDevice::OnPresentingControllerMojoConnectionError,
155                      base::Unretained(this)));
156 }
157 
OnPresentingControllerMojoConnectionError()158 void OpenXrDevice::OnPresentingControllerMojoConnectionError() {
159   // This method is called when the rendering process exit presents.
160 
161   if (render_loop_) {
162     render_loop_->task_runner()->PostTask(
163         FROM_HERE, base::BindOnce(&XRCompositorCommon::ExitPresent,
164                                   base::Unretained(render_loop_.get())));
165   }
166   OnExitPresent();
167   exclusive_controller_receiver_.reset();
168 }
169 
SetFrameDataRestricted(bool restricted)170 void OpenXrDevice::SetFrameDataRestricted(bool restricted) {
171   // Presentation sessions can not currently be restricted.
172   NOTREACHED();
173 }
174 
CreateImmersiveOverlay(mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver)175 void OpenXrDevice::CreateImmersiveOverlay(
176     mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) {
177   EnsureRenderLoop();
178   if (render_loop_->IsRunning()) {
179     render_loop_->task_runner()->PostTask(
180         FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
181                                   base::Unretained(render_loop_.get()),
182                                   std::move(overlay_receiver)));
183   } else {
184     overlay_receiver_ = std::move(overlay_receiver);
185   }
186 }
187 
IsArBlendModeSupported(OpenXrStatics * openxr_statics)188 bool OpenXrDevice::IsArBlendModeSupported(OpenXrStatics* openxr_statics) {
189   XrSystemId system;
190   if (XR_FAILED(GetSystem(openxr_statics->GetXrInstance(), &system)))
191     return false;
192 
193   std::vector<XrEnvironmentBlendMode> environment_blend_modes =
194       GetSupportedBlendModes(openxr_statics->GetXrInstance(), system);
195 
196   return base::Contains(environment_blend_modes,
197                         XR_ENVIRONMENT_BLEND_MODE_ADDITIVE) ||
198          base::Contains(environment_blend_modes,
199                         XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND);
200 }
201 
202 }  // namespace device
203