blob: 01c549158c198917879defac7bc9e6eeef16234f [file] [log] [blame]
// Copyright 2015 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/browser/vr/service/xr_runtime_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "base/trace_event/common/trace_event_common.h"
#include "build/build_config.h"
#include "chrome/browser/vr/service/browser_xr_runtime.h"
#include "chrome/common/chrome_features.h"
#include "content/public/browser/system_connector.h"
#include "content/public/common/content_features.h"
#include "device/vr/buildflags/buildflags.h"
#include "device/vr/orientation/orientation_device_provider.h"
#include "device/vr/vr_device_provider.h"
#if BUILDFLAG(ENABLE_OPENVR)
#include "device/vr/openvr/openvr_device.h"
#endif
#if defined(OS_ANDROID)
#include "device/vr/android/gvr/gvr_device_provider.h"
#if BUILDFLAG(ENABLE_ARCORE)
#include "device/vr/android/arcore/arcore_device_provider_factory.h"
#endif // BUILDFLAG(ENABLE_ARCORE)
#else // !defined(OS_ANDROID)
#include "chrome/browser/vr/service/isolated_device_provider.h"
#endif // defined(OS_ANDROID)
namespace vr {
namespace {
XRRuntimeManager* g_xr_runtime_manager = nullptr;
base::LazyInstance<base::ObserverList<XRRuntimeManagerObserver>>::Leaky
g_xr_runtime_manager_observers;
} // namespace
scoped_refptr<XRRuntimeManager> XRRuntimeManager::GetOrCreateInstance() {
if (g_xr_runtime_manager) {
return base::WrapRefCounted(g_xr_runtime_manager);
}
// Register VRDeviceProviders for the current platform
ProviderList providers;
// TODO(https://crbug.com/966647) remove this check when AR features ships as
// enabled by default or as an origin trial.
if (base::FeatureList::IsEnabled(features::kWebXrArModule)) {
#if defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_ARCORE)
providers.emplace_back(device::ArCoreDeviceProviderFactory::Create());
#endif // BUILDFLAG(ENABLE_ARCORE)
#endif // defined(OS_ANDROID)
}
#if defined(OS_ANDROID)
providers.emplace_back(std::make_unique<device::GvrDeviceProvider>());
#else // !defined(OS_ANDROID)
providers.emplace_back(std::make_unique<vr::IsolatedVRDeviceProvider>());
#endif // defined(OS_ANDROID)
auto* connector = content::GetSystemConnector();
if (connector) {
providers.emplace_back(
std::make_unique<device::VROrientationDeviceProvider>(connector));
}
return CreateInstance(std::move(providers));
}
bool XRRuntimeManager::HasInstance() {
return g_xr_runtime_manager != nullptr;
}
XRRuntimeManager* XRRuntimeManager::GetInstanceIfCreated() {
return g_xr_runtime_manager;
}
void XRRuntimeManager::RecordVrStartupHistograms() {
#if BUILDFLAG(ENABLE_OPENVR)
device::OpenVRDevice::RecordRuntimeAvailability();
#endif
}
void XRRuntimeManager::AddObserver(XRRuntimeManagerObserver* observer) {
g_xr_runtime_manager_observers.Get().AddObserver(observer);
}
void XRRuntimeManager::RemoveObserver(XRRuntimeManagerObserver* observer) {
g_xr_runtime_manager_observers.Get().RemoveObserver(observer);
}
/* static */
void XRRuntimeManager::ExitImmersivePresentation() {
if (!g_xr_runtime_manager) {
return;
}
auto* browser_xr_runtime = g_xr_runtime_manager->GetImmersiveRuntime();
if (!browser_xr_runtime) {
return;
}
browser_xr_runtime->ExitVrFromPresentingService();
}
void XRRuntimeManager::AddService(VRServiceImpl* service) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Loop through any currently active runtimes and send Connected messages to
// the service. Future runtimes that come online will send a Connected message
// when they are created.
InitializeProviders();
if (AreAllProvidersInitialized())
service->InitializationComplete();
services_.insert(service);
}
void XRRuntimeManager::RemoveService(VRServiceImpl* service) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
services_.erase(service);
for (const auto& runtime : runtimes_) {
runtime.second->OnServiceRemoved(service);
}
}
BrowserXRRuntime* XRRuntimeManager::GetRuntime(device::mojom::XRDeviceId id) {
auto it = runtimes_.find(id);
if (it == runtimes_.end())
return nullptr;
return it->second.get();
}
BrowserXRRuntime* XRRuntimeManager::GetRuntimeForOptions(
device::mojom::XRSessionOptions* options) {
// Examine options to determine which device provider we should use.
// AR requested.
if (options->environment_integration) {
if (!options->immersive) {
DVLOG(1) << __func__ << ": non-immersive AR mode is unsupported";
return nullptr;
}
// Return the ARCore runtime, but only if it supports all required features.
auto* runtime = GetRuntime(device::mojom::XRDeviceId::ARCORE_DEVICE_ID);
return runtime && runtime->SupportsAllFeatures(options->required_features)
? runtime
: nullptr;
}
if (options->immersive) {
auto* runtime = GetImmersiveRuntime();
return runtime && runtime->SupportsAllFeatures(options->required_features)
? runtime
: nullptr;
} else {
// Non immersive session.
// Try the orientation provider if it exists.
// If we don't have an orientation provider, then we don't have an explicit
// runtime to back a non-immersive session
auto* orientation_runtime =
GetRuntime(device::mojom::XRDeviceId::ORIENTATION_DEVICE_ID);
return orientation_runtime && orientation_runtime->SupportsAllFeatures(
options->required_features)
? orientation_runtime
: nullptr;
}
}
BrowserXRRuntime* XRRuntimeManager::GetImmersiveRuntime() {
#if defined(OS_ANDROID)
auto* gvr = GetRuntime(device::mojom::XRDeviceId::GVR_DEVICE_ID);
if (gvr)
return gvr;
#endif
#if BUILDFLAG(ENABLE_OPENXR)
auto* openxr = GetRuntime(device::mojom::XRDeviceId::OPENXR_DEVICE_ID);
if (openxr)
return openxr;
#endif
#if BUILDFLAG(ENABLE_OPENVR)
auto* openvr = GetRuntime(device::mojom::XRDeviceId::OPENVR_DEVICE_ID);
if (openvr)
return openvr;
#endif
#if BUILDFLAG(ENABLE_OCULUS_VR)
auto* oculus = GetRuntime(device::mojom::XRDeviceId::OCULUS_DEVICE_ID);
if (oculus)
return oculus;
#endif
#if BUILDFLAG(ENABLE_WINDOWS_MR)
auto* wmr = GetRuntime(device::mojom::XRDeviceId::WINDOWS_MIXED_REALITY_ID);
if (wmr)
return wmr;
#endif
return nullptr;
}
device::mojom::VRDisplayInfoPtr XRRuntimeManager::GetCurrentVRDisplayInfo(
VRServiceImpl* service) {
DVLOG(1) << __func__;
// Get an immersive runtime if there is one.
auto* immersive_runtime = GetImmersiveRuntime();
if (immersive_runtime) {
// Listen to changes for this runtime.
immersive_runtime->OnServiceAdded(service);
// If we don't have display info for the immersive runtime, get display info
// from a different runtime.
if (!immersive_runtime->GetVRDisplayInfo()) {
immersive_runtime = nullptr;
}
}
// Get an AR runtime if there is one.
device::mojom::XRSessionOptions options = {};
options.environment_integration = true;
auto* ar_runtime = GetRuntimeForOptions(&options);
if (ar_runtime) {
// Listen to changes for this runtime.
ar_runtime->OnServiceAdded(service);
}
// If there is neither, use the generic non-immersive runtime.
if (!ar_runtime && !immersive_runtime) {
device::mojom::XRSessionOptions options = {};
auto* non_immersive_runtime = GetRuntimeForOptions(&options);
if (non_immersive_runtime) {
// Listen to changes for this runtime.
non_immersive_runtime->OnServiceAdded(service);
}
// If we don't have an AR or immersive runtime, return the generic non-
// immersive runtime's DisplayInfo if we have it.
return non_immersive_runtime ? non_immersive_runtime->GetVRDisplayInfo()
: nullptr;
}
// Use the immersive or AR runtime.
device::mojom::VRDisplayInfoPtr device_info =
immersive_runtime ? immersive_runtime->GetVRDisplayInfo()
: ar_runtime->GetVRDisplayInfo();
return device_info;
}
bool XRRuntimeManager::IsOtherClientPresenting(VRServiceImpl* service) {
DCHECK(service);
auto* runtime = GetImmersiveRuntime();
if (!runtime)
return false; // No immersive runtime to be presenting.
VRServiceImpl* presenting_service = runtime->GetPresentingVRService();
if (!presenting_service)
return false; // No VRServiceImpl is presenting.
// True if some other VRServiceImpl is presenting.
return (presenting_service != service);
}
bool XRRuntimeManager::HasAnyRuntime() {
return !runtimes_.empty();
}
void XRRuntimeManager::SupportsSession(
device::mojom::XRSessionOptionsPtr options,
device::mojom::VRService::SupportsSessionCallback callback) {
auto* runtime = GetRuntimeForOptions(options.get());
if (!runtime) {
std::move(callback).Run(false);
return;
}
// TODO(http://crbug.com/842025): Pass supports session on to the runtimes.
std::move(callback).Run(true);
}
XRRuntimeManager::XRRuntimeManager(ProviderList providers)
: providers_(std::move(providers)) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
CHECK(!g_xr_runtime_manager);
g_xr_runtime_manager = this;
}
XRRuntimeManager::~XRRuntimeManager() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
CHECK_EQ(g_xr_runtime_manager, this);
g_xr_runtime_manager = nullptr;
}
scoped_refptr<XRRuntimeManager> XRRuntimeManager::CreateInstance(
ProviderList providers) {
auto* ptr = new XRRuntimeManager(std::move(providers));
CHECK_EQ(ptr, g_xr_runtime_manager);
return base::AdoptRef(ptr);
}
device::mojom::XRRuntime* XRRuntimeManager::GetRuntimeForTest(
device::mojom::XRDeviceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DeviceRuntimeMap::iterator iter =
runtimes_.find(static_cast<device::mojom::XRDeviceId>(id));
if (iter == runtimes_.end())
return nullptr;
return iter->second->GetRuntime();
}
size_t XRRuntimeManager::NumberOfConnectedServices() {
return services_.size();
}
void XRRuntimeManager::InitializeProviders() {
if (providers_initialized_)
return;
for (const auto& provider : providers_) {
provider->Initialize(
base::BindRepeating(&XRRuntimeManager::AddRuntime,
base::Unretained(this)),
base::BindRepeating(&XRRuntimeManager::RemoveRuntime,
base::Unretained(this)),
base::BindOnce(&XRRuntimeManager::OnProviderInitialized,
base::Unretained(this)));
}
providers_initialized_ = true;
}
void XRRuntimeManager::OnProviderInitialized() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
++num_initialized_providers_;
if (AreAllProvidersInitialized()) {
for (VRServiceImpl* service : services_)
service->InitializationComplete();
}
}
bool XRRuntimeManager::AreAllProvidersInitialized() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return num_initialized_providers_ == providers_.size();
}
void XRRuntimeManager::AddRuntime(device::mojom::XRDeviceId id,
device::mojom::VRDisplayInfoPtr info,
device::mojom::XRRuntimePtr runtime) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(runtimes_.find(id) == runtimes_.end());
TRACE_EVENT_INSTANT1("xr", "AddRuntime", TRACE_EVENT_SCOPE_THREAD, "id", id);
runtimes_[id] = std::make_unique<BrowserXRRuntime>(id, std::move(runtime),
std::move(info));
for (XRRuntimeManagerObserver& obs : g_xr_runtime_manager_observers.Get())
obs.OnRuntimeAdded(runtimes_[id].get());
for (VRServiceImpl* service : services_)
// TODO(sumankancherla): Consider combining with XRRuntimeManagerObserver.
service->RuntimesChanged();
}
void XRRuntimeManager::RemoveRuntime(device::mojom::XRDeviceId id) {
TRACE_EVENT_INSTANT1("xr", "RemoveRuntime", TRACE_EVENT_SCOPE_THREAD, "id",
id);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = runtimes_.find(id);
DCHECK(it != runtimes_.end());
// Remove the runtime from runtimes_ before notifying services that it was
// removed, since they will query for runtimes in RuntimesChanged.
std::unique_ptr<BrowserXRRuntime> removed_runtime = std::move(it->second);
runtimes_.erase(it);
for (VRServiceImpl* service : services_)
service->RuntimesChanged();
}
} // namespace vr