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