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