blob: aa6ea5506e87465a7cd8d83c9a119779bd3ef87c [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/isolated_xr_device/xr_runtime_provider.h"
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
#include "chrome/common/chrome_features.h"
#include "device/vr/buildflags/buildflags.h"
#include "device/vr/vr_device_base.h"
#if BUILDFLAG(ENABLE_OPENVR)
#include "device/vr/openvr/openvr_device.h"
#endif
#if BUILDFLAG(ENABLE_OCULUS_VR)
#include "device/vr/oculus/oculus_device.h"
#endif
#if BUILDFLAG(ENABLE_WINDOWS_MR)
#include "device/vr/windows_mixed_reality/mixed_reality_device.h"
#include "device/vr/windows_mixed_reality/mixed_reality_statics.h"
#endif
#if BUILDFLAG(ENABLE_OPENXR)
#include "device/vr/openxr/openxr_device.h"
#endif
enum class IsolatedXRRuntimeProvider::RuntimeStatus {
kEnable,
kDisable,
};
namespace {
// Poll for device add/remove every 5 seconds.
constexpr base::TimeDelta kTimeBetweenPollingEvents =
base::TimeDelta::FromSecondsD(5);
template <typename VrDeviceT>
std::unique_ptr<VrDeviceT> EnableRuntime(
device::mojom::IsolatedXRRuntimeProviderClientPtr& client) {
auto device = std::make_unique<VrDeviceT>();
TRACE_EVENT_INSTANT1("xr", "HardwareAdded", TRACE_EVENT_SCOPE_THREAD, "id",
static_cast<int>(device->GetId()));
// "Device" here refers to a runtime + hardware pair, not necessarily
// a physical device.
client->OnDeviceAdded(device->BindXRRuntimePtr(),
device->BindGamepadFactory(),
device->BindCompositorHost(), device->GetId());
return device;
}
template <typename VrDeviceT>
void DisableRuntime(device::mojom::IsolatedXRRuntimeProviderClientPtr& client,
std::unique_ptr<VrDeviceT> device) {
TRACE_EVENT_INSTANT1("xr", "HardwareRemoved", TRACE_EVENT_SCOPE_THREAD, "id",
static_cast<int>(device->GetId()));
// "Device" here refers to a runtime + hardware pair, not necessarily physical
// device.
client->OnDeviceRemoved(device->GetId());
}
template <typename VrHardwareT>
void SetRuntimeStatus(device::mojom::IsolatedXRRuntimeProviderClientPtr& client,
IsolatedXRRuntimeProvider::RuntimeStatus status,
std::unique_ptr<VrHardwareT>* out_device) {
if (status == IsolatedXRRuntimeProvider::RuntimeStatus::kEnable &&
!*out_device) {
*out_device = EnableRuntime<VrHardwareT>(client);
} else if (status == IsolatedXRRuntimeProvider::RuntimeStatus::kDisable &&
*out_device) {
DisableRuntime(client, std::move(*out_device));
}
}
} // namespace
// This function is called periodically to check the availability of hardware
// backed by the various supported VR runtimes. Only one "device" (hardware +
// runtime) should be enabled at once, so this chooses the most preferred among
// available options.
void IsolatedXRRuntimeProvider::PollForDeviceChanges() {
bool preferred_device_enabled = false;
// If none of the following runtimes are enabled,
// we'll get an error for 'preferred_device_enabled' being unused.
// Cast it to void (nop) here to mitigate that error.
(void)preferred_device_enabled;
#if BUILDFLAG(ENABLE_OPENXR)
if (!preferred_device_enabled && IsOpenXrHardwareAvailable()) {
SetOpenXrRuntimeStatus(RuntimeStatus::kEnable);
preferred_device_enabled = true;
} else {
SetOpenXrRuntimeStatus(RuntimeStatus::kDisable);
}
#endif
#if BUILDFLAG(ENABLE_WINDOWS_MR)
if (!preferred_device_enabled && IsWMRHardwareAvailable()) {
SetWMRRuntimeStatus(RuntimeStatus::kEnable);
preferred_device_enabled = true;
} else {
SetWMRRuntimeStatus(RuntimeStatus::kDisable);
}
#endif
#if BUILDFLAG(ENABLE_OCULUS_VR)
if (!preferred_device_enabled && IsOculusVrHardwareAvailable()) {
SetOculusVrRuntimeStatus(RuntimeStatus::kEnable);
preferred_device_enabled = true;
} else {
SetOculusVrRuntimeStatus(RuntimeStatus::kDisable);
}
#endif
#if BUILDFLAG(ENABLE_OPENVR)
if (!preferred_device_enabled && IsOpenVrHardwareAvailable()) {
SetOpenVrRuntimeStatus(RuntimeStatus::kEnable);
preferred_device_enabled = true;
} else {
SetOpenVrRuntimeStatus(RuntimeStatus::kDisable);
}
#endif
// Schedule this function to run again later.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&IsolatedXRRuntimeProvider::PollForDeviceChanges,
weak_ptr_factory_.GetWeakPtr()),
kTimeBetweenPollingEvents);
}
void IsolatedXRRuntimeProvider::SetupPollingForDeviceChanges() {
bool any_runtimes_available = false;
#if BUILDFLAG(ENABLE_OCULUS_VR)
if (base::FeatureList::IsEnabled(features::kOculusVR)) {
should_check_oculus_ = device::OculusDevice::IsApiAvailable();
any_runtimes_available |= should_check_oculus_;
}
#endif
#if BUILDFLAG(ENABLE_OPENVR)
if (base::FeatureList::IsEnabled(features::kOpenVR)) {
should_check_openvr_ = device::OpenVRDevice::IsApiAvailable();
any_runtimes_available |= should_check_openvr_;
}
#endif
#if BUILDFLAG(ENABLE_WINDOWS_MR)
if (base::FeatureList::IsEnabled(features::kWindowsMixedReality)) {
wmr_statics_ = device::MixedRealityDeviceStatics::CreateInstance();
should_check_wmr_ = wmr_statics_->IsApiAvailable();
any_runtimes_available |= should_check_wmr_;
}
#endif
#if BUILDFLAG(ENABLE_OPENXR)
if (base::FeatureList::IsEnabled(features::kOpenXR)) {
should_check_openxr_ = device::OpenXrDevice::IsApiAvailable();
any_runtimes_available |= should_check_openxr_;
}
#endif
// Begin polling for devices
if (any_runtimes_available) {
PollForDeviceChanges();
}
}
void IsolatedXRRuntimeProvider::RequestDevices(
device::mojom::IsolatedXRRuntimeProviderClientPtr client) {
// Start polling to detect devices being added/removed.
client_ = std::move(client);
SetupPollingForDeviceChanges();
client_->OnDevicesEnumerated();
}
#if BUILDFLAG(ENABLE_OCULUS_VR)
bool IsolatedXRRuntimeProvider::IsOculusVrHardwareAvailable() {
return should_check_oculus_ &&
((oculus_device_ && oculus_device_->IsAvailable()) ||
device::OculusDevice::IsHwAvailable());
}
void IsolatedXRRuntimeProvider::SetOculusVrRuntimeStatus(RuntimeStatus status) {
SetRuntimeStatus(client_, status, &oculus_device_);
}
#endif // BUILDFLAG(ENABLE_OCULUS_VR)
#if BUILDFLAG(ENABLE_OPENVR)
bool IsolatedXRRuntimeProvider::IsOpenVrHardwareAvailable() {
return should_check_openvr_ &&
((openvr_device_ && openvr_device_->IsAvailable()) ||
device::OpenVRDevice::IsHwAvailable());
}
void IsolatedXRRuntimeProvider::SetOpenVrRuntimeStatus(RuntimeStatus status) {
SetRuntimeStatus(client_, status, &openvr_device_);
}
#endif // BUILDFLAG(ENABLE_OPENVR)
#if BUILDFLAG(ENABLE_WINDOWS_MR)
bool IsolatedXRRuntimeProvider::IsWMRHardwareAvailable() {
return should_check_wmr_ && wmr_statics_->IsHardwareAvailable();
}
void IsolatedXRRuntimeProvider::SetWMRRuntimeStatus(RuntimeStatus status) {
SetRuntimeStatus(client_, status, &wmr_device_);
}
#endif // BUILDFLAG(ENABLE_WINDOWS_MR)
#if BUILDFLAG(ENABLE_OPENXR)
bool IsolatedXRRuntimeProvider::IsOpenXrHardwareAvailable() {
return should_check_openxr_ && device::OpenXrDevice::IsHardwareAvailable();
}
void IsolatedXRRuntimeProvider::SetOpenXrRuntimeStatus(RuntimeStatus status) {
SetRuntimeStatus(client_, status, &openxr_device_);
}
#endif // BUILDFLAG(ENABLE_OPENXR)
IsolatedXRRuntimeProvider::IsolatedXRRuntimeProvider() = default;
IsolatedXRRuntimeProvider::~IsolatedXRRuntimeProvider() {
#if BUILDFLAG(ENABLE_WINDOWS_MR)
// Explicitly null out wmr_device_ to clean up any COM objects that depend
// on being RoInitialized
wmr_device_ = nullptr;
#endif // BUILDFLAG(ENABLE_WINDOWS_MR)
}