Commit 000e431a authored by Patrick To's avatar Patrick To Committed by Commit Bot

Restart the GPU process if necessary to be XR compatible on Windows

When XR compatibility is requested, determine whether the GPU process
is using the same adapter that the active XR runtime is attached to.
If not, restart the GPU process and notify it to initialize and use
the XR runtime adapter.

This is the last series of changes to add multi-GPU support for XR
behind a flag. This change was initially reviewed as one large CL here:
crrev.com/c/2096778

That CL was split into this CL and the following:
crrev.com/c/2226071
crrev.com/c/2219780
crrev.com/c/2216166
crrev.com/c/2225366

After these land, there is followup work before enabling by default:
- crbug.com/1086697: Return a WebGL context if no XR runtimes available
- crbug.com/1090951: Migrate back to the default GPU on navigation
- crbug.com/1090955: Remove flicker when GPU process is restarting
- crbug.com/1087356: WebGL2 context

Bug: 792657
Change-Id: Ie3342afd630b624b78a4f24a70fedf2cae5eace4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2228544
Commit-Queue: Patrick To <patrto@microsoft.com>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarZhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarAlexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/master@{#779435}
parent 41c0034b
...@@ -252,6 +252,7 @@ static const char* const kSwitchNames[] = { ...@@ -252,6 +252,7 @@ static const char* const kSwitchNames[] = {
switches::kUseGpuInTests, switches::kUseGpuInTests,
switches::kV, switches::kV,
switches::kVModule, switches::kVModule,
switches::kUseAdapterLuid,
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
service_manager::switches::kEnableSandboxLogging, service_manager::switches::kEnableSandboxLogging,
switches::kDisableAVFoundationOverlays, switches::kDisableAVFoundationOverlays,
...@@ -1072,6 +1073,7 @@ GpuProcessKind GpuProcessHost::kind() { ...@@ -1072,6 +1073,7 @@ GpuProcessKind GpuProcessHost::kind() {
return kind_; return kind_;
} }
// Atomically shut down the GPU process with a normal termination status.
void GpuProcessHost::ForceShutdown() { void GpuProcessHost::ForceShutdown() {
// This is only called on the IO thread so no race against the constructor // This is only called on the IO thread so no race against the constructor
// for another GpuProcessHost. // for another GpuProcessHost.
......
...@@ -9,21 +9,27 @@ ...@@ -9,21 +9,27 @@
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/common/trace_event_common.h" #include "base/trace_event/common/trace_event_common.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/xr/xr_utils.h" #include "content/browser/xr/xr_utils.h"
#include "content/public/browser/device_service.h" #include "content/public/browser/device_service.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/gpu_utils.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "device/base/features.h" #include "device/base/features.h"
#include "device/vr/buildflags/buildflags.h" #include "device/vr/buildflags/buildflags.h"
#include "device/vr/orientation/orientation_device_provider.h" #include "device/vr/orientation/orientation_device_provider.h"
#include "device/vr/public/cpp/vr_device_provider.h" #include "device/vr/public/cpp/vr_device_provider.h"
#include "gpu/config/gpu_info.h"
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/mojom/sensor_provider.mojom.h" #include "services/device/public/mojom/sensor_provider.mojom.h"
#include "ui/gl/gl_switches.h"
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
#include "content/browser/xr/service/isolated_device_provider.h" #include "content/browser/xr/service/isolated_device_provider.h"
...@@ -311,12 +317,86 @@ void XRRuntimeManagerImpl::SupportsSession( ...@@ -311,12 +317,86 @@ void XRRuntimeManagerImpl::SupportsSession(
} }
void XRRuntimeManagerImpl::MakeXrCompatible() { void XRRuntimeManagerImpl::MakeXrCompatible() {
// TODO(http://crbug.com/792657): Restart GPU on correct adapter if needed. auto* runtime = GetImmersiveVrRuntime();
if (!runtime) {
// WebXR spec: if there's no device, xr compatible is false.
for (VRServiceImpl* service : services_)
service->OnMakeXrCompatibleComplete(
device::mojom::XrCompatibleResult::kNotCompatible);
return;
}
if (!IsInitializedOnCompatibleAdapter(runtime)) {
#if defined(OS_WIN)
base::Optional<LUID> luid = runtime->GetLuid();
// IsInitializedOnCompatibleAdapter should have returned true if the
// runtime doesn't specify a LUID.
DCHECK(luid && (luid->HighPart != 0 || luid->LowPart != 0));
// Add the XR compatible adapter LUID to the browser command line.
// GpuProcessHost::LaunchGpuProcess passes this to the GPU process.
std::string luid_string = base::NumberToString(luid->HighPart) + "," +
base::NumberToString(luid->LowPart);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kUseAdapterLuid, luid_string);
// Get notified when the new GPU process sends back its GPUInfo. This
// indicates that the GPU process has finished initializing and the GPUInfo
// contains the LUID of the active adapter.
content::GpuDataManager::GetInstance()->AddObserver(this);
content::KillGpuProcess();
return;
#else
// MakeXrCompatible is not yet supported on other platforms so
// IsInitializedOnCompatibleAdapter should have returned true.
NOTREACHED();
#endif
}
for (VRServiceImpl* service : services_) for (VRServiceImpl* service : services_)
service->OnMakeXrCompatibleComplete( service->OnMakeXrCompatibleComplete(
device::mojom::XrCompatibleResult::kAlreadyCompatible); device::mojom::XrCompatibleResult::kAlreadyCompatible);
} }
bool XRRuntimeManagerImpl::IsInitializedOnCompatibleAdapter(
BrowserXRRuntimeImpl* runtime) {
#if defined(OS_WIN)
base::Optional<LUID> luid = runtime->GetLuid();
if (luid && (luid->HighPart != 0 || luid->LowPart != 0)) {
LUID active_luid =
content::GpuDataManager::GetInstance()->GetGPUInfo().active_gpu().luid;
return active_luid.HighPart == luid->HighPart &&
active_luid.LowPart == luid->LowPart;
}
#endif
return true;
}
void XRRuntimeManagerImpl::OnGpuInfoUpdate() {
content::GpuDataManager::GetInstance()->RemoveObserver(this);
device::mojom::XrCompatibleResult xr_compatible_result;
auto* runtime = GetImmersiveVrRuntime();
if (runtime && IsInitializedOnCompatibleAdapter(runtime)) {
xr_compatible_result =
device::mojom::XrCompatibleResult::kCompatibleAfterRestart;
} else {
// We can still be incompatible after restarting if either:
// 1. The runtime has been removed (usually means the VR headset was
// unplugged) since the GPU process restart was triggered. Per the WebXR
// spec, if there is no device, xr compatible is false.
// 2. The GPU process is still not using the correct GPU after restarting.
xr_compatible_result =
device::mojom::XrCompatibleResult::kNotCompatibleAfterRestart;
}
for (VRServiceImpl* service : services_)
service->OnMakeXrCompatibleComplete(xr_compatible_result);
}
XRRuntimeManagerImpl::XRRuntimeManagerImpl(XRProviderList providers) XRRuntimeManagerImpl::XRRuntimeManagerImpl(XRProviderList providers)
: providers_(std::move(providers)) { : providers_(std::move(providers)) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "content/browser/xr/service/browser_xr_runtime_impl.h" #include "content/browser/xr/service/browser_xr_runtime_impl.h"
#include "content/browser/xr/service/vr_service_impl.h" #include "content/browser/xr/service/vr_service_impl.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "content/public/browser/xr_integration_client.h" #include "content/public/browser/xr_integration_client.h"
#include "content/public/browser/xr_runtime_manager.h" #include "content/public/browser/xr_runtime_manager.h"
#include "device/vr/public/mojom/vr_service.mojom-forward.h" #include "device/vr/public/mojom/vr_service.mojom-forward.h"
...@@ -33,7 +34,8 @@ class XRRuntimeManagerTest; ...@@ -33,7 +34,8 @@ class XRRuntimeManagerTest;
// instances. // instances.
class CONTENT_EXPORT XRRuntimeManagerImpl class CONTENT_EXPORT XRRuntimeManagerImpl
: public XRRuntimeManager, : public XRRuntimeManager,
public base::RefCounted<XRRuntimeManagerImpl> { public base::RefCounted<XRRuntimeManagerImpl>,
public content::GpuDataManagerObserver {
public: public:
friend base::RefCounted<XRRuntimeManagerImpl>; friend base::RefCounted<XRRuntimeManagerImpl>;
static constexpr auto kRefCountPreference = static constexpr auto kRefCountPreference =
...@@ -75,6 +77,9 @@ class CONTENT_EXPORT XRRuntimeManagerImpl ...@@ -75,6 +77,9 @@ class CONTENT_EXPORT XRRuntimeManagerImpl
void MakeXrCompatible(); void MakeXrCompatible();
// content::GpuDataManagerObserver
void OnGpuInfoUpdate() override;
// XRRuntimeManager implementation // XRRuntimeManager implementation
BrowserXRRuntimeImpl* GetRuntime(device::mojom::XRDeviceId id) override; BrowserXRRuntimeImpl* GetRuntime(device::mojom::XRDeviceId id) override;
void ForEachRuntime( void ForEachRuntime(
...@@ -105,6 +110,8 @@ class CONTENT_EXPORT XRRuntimeManagerImpl ...@@ -105,6 +110,8 @@ class CONTENT_EXPORT XRRuntimeManagerImpl
mojo::PendingRemote<device::mojom::XRRuntime> runtime); mojo::PendingRemote<device::mojom::XRRuntime> runtime);
void RemoveRuntime(device::mojom::XRDeviceId id); void RemoveRuntime(device::mojom::XRDeviceId id);
bool IsInitializedOnCompatibleAdapter(BrowserXRRuntimeImpl* runtime);
// Gets the system default immersive-vr runtime if available. // Gets the system default immersive-vr runtime if available.
BrowserXRRuntimeImpl* GetImmersiveVrRuntime(); BrowserXRRuntimeImpl* GetImmersiveVrRuntime();
......
...@@ -41,6 +41,12 @@ void StopGpuProcessImpl(base::OnceClosure callback, ...@@ -41,6 +41,12 @@ void StopGpuProcessImpl(base::OnceClosure callback,
std::move(callback).Run(); std::move(callback).Run();
} }
void KillGpuProcessImpl(content::GpuProcessHost* host) {
if (host) {
host->ForceShutdown();
}
}
} // namespace } // namespace
namespace content { namespace content {
...@@ -131,6 +137,11 @@ void StopGpuProcess(base::OnceClosure callback) { ...@@ -131,6 +137,11 @@ void StopGpuProcess(base::OnceClosure callback) {
std::move(callback)))); std::move(callback))));
} }
void KillGpuProcess() {
GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED, false /* force_create */,
base::BindOnce(&KillGpuProcessImpl));
}
gpu::GpuChannelEstablishFactory* GetGpuChannelEstablishFactory() { gpu::GpuChannelEstablishFactory* GetGpuChannelEstablishFactory() {
return BrowserMainLoop::GetInstance()->gpu_channel_establish_factory(); return BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
} }
......
...@@ -20,6 +20,10 @@ CONTENT_EXPORT const gpu::GpuPreferences GetGpuPreferencesFromCommandLine(); ...@@ -20,6 +20,10 @@ CONTENT_EXPORT const gpu::GpuPreferences GetGpuPreferencesFromCommandLine();
CONTENT_EXPORT void StopGpuProcess(base::OnceClosure callback); CONTENT_EXPORT void StopGpuProcess(base::OnceClosure callback);
// Kills the GPU process with a normal termination status.
// TODO(crbug.com/1095977): Combine with StopGpuProcess
CONTENT_EXPORT void KillGpuProcess();
CONTENT_EXPORT gpu::GpuChannelEstablishFactory* GetGpuChannelEstablishFactory(); CONTENT_EXPORT gpu::GpuChannelEstablishFactory* GetGpuChannelEstablishFactory();
#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
......
...@@ -89,6 +89,12 @@ ...@@ -89,6 +89,12 @@
#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C
#endif /* EGL_ANGLE_platform_angle_d3d */ #endif /* EGL_ANGLE_platform_angle_d3d */
#ifndef EGL_ANGLE_platform_angle_d3d_luid
#define EGL_ANGLE_platform_angle_d3d_luid 1
#define EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE 0x34A0
#define EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE 0x34A1
#endif /* EGL_ANGLE_platform_angle_d3d_luid */
#ifndef EGL_ANGLE_platform_angle_d3d11on12 #ifndef EGL_ANGLE_platform_angle_d3d11on12
#define EGL_ANGLE_platform_angle_d3d11on12 1 #define EGL_ANGLE_platform_angle_d3d11on12 1
#define EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE 0x3488 #define EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE 0x3488
...@@ -289,6 +295,30 @@ EGLDisplay GetPlatformANGLEDisplay( ...@@ -289,6 +295,30 @@ EGLDisplay GetPlatformANGLEDisplay(
display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
display_attribs.push_back(static_cast<EGLAttrib>(platform_type)); display_attribs.push_back(static_cast<EGLAttrib>(platform_type));
if (platform_type == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kUseAdapterLuid)) {
// If the LUID is specified, the format is <high part>,<low part>. Split
// and add them to the EGL_ANGLE_platform_angle_d3d_luid ext attributes.
std::string luid =
command_line->GetSwitchValueASCII(switches::kUseAdapterLuid);
size_t comma = luid.find(',');
if (comma != std::string::npos) {
int32_t high;
uint32_t low;
if (!base::StringToInt(luid.substr(0, comma), &high) ||
!base::StringToUint(luid.substr(comma + 1), &low))
return EGL_NO_DISPLAY;
display_attribs.push_back(EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE);
display_attribs.push_back(high);
display_attribs.push_back(EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE);
display_attribs.push_back(low);
}
}
}
GLDisplayEglUtil::GetInstance()->GetPlatformExtraDisplayAttribs( GLDisplayEglUtil::GetInstance()->GetPlatformExtraDisplayAttribs(
platform_type, &display_attribs); platform_type, &display_attribs);
......
...@@ -141,6 +141,10 @@ const char kEnableDirectCompositionVideoOverlays[] = ...@@ -141,6 +141,10 @@ const char kEnableDirectCompositionVideoOverlays[] =
const char kDisableDirectCompositionVideoOverlays[] = const char kDisableDirectCompositionVideoOverlays[] =
"disable-direct-composition-video-overlays"; "disable-direct-composition-video-overlays";
// Initialize the GPU process using the adapter with the specified LUID. This is
// only used on Windows, as LUID is a Windows specific structure.
const char kUseAdapterLuid[] = "use-adapter-luid";
// This is the list of switches passed from this file that are passed from the // This is the list of switches passed from this file that are passed from the
// GpuProcessHost to the GPU Process. Add your switch to this list if you need // GpuProcessHost to the GPU Process. Add your switch to this list if you need
// to read it in the GPU process, else don't add it. // to read it in the GPU process, else don't add it.
......
...@@ -69,6 +69,7 @@ GL_EXPORT extern const char kEnableSwapBuffersWithBounds[]; ...@@ -69,6 +69,7 @@ GL_EXPORT extern const char kEnableSwapBuffersWithBounds[];
GL_EXPORT extern const char kDisableDirectComposition[]; GL_EXPORT extern const char kDisableDirectComposition[];
GL_EXPORT extern const char kEnableDirectCompositionVideoOverlays[]; GL_EXPORT extern const char kEnableDirectCompositionVideoOverlays[];
GL_EXPORT extern const char kDisableDirectCompositionVideoOverlays[]; GL_EXPORT extern const char kDisableDirectCompositionVideoOverlays[];
GL_EXPORT extern const char kUseAdapterLuid[];
// These flags are used by the test harness code, not passed in by users. // These flags are used by the test harness code, not passed in by users.
GL_EXPORT extern const char kDisableGLDrawingForTests[]; GL_EXPORT extern const char kDisableGLDrawingForTests[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment