blob: 7bc59d7e89792e8c178698107e5c04ea2b201186 [file]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/openxr_device.h"
#include <string>
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "device/base/features.h"
#include "device/vr/openxr/openxr_api_wrapper.h"
#include "device/vr/openxr/openxr_render_loop.h"
#include "device/vr/openxr/openxr_statics.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
namespace device {
namespace {
const std::vector<mojom::XRSessionFeature>& GetSupportedFeatures() {
static base::NoDestructor<std::vector<mojom::XRSessionFeature>>
kSupportedFeatures{{
mojom::XRSessionFeature::REF_SPACE_VIEWER,
mojom::XRSessionFeature::REF_SPACE_LOCAL,
mojom::XRSessionFeature::REF_SPACE_LOCAL_FLOOR,
mojom::XRSessionFeature::REF_SPACE_BOUNDED_FLOOR,
mojom::XRSessionFeature::REF_SPACE_UNBOUNDED,
mojom::XRSessionFeature::ANCHORS,
}};
return *kSupportedFeatures;
}
} // namespace
// OpenXrDevice must not take ownership of the OpenXrStatics passed in.
// The OpenXrStatics object is owned by IsolatedXRRuntimeProvider.
OpenXrDevice::OpenXrDevice(
VizContextProviderFactoryAsync context_provider_factory_async)
: VRDeviceBase(device::mojom::XRDeviceId::OPENXR_DEVICE_ID),
instance_(OpenXrStatics::GetInstance()->GetXrInstance()),
extension_helper_(
instance_,
OpenXrStatics::GetInstance()->GetExtensionEnumeration()),
context_provider_factory_async_(
std::move(context_provider_factory_async)),
weak_ptr_factory_(this) {
SetArBlendModeSupported(IsArBlendModeSupported());
#if BUILDFLAG(IS_WIN)
SetLuid(OpenXrStatics::GetInstance()->GetLuid(extension_helper_));
#endif
std::vector<mojom::XRSessionFeature> device_features(
GetSupportedFeatures());
// Only support hand input if the feature flag is enabled.
if (base::FeatureList::IsEnabled(features::kWebXrHandInput))
device_features.emplace_back(mojom::XRSessionFeature::HAND_INPUT);
// Only support hit test if the feature flag is enabled.
if (base::FeatureList::IsEnabled(features::kWebXrHitTest) &&
base::FeatureList::IsEnabled(
features::kOpenXrExtendedFeatureSupport))
device_features.emplace_back(mojom::XRSessionFeature::HIT_TEST);
if (extension_helper_.ExtensionEnumeration()->ExtensionSupported(
XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME)) {
device_features.emplace_back(mojom::XRSessionFeature::SECONDARY_VIEWS);
}
SetSupportedFeatures(device_features);
}
OpenXrDevice::~OpenXrDevice() {
// Wait for the render loop to stop before completing destruction. This will
// ensure that the render loop doesn't get shutdown while it is processing
// any requests.
if (render_loop_ && render_loop_->IsRunning()) {
render_loop_->Stop();
}
// request_session_callback_ may still be active if we're tearing down the
// OpenXrDevice while we're still making asynchronous calls to setup the GPU
// process connection. Ensure the callback is run regardless.
if (request_session_callback_) {
std::move(request_session_callback_).Run(nullptr);
}
}
mojo::PendingRemote<mojom::XRCompositorHost>
OpenXrDevice::BindCompositorHost() {
return compositor_host_receiver_.BindNewPipeAndPassRemote();
}
void OpenXrDevice::EnsureRenderLoop() {
if (!render_loop_) {
render_loop_ = std::make_unique<OpenXrRenderLoop>(
context_provider_factory_async_, instance_, extension_helper_);
}
}
void OpenXrDevice::RequestSession(
mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
DCHECK(!request_session_callback_);
// Check feature support and reject session request if we cannot fulfil it
// TODO(https://crbug.com/995377): Currently OpenXR features are declared
// statically, but we may only know a runtime's true support for a feature
// dynamically
const bool anchors_required = base::Contains(
options->required_features, device::mojom::XRSessionFeature::ANCHORS);
const bool anchors_supported =
extension_helper_.ExtensionEnumeration()->ExtensionSupported(
XR_MSFT_SPATIAL_ANCHOR_EXTENSION_NAME);
const bool hand_input_required = base::Contains(
options->required_features, device::mojom::XRSessionFeature::HAND_INPUT);
const bool hand_input_supported =
extension_helper_.ExtensionEnumeration()->ExtensionSupported(
kMSFTHandInteractionExtensionName);
const bool hittest_required = base::Contains(
options->required_features, device::mojom::XRSessionFeature::HIT_TEST);
const bool hittest_supported =
extension_helper_.ExtensionEnumeration()->ExtensionSupported(
XR_MSFT_SCENE_UNDERSTANDING_EXTENSION_NAME);
if ((anchors_required && !anchors_supported) ||
(hand_input_required && !hand_input_supported) ||
(hittest_required && !hittest_supported)) {
// Reject session request
std::move(callback).Run(nullptr);
return;
}
EnsureRenderLoop();
if (!render_loop_->IsRunning()) {
render_loop_->Start();
if (!render_loop_->IsRunning()) {
std::move(callback).Run(nullptr);
return;
}
if (overlay_receiver_) {
render_loop_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
base::Unretained(render_loop_.get()),
std::move(overlay_receiver_)));
}
}
auto my_callback = base::BindOnce(&OpenXrDevice::OnRequestSessionResult,
weak_ptr_factory_.GetWeakPtr());
auto on_visibility_state_changed = base::BindRepeating(
&OpenXrDevice::OnVisibilityStateChanged, weak_ptr_factory_.GetWeakPtr());
// OpenXr doesn't need to handle anything when presentation has ended, but
// the mojo interface to call to XRCompositorCommon::RequestSession requires
// a method and cannot take nullptr, so passing in base::DoNothing()
// for on_presentation_ended
render_loop_->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&XRCompositorCommon::RequestSession,
base::Unretained(render_loop_.get()), base::DoNothing(),
std::move(on_visibility_state_changed), std::move(options),
std::move(my_callback)));
request_session_callback_ = std::move(callback);
}
void OpenXrDevice::OnRequestSessionResult(
bool result,
mojom::XRSessionPtr session) {
DCHECK(request_session_callback_);
if (!result) {
std::move(request_session_callback_).Run(nullptr);
return;
}
OnStartPresenting();
auto session_result = mojom::XRRuntimeSessionResult::New();
session_result->session = std::move(session);
session_result->controller =
exclusive_controller_receiver_.BindNewPipeAndPassRemote();
std::move(request_session_callback_).Run(std::move(session_result));
// Use of Unretained is safe because the callback will only occur if the
// binding is not destroyed.
exclusive_controller_receiver_.set_disconnect_handler(
base::BindOnce(&OpenXrDevice::OnPresentingControllerMojoConnectionError,
base::Unretained(this)));
}
void OpenXrDevice::OnPresentingControllerMojoConnectionError() {
// This method is called when the rendering process exit presents.
if (render_loop_) {
render_loop_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&XRCompositorCommon::ExitPresent,
base::Unretained(render_loop_.get()),
ExitXrPresentReason::kMojoConnectionError));
}
OnExitPresent();
exclusive_controller_receiver_.reset();
}
void OpenXrDevice::SetFrameDataRestricted(bool restricted) {
// Presentation sessions can not currently be restricted.
NOTREACHED();
}
void OpenXrDevice::CreateImmersiveOverlay(
mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) {
EnsureRenderLoop();
if (render_loop_->IsRunning()) {
render_loop_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay,
base::Unretained(render_loop_.get()),
std::move(overlay_receiver)));
} else {
overlay_receiver_ = std::move(overlay_receiver);
}
}
bool OpenXrDevice::IsArBlendModeSupported() {
XrSystemId system;
if (XR_FAILED(
GetSystem(OpenXrStatics::GetInstance()->GetXrInstance(), &system)))
return false;
std::vector<XrEnvironmentBlendMode> environment_blend_modes =
GetSupportedBlendModes(OpenXrStatics::GetInstance()->GetXrInstance(),
system);
return base::Contains(environment_blend_modes,
XR_ENVIRONMENT_BLEND_MODE_ADDITIVE) ||
base::Contains(environment_blend_modes,
XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND);
}
} // namespace device