blob: 9d88fe61bee80b0fe686ffaf4d5bce447c7dd421 [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 "content/services/isolated_xr_device/xr_runtime_provider.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "content/public/common/content_switches.h"
#include "device/base/features.h"
#include "device/vr/buildflags/buildflags.h"
#if BUILDFLAG(ENABLE_OPENXR)
#include "content/public/common/gpu_stream_constants.h"
#include "device/vr/openxr/openxr_device.h"
#include "device/vr/openxr/openxr_statics.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#endif
enum class IsolatedXRRuntimeProvider::RuntimeStatus {
kEnable,
kDisable,
};
namespace {
// Poll for device add/remove every 5 seconds.
constexpr base::TimeDelta kTimeBetweenPollingEvents = base::Seconds(5);
template <typename VrDeviceT>
std::unique_ptr<VrDeviceT> CreateDevice() {
return std::make_unique<VrDeviceT>();
}
template <typename VrDeviceT>
std::unique_ptr<VrDeviceT> EnableRuntime(
device::mojom::IsolatedXRRuntimeProviderClient* client,
base::OnceCallback<std::unique_ptr<VrDeviceT>()> create_device) {
auto device = std::move(create_device).Run();
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->BindXRRuntime(), device->BindCompositorHost(),
device->GetDeviceData(), device->GetId());
return device;
}
template <typename VrDeviceT>
void DisableRuntime(device::mojom::IsolatedXRRuntimeProviderClient* 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::IsolatedXRRuntimeProviderClient* client,
IsolatedXRRuntimeProvider::RuntimeStatus status,
base::OnceCallback<std::unique_ptr<VrHardwareT>()> create_device,
std::unique_ptr<VrHardwareT>* out_device) {
if (status == IsolatedXRRuntimeProvider::RuntimeStatus::kEnable &&
!*out_device) {
*out_device = EnableRuntime<VrHardwareT>(client, std::move(create_device));
} else if (status == IsolatedXRRuntimeProvider::RuntimeStatus::kDisable &&
*out_device) {
DisableRuntime(client, std::move(*out_device));
}
}
// If none of the runtimes are enabled, this function will be unused.
// This is a bit more scalable than wrapping it in all the typedefs
[[maybe_unused]] bool IsEnabled(const base::CommandLine* command_line,
const base::Feature& feature,
const std::string& name) {
if (!command_line->HasSwitch(switches::kWebXrForceRuntime))
return base::FeatureList::IsEnabled(feature);
return (base::CompareCaseInsensitiveASCII(
command_line->GetSwitchValueASCII(switches::kWebXrForceRuntime),
name) == 0);
}
} // 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() {
// If none of the following runtimes are enabled, we'll get an error for
// 'preferred_device_enabled' being unused, thus [[maybe_unused]].
[[maybe_unused]] bool preferred_device_enabled = false;
#if BUILDFLAG(ENABLE_OPENXR)
if (!preferred_device_enabled && IsOpenXrHardwareAvailable()) {
SetOpenXrRuntimeStatus(RuntimeStatus::kEnable);
preferred_device_enabled = true;
} else {
SetOpenXrRuntimeStatus(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;
[[maybe_unused]] const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
// If none of the following runtimes are enabled, we'll get an error for
// 'command_line' being unused, thus [[maybe_unused]].
#if BUILDFLAG(ENABLE_OPENXR)
if (IsEnabled(command_line, device::features::kOpenXR,
switches::kWebXrRuntimeOpenXr)) {
should_check_openxr_ =
device::OpenXrStatics::GetInstance()->IsApiAvailable();
any_runtimes_available |= should_check_openxr_;
}
#endif
// Begin polling for devices
if (any_runtimes_available) {
PollForDeviceChanges();
}
}
void IsolatedXRRuntimeProvider::RequestDevices(
mojo::PendingRemote<device::mojom::IsolatedXRRuntimeProviderClient>
client) {
// Start polling to detect devices being added/removed.
client_.Bind(std::move(client));
SetupPollingForDeviceChanges();
client_->OnDevicesEnumerated();
}
#if BUILDFLAG(ENABLE_OPENXR)
bool IsolatedXRRuntimeProvider::IsOpenXrHardwareAvailable() {
return should_check_openxr_ &&
device::OpenXrStatics::GetInstance()->IsHardwareAvailable();
}
void IsolatedXRRuntimeProvider::SetOpenXrRuntimeStatus(RuntimeStatus status) {
auto factory_async = base::BindRepeating(
&IsolatedXRRuntimeProvider::CreateContextProviderAsync,
weak_ptr_factory_.GetWeakPtr());
SetRuntimeStatus(client_.get(), status,
base::BindOnce(
[](VizContextProviderFactoryAsync factory_async) {
return std::make_unique<device::OpenXrDevice>(
std::move(factory_async));
},
std::move(factory_async)),
&openxr_device_);
}
// A repeating callback to CreateContextProviderAsync is created in
// SetOpenXrRuntimeStatus and passed to OpenXrDevice. OpenXrRenderLoop posts a
// task with this callback onto the main thread's task runner while it is
// running on the render loop thread's task runner. The context provider and its
// supporting object, viz::Gpu, are required to be created on the main thread's
// task runner. Upon creating the context provider, CreateContextProviderAsync
// posts a callback back to the render loop's thread runner with the newly
// created context provider.
void IsolatedXRRuntimeProvider::CreateContextProviderAsync(
VizContextProviderCallback viz_context_provider_callback,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
// viz_gpu_ must be kept alive so long as there are outstanding context
// providers attached to it, otherwise the GPU process channel gets closed out
// from under it.
if (!viz_gpu_ || !viz_gpu_->GetGpuChannel() ||
viz_gpu_->GetGpuChannel()->IsLost()) {
mojo::PendingRemote<viz::mojom::Gpu> remote_gpu;
device_service_host_->BindGpu(remote_gpu.InitWithNewPipeAndPassReceiver());
viz_gpu_ = viz::Gpu::Create(std::move(remote_gpu), io_task_runner_);
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
viz_gpu_->EstablishGpuChannelSync();
}
scoped_refptr<viz::ContextProvider> context_provider =
base::MakeRefCounted<viz::ContextProviderCommandBuffer>(
viz_gpu_->GetGpuChannel(), nullptr /* gpu_memory_buffer_manager */,
content::kGpuStreamIdDefault, content::kGpuStreamPriorityUI,
gpu::kNullSurfaceHandle, GURL(std::string("chrome://gpu/XrRuntime")),
false /* automatic flushes */, false /* support locking */,
false /* support grcontext */,
gpu::SharedMemoryLimits::ForMailboxContext(),
gpu::ContextCreationAttribs(),
viz::command_buffer_metrics::ContextType::XR_COMPOSITING);
task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(viz_context_provider_callback),
std::move(context_provider)));
}
#endif // BUILDFLAG(ENABLE_OPENXR)
IsolatedXRRuntimeProvider::IsolatedXRRuntimeProvider(
mojo::PendingRemote<device::mojom::XRDeviceServiceHost> device_service_host,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: device_service_host_(std::move(device_service_host)),
io_task_runner_(std::move(io_task_runner)) {}
IsolatedXRRuntimeProvider::~IsolatedXRRuntimeProvider() = default;