Autogenerated with:
cp dom/.clang-format gfx/ && mach format gfx/**.{cpp,h,mm}
Manual changes:
* OSVRSession headers are not system headers and they are not
standalone / rely on stdint, so tweaked to preserve previous
ordering.
* Missing include in GLDefs.h
* Missing include in gfxOTSUtils.
* Need to keep the windows header order in DCLayerTree.
* missing hb_font include in MockScaledFont.h
Differential Revision: https://phabricator.services.mozilla.com/D311695
375 lines
11 KiB
C++
375 lines
11 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "GPUProcessHost.h"
|
|
|
|
#include "VRGPUChild.h"
|
|
#include "chrome/common/process_watcher.h"
|
|
#include "gfxPlatform.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/StaticPrefs_layers.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/gfx/GPUChild.h"
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/ipc/ProcessUtils.h"
|
|
#include "mozilla/layers/SynchronousTask.h"
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
# include "mozilla/java/GeckoProcessManagerWrappers.h"
|
|
#endif
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
# include "mozilla/SandboxSettings.h"
|
|
#endif
|
|
#ifdef XP_WIN
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace gfx {
|
|
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
bool GPUProcessHost::sLaunchWithMacSandbox = false;
|
|
#endif
|
|
|
|
using namespace ipc;
|
|
|
|
GPUProcessHost::GPUProcessHost(Listener* aListener)
|
|
: GeckoChildProcessHost(GeckoProcessType_GPU),
|
|
mListener(aListener),
|
|
mLaunchPhase(LaunchPhase::Unlaunched),
|
|
mProcessToken(0),
|
|
mShutdownRequested(false),
|
|
mChannelClosed(false),
|
|
mLiveToken(new media::Refcountable<bool>(true)) {
|
|
MOZ_COUNT_CTOR(GPUProcessHost);
|
|
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
if (!sLaunchWithMacSandbox) {
|
|
sLaunchWithMacSandbox = IsGPUSandboxEnabled();
|
|
}
|
|
mDisableOSActivityMode = sLaunchWithMacSandbox;
|
|
#endif
|
|
}
|
|
|
|
GPUProcessHost::~GPUProcessHost() { MOZ_COUNT_DTOR(GPUProcessHost); }
|
|
|
|
bool GPUProcessHost::Launch(geckoargs::ChildProcessArgs aExtraOpts) {
|
|
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
|
|
MOZ_ASSERT(!mGPUChild);
|
|
MOZ_ASSERT(!gfxPlatform::IsHeadless());
|
|
|
|
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
|
|
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_GPU,
|
|
/* remoteType */ ""_ns)) {
|
|
return false;
|
|
}
|
|
mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
|
|
|
|
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
|
mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level");
|
|
#endif
|
|
|
|
mLaunchPhase = LaunchPhase::Waiting;
|
|
mLaunchTime = TimeStamp::Now();
|
|
|
|
if (!GeckoChildProcessHost::AsyncLaunch(std::move(aExtraOpts))) {
|
|
mLaunchPhase = LaunchPhase::Complete;
|
|
mPrefSerializer = nullptr;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GPUProcessHost::WaitForLaunch() {
|
|
MOZ_ASSERT(mLaunchPhase != LaunchPhase::Unlaunched);
|
|
if (mLaunchPhase == LaunchPhase::Complete) {
|
|
return !!mGPUChild;
|
|
}
|
|
|
|
int32_t timeoutMs =
|
|
StaticPrefs::layers_gpu_process_startup_timeout_ms_AtStartup();
|
|
|
|
// If one of the following environment variables are set we can effectively
|
|
// ignore the timeout - as we can guarantee the compositor process will be
|
|
// terminated
|
|
if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
|
|
PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
|
|
timeoutMs = 0;
|
|
}
|
|
|
|
if (mLaunchPhase == LaunchPhase::Waiting) {
|
|
// Our caller expects the connection to be finished by the time we return,
|
|
// so we immediately set up the IPDL actor and fire callbacks. The IO thread
|
|
// will still dispatch a notification to the main thread - we'll just ignore
|
|
// it.
|
|
bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
|
|
InitAfterConnect(result);
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Connected);
|
|
// Our caller expects post-connection initialization tasks, such as ensuring
|
|
// the GPUChild is initialized, to be finished by the time we return, so
|
|
// finish these tasks synchronously now.
|
|
return CompleteInitSynchronously();
|
|
}
|
|
|
|
void GPUProcessHost::OnProcessLaunchError(const base::LaunchError aError) {
|
|
bool oom = false;
|
|
#ifdef XP_WIN
|
|
static const size_t kLowMemoryThreshold = 1024 * 1024 * 1024;
|
|
|
|
MEMORYSTATUSEX stat;
|
|
switch (aError.ErrorCode()) {
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_OUTOFMEMORY:
|
|
case ERROR_DEVICE_NO_RESOURCES:
|
|
case ERROR_COMMITMENT_LIMIT:
|
|
oom = true;
|
|
break;
|
|
default:
|
|
// It could fail for many reasons but if it isn't an explicit memory
|
|
// failure, but we are low on memory, it is probably due to OOM.
|
|
stat.dwLength = sizeof(stat);
|
|
oom = GlobalMemoryStatusEx(&stat) &&
|
|
(stat.ullAvailVirtual < kLowMemoryThreshold ||
|
|
stat.ullAvailPhys < kLowMemoryThreshold);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
gfxCriticalNote << "GPU proc launch error " << aError.FunctionName().get()
|
|
<< (oom ? " OOM " : " ") << gfx::hexa(aError.ErrorCode());
|
|
|
|
MonitorAutoLock lock(mMonitor);
|
|
mProcessState = PROCESS_ERROR;
|
|
mLaunchOomError = oom;
|
|
lock.Notify();
|
|
}
|
|
|
|
void GPUProcessHost::OnChannelConnected(base::ProcessId peer_pid) {
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
GeckoChildProcessHost::OnChannelConnected(peer_pid);
|
|
|
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
|
"GPUProcessHost::OnChannelConnected",
|
|
[self = this, liveToken = mLiveToken]() {
|
|
if (*liveToken && self->mLaunchPhase == LaunchPhase::Waiting) {
|
|
self->InitAfterConnect(true);
|
|
}
|
|
}));
|
|
}
|
|
|
|
static uint64_t sProcessTokenCounter = 0;
|
|
|
|
void GPUProcessHost::InitAfterConnect(bool aSucceeded) {
|
|
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
|
|
MOZ_ASSERT(!mGPUChild);
|
|
|
|
mPrefSerializer = nullptr;
|
|
|
|
if (aSucceeded) {
|
|
mLaunchPhase = LaunchPhase::Connected;
|
|
mProcessToken = ++sProcessTokenCounter;
|
|
mGPUChild = MakeRefPtr<GPUChild>(this);
|
|
DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mGPUChild.get());
|
|
MOZ_ASSERT(rv);
|
|
|
|
nsTArray<RefPtr<GPUChild::InitPromiseType>> initPromises;
|
|
initPromises.AppendElement(mGPUChild->Init());
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
nsCOMPtr<nsISerialEventTarget> launcherThread(GetIPCLauncher());
|
|
MOZ_ASSERT(launcherThread);
|
|
RefPtr<GPUChild::InitPromiseType> csmPromise =
|
|
InvokeAsync(
|
|
launcherThread, __func__,
|
|
[] {
|
|
java::CompositorSurfaceManager::LocalRef csm =
|
|
java::GeckoProcessManager::GetCompositorSurfaceManager();
|
|
return MozPromise<java::CompositorSurfaceManager::GlobalRef, Ok,
|
|
true>::CreateAndResolve(csm, __func__);
|
|
})
|
|
->Map(GetCurrentSerialEventTarget(), __func__,
|
|
[self = this, liveToken = mLiveToken](
|
|
java::CompositorSurfaceManager::GlobalRef&& aCsm) {
|
|
if (*liveToken) {
|
|
self->mCompositorSurfaceManager = aCsm;
|
|
}
|
|
return Ok{};
|
|
});
|
|
initPromises.AppendElement(csmPromise);
|
|
#endif
|
|
|
|
GPUChild::InitPromiseType::All(GetCurrentSerialEventTarget(), initPromises)
|
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
|
[self = this, liveToken = mLiveToken]() {
|
|
if (*liveToken) {
|
|
self->OnAsyncInitComplete();
|
|
}
|
|
});
|
|
} else {
|
|
mLaunchPhase = LaunchPhase::Complete;
|
|
if (mListener) {
|
|
mListener->OnProcessLaunchComplete(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GPUProcessHost::OnAsyncInitComplete() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mLaunchPhase == LaunchPhase::Connected) {
|
|
mLaunchPhase = LaunchPhase::Complete;
|
|
if (mListener) {
|
|
mListener->OnProcessLaunchComplete(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GPUProcessHost::CompleteInitSynchronously() {
|
|
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Connected);
|
|
|
|
const bool result = mGPUChild->EnsureGPUReady();
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
if (!mCompositorSurfaceManager) {
|
|
layers::SynchronousTask task(
|
|
"GeckoProcessManager::GetCompositorSurfaceManager");
|
|
|
|
nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
|
|
MOZ_ASSERT(launcherThread);
|
|
launcherThread->Dispatch(NS_NewRunnableFunction(
|
|
"GeckoProcessManager::GetCompositorSurfaceManager", [&]() {
|
|
layers::AutoCompleteTask complete(&task);
|
|
mCompositorSurfaceManager =
|
|
java::GeckoProcessManager::GetCompositorSurfaceManager();
|
|
}));
|
|
|
|
task.Wait();
|
|
}
|
|
#endif
|
|
|
|
mLaunchPhase = LaunchPhase::Complete;
|
|
if (mListener) {
|
|
mListener->OnProcessLaunchComplete(this);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void GPUProcessHost::Shutdown(bool aUnexpectedShutdown) {
|
|
MOZ_ASSERT(!mShutdownRequested);
|
|
|
|
mListener = nullptr;
|
|
|
|
if (mGPUChild) {
|
|
// OnChannelClosed uses this to check if the shutdown was expected or
|
|
// unexpected.
|
|
mShutdownRequested = true;
|
|
|
|
if (aUnexpectedShutdown) {
|
|
mGPUChild->OnUnexpectedShutdown();
|
|
}
|
|
|
|
// The channel might already be closed if we got here unexpectedly.
|
|
if (!mChannelClosed) {
|
|
if (VRGPUChild::IsCreated()) {
|
|
VRGPUChild::Get()->Close();
|
|
}
|
|
mGPUChild->SendShutdownVR();
|
|
mGPUChild->Close();
|
|
}
|
|
|
|
#ifndef NS_FREE_PERMANENT_DATA
|
|
// No need to communicate shutdown, the GPU process doesn't need to
|
|
// communicate anything back.
|
|
KillHard(/* aGenerateMinidump */ false);
|
|
#endif
|
|
|
|
// If we're shutting down unexpectedly, we're in the middle of handling an
|
|
// ActorDestroy for PGPUChild, which is still on the stack. We'll return
|
|
// back to OnChannelClosed.
|
|
//
|
|
// Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
|
|
// acknowledges shutdown.
|
|
return;
|
|
}
|
|
|
|
DestroyProcess();
|
|
}
|
|
|
|
void GPUProcessHost::OnChannelClosed() {
|
|
mChannelClosed = true;
|
|
|
|
if (!mShutdownRequested && mListener) {
|
|
// This is an unclean shutdown. Notify our listener that we're going away.
|
|
mListener->OnProcessUnexpectedShutdown(this);
|
|
} else {
|
|
DestroyProcess();
|
|
}
|
|
|
|
// Release the actor.
|
|
GPUChild::Destroy(std::move(mGPUChild));
|
|
MOZ_ASSERT(!mGPUChild);
|
|
}
|
|
|
|
void GPUProcessHost::KillHard(bool aGenerateMinidump) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mGPUChild && aGenerateMinidump) {
|
|
mGPUChild->GeneratePairedMinidump();
|
|
}
|
|
|
|
const ProcessHandle handle = GetChildProcessHandle();
|
|
if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) {
|
|
if (mGPUChild) {
|
|
mGPUChild->DeletePairedMinidump();
|
|
}
|
|
NS_WARNING("failed to kill subprocess!");
|
|
}
|
|
|
|
SetAlreadyDead();
|
|
}
|
|
|
|
uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; }
|
|
|
|
void GPUProcessHost::KillProcess(bool aGenerateMinidump) {
|
|
KillHard(aGenerateMinidump);
|
|
}
|
|
|
|
void GPUProcessHost::CrashProcess() { mGPUChild->SendCrashProcess(); }
|
|
|
|
void GPUProcessHost::DestroyProcess() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Any pending tasks will be cancelled from now on.
|
|
*mLiveToken = false;
|
|
|
|
NS_DispatchToMainThread(
|
|
NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
|
|
}
|
|
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
bool GPUProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
|
|
GeckoChildProcessHost::FillMacSandboxInfo(aInfo);
|
|
if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_GPU_LOGGING")) {
|
|
aInfo.shouldLog = true;
|
|
}
|
|
aInfo.type = MacSandboxType::MacSandboxType_GPU;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
java::CompositorSurfaceManager::Param
|
|
GPUProcessHost::GetCompositorSurfaceManager() {
|
|
return mCompositorSurfaceManager;
|
|
}
|
|
#endif
|
|
|
|
} // namespace gfx
|
|
} // namespace mozilla
|