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/bind_helpers.h"
10 #include "device/vr/openxr/openxr_api_wrapper.h"
11 #include "device/vr/openxr/openxr_render_loop.h"
12 #include "device/vr/util/transform_utils.h"
13 #include "mojo/public/cpp/bindings/pending_remote.h"
14 
15 namespace device {
16 
17 namespace {
18 
19 constexpr float kFov = 45.0f;
20 
21 constexpr unsigned int kRenderWidth = 1024;
22 constexpr unsigned int kRenderHeight = 1024;
23 
24 // OpenXR doesn't give out display info until you start a session.
25 // However our mojo interface expects display info right away to support WebVR.
26 // We create a fake display info to use, then notify the client that the display
27 // info changed when we get real data.
CreateFakeVRDisplayInfo(device::mojom::XRDeviceId id)28 mojom::VRDisplayInfoPtr CreateFakeVRDisplayInfo(device::mojom::XRDeviceId id) {
29   mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
30 
31   display_info->id = id;
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 
OpenXrDevice()56 OpenXrDevice::OpenXrDevice()
57     : VRDeviceBase(device::mojom::XRDeviceId::OPENXR_DEVICE_ID),
58       weak_ptr_factory_(this) {
59   SetVRDisplayInfo(CreateFakeVRDisplayInfo(GetId()));
60 }
61 
~OpenXrDevice()62 OpenXrDevice::~OpenXrDevice() {
63   // Wait for the render loop to stop before completing destruction. This will
64   // ensure that the render loop doesn't get shutdown while it is processing
65   // any requests.
66   if (render_loop_ && render_loop_->IsRunning()) {
67     render_loop_->Stop();
68   }
69 }
70 
71 mojo::PendingRemote<mojom::XRCompositorHost>
BindCompositorHost()72 OpenXrDevice::BindCompositorHost() {
73   return compositor_host_receiver_.BindNewPipeAndPassRemote();
74 }
75 
EnsureRenderLoop()76 void OpenXrDevice::EnsureRenderLoop() {
77   if (!render_loop_) {
78     auto on_info_changed = base::BindRepeating(&OpenXrDevice::SetVRDisplayInfo,
79                                                weak_ptr_factory_.GetWeakPtr());
80     render_loop_ =
81         std::make_unique<OpenXrRenderLoop>(std::move(on_info_changed));
82   }
83 }
84 
RequestSession(mojom::XRRuntimeSessionOptionsPtr options,mojom::XRRuntime::RequestSessionCallback callback)85 void OpenXrDevice::RequestSession(
86     mojom::XRRuntimeSessionOptionsPtr options,
87     mojom::XRRuntime::RequestSessionCallback callback) {
88   DCHECK_EQ(options->mode, mojom::XRSessionMode::kImmersiveVr);
89   EnsureRenderLoop();
90 
91   if (!render_loop_->IsRunning()) {
92     render_loop_->Start();
93 
94     if (!render_loop_->IsRunning()) {
95       std::move(callback).Run(nullptr, mojo::NullRemote());
96       return;
97     }
98 
99     if (overlay_receiver_) {
100       render_loop_->task_runner()->PostTask(
101           FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
102                                     base::Unretained(render_loop_.get()),
103                                     std::move(overlay_receiver_)));
104     }
105   }
106 
107   auto my_callback =
108       base::BindOnce(&OpenXrDevice::OnRequestSessionResult,
109                      weak_ptr_factory_.GetWeakPtr(), std::move(callback));
110 
111   auto on_visibility_state_changed = base::BindRepeating(
112       &OpenXrDevice::OnVisibilityStateChanged, weak_ptr_factory_.GetWeakPtr());
113 
114   // OpenXr doesn't need to handle anything when presentation has ended, but
115   // the mojo interface to call to XRCompositorCommon::RequestSession requires
116   // a method and cannot take nullptr, so passing in base::DoNothing::Once()
117   // for on_presentation_ended
118   render_loop_->task_runner()->PostTask(
119       FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession,
120                                 base::Unretained(render_loop_.get()),
121                                 base::DoNothing::Once(),
122                                 std::move(on_visibility_state_changed),
123                                 std::move(options), std::move(my_callback)));
124 }
125 
OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,bool result,mojom::XRSessionPtr session)126 void OpenXrDevice::OnRequestSessionResult(
127     mojom::XRRuntime::RequestSessionCallback callback,
128     bool result,
129     mojom::XRSessionPtr session) {
130   if (!result) {
131     std::move(callback).Run(nullptr, mojo::NullRemote());
132     return;
133   }
134 
135   OnStartPresenting();
136 
137   session->display_info = display_info_.Clone();
138 
139   std::move(callback).Run(
140       std::move(session),
141       exclusive_controller_receiver_.BindNewPipeAndPassRemote());
142 
143   // Use of Unretained is safe because the callback will only occur if the
144   // binding is not destroyed.
145   exclusive_controller_receiver_.set_disconnect_handler(
146       base::BindOnce(&OpenXrDevice::OnPresentingControllerMojoConnectionError,
147                      base::Unretained(this)));
148 }
149 
OnPresentingControllerMojoConnectionError()150 void OpenXrDevice::OnPresentingControllerMojoConnectionError() {
151   // This method is called when the rendering process exit presents.
152 
153   if (render_loop_) {
154     render_loop_->task_runner()->PostTask(
155         FROM_HERE, base::BindOnce(&XRCompositorCommon::ExitPresent,
156                                   base::Unretained(render_loop_.get())));
157   }
158   OnExitPresent();
159   exclusive_controller_receiver_.reset();
160 }
161 
SetFrameDataRestricted(bool restricted)162 void OpenXrDevice::SetFrameDataRestricted(bool restricted) {
163   // Presentation sessions can not currently be restricted.
164   NOTREACHED();
165 }
166 
CreateImmersiveOverlay(mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver)167 void OpenXrDevice::CreateImmersiveOverlay(
168     mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) {
169   EnsureRenderLoop();
170   if (render_loop_->IsRunning()) {
171     render_loop_->task_runner()->PostTask(
172         FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
173                                   base::Unretained(render_loop_.get()),
174                                   std::move(overlay_receiver)));
175   } else {
176     overlay_receiver_ = std::move(overlay_receiver);
177   }
178 }
179 
180 }  // namespace device
181