diff options
Diffstat (limited to 'gfx/ipc/GPUChild.cpp')
-rw-r--r-- | gfx/ipc/GPUChild.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/gfx/ipc/GPUChild.cpp b/gfx/ipc/GPUChild.cpp new file mode 100644 index 0000000000..7dca827069 --- /dev/null +++ b/gfx/ipc/GPUChild.cpp @@ -0,0 +1,366 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "GPUChild.h" + +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "GfxInfoBase.h" +#include "VRProcessManager.h" +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "mozilla/Components.h" +#include "mozilla/FOGIPC.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TelemetryIPC.h" +#include "mozilla/dom/CheckerboardReportService.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/MemoryReportRequest.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +#endif +#include "mozilla/HangDetails.h" +#include "mozilla/RemoteDecoderManagerChild.h" // For RemoteDecodeIn +#include "mozilla/Unused.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZInputBridgeChild.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "nsHashPropertyBag.h" +#include "nsIGfxInfo.h" +#include "nsIObserverService.h" +#include "nsIPropertyBag2.h" +#include "ProfilerParent.h" + +namespace mozilla { +namespace gfx { + +using namespace layers; + +GPUChild::GPUChild(GPUProcessHost* aHost) : mHost(aHost), mGPUReady(false) {} + +GPUChild::~GPUChild() = default; + +void GPUChild::Init() { + nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); + + DevicePrefs devicePrefs; + devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); + devicePrefs.d3d11Compositing() = + gfxConfig::GetValue(Feature::D3D11_COMPOSITING); + devicePrefs.oglCompositing() = + gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); + devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D); + devicePrefs.d3d11HwAngle() = gfxConfig::GetValue(Feature::D3D11_HW_ANGLE); + + nsTArray<LayerTreeIdMapping> mappings; + LayerTreeOwnerTracker::Get()->Iterate( + [&](LayersId aLayersId, base::ProcessId aProcessId) { + mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); + }); + + nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); + nsTArray<GfxInfoFeatureStatus> features; + if (gfxInfo) { + auto* gfxInfoRaw = static_cast<widget::GfxInfoBase*>(gfxInfo.get()); + features = gfxInfoRaw->GetAllFeatures(); + } + + SendInit(updates, devicePrefs, mappings, features, + GPUProcessManager::Get()->AllocateNamespace()); + + gfxVars::AddReceiver(this); + + Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid())); +} + +void GPUChild::OnVarChanged(const GfxVarUpdate& aVar) { SendUpdateVar(aVar); } + +bool GPUChild::EnsureGPUReady() { + // On our initial process launch, we want to block on the GetDeviceStatus + // message. Additionally, we may have updated our compositor configuration + // through the gfxVars after fallback, in which case we want to ensure the + // GPU process has handled any updates before creating compositor sessions. + if (mGPUReady && !mWaitForVarUpdate) { + return true; + } + + GPUDeviceData data; + if (!SendGetDeviceStatus(&data)) { + return false; + } + + // Only import and collect telemetry for the initial GPU process launch. + if (!mGPUReady) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + } + + mWaitForVarUpdate = false; + return true; +} + +void GPUChild::OnUnexpectedShutdown() { mUnexpectedShutdown = true; } + +mozilla::ipc::IPCResult GPUChild::RecvInitComplete(const GPUDeviceData& aData) { + // We synchronously requested GPU parameters before this arrived. + if (mGPUReady) { + return IPC_OK(); + } + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvDeclareStable() { + mHost->mListener->OnProcessDeclaredStable(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvReportCheckerboard( + const uint32_t& aSeverity, const nsCString& aLog) { + layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get())); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvGraphicsError(const nsCString& aError) { + gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); + if (lf) { + std::stringstream message; + message << "GP+" << aError.get(); + lf->UpdateStringsVector(message.str()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvCreateVRProcess() { + // Make sure create VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Initialize(); + VRProcessManager* vr = VRProcessManager::Get(); + MOZ_ASSERT(vr, "VRProcessManager must be initialized first."); + + if (vr) { + vr->LaunchVRProcess(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvShutdownVRProcess() { + // Make sure stopping VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Shutdown(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyUiObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildHistograms( + nsTArray<HistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildKeyedHistograms( + nsTArray<KeyedHistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildScalars( + nsTArray<ScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Gpu, aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildKeyedScalars( + nsTArray<KeyedScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Gpu, + aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordChildEvents( + nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) { + TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Gpu, aEvents); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordDiscardedData( + const mozilla::Telemetry::DiscardedData& aDiscardedData) { + TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Gpu, aDiscardedData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyDeviceReset( + const GPUDeviceData& aData) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + mHost->mListener->OnRemoteProcessDeviceReset(mHost); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyOverlayInfo( + const OverlayInfo aInfo) { + gfxPlatform::GetPlatform()->SetOverlayInfo(aInfo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifySwapChainInfo( + const SwapChainInfo aInfo) { + gfxPlatform::GetPlatform()->SetSwapChainInfo(aInfo); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvFlushMemory(const nsString& aReason) { + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->NotifyObservers(nullptr, "memory-pressure", aReason.get()); + } + return IPC_OK(); +} + +bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<FileDescriptor>& aDMDFile) { + mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); + + PGPUChild::SendRequestMemoryReport( + aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, + [&](const uint32_t& aGeneration2) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + if (child->mMemoryReportRequest) { + child->mMemoryReportRequest->Finish(aGeneration2); + child->mMemoryReportRequest = nullptr; + } + } + } + }, + [&](mozilla::ipc::ResponseRejectReason) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + child->mMemoryReportRequest = nullptr; + } + } + }); + + return true; +} + +mozilla::ipc::IPCResult GPUChild::RecvAddMemoryReport( + const MemoryReport& aReport) { + if (mMemoryReportRequest) { + mMemoryReportRequest->RecvReport(aReport); + } + return IPC_OK(); +} + +void GPUChild::ActorDestroy(ActorDestroyReason aWhy) { + if (aWhy == AbnormalShutdown || mUnexpectedShutdown) { + nsAutoString dumpId; + GenerateCrashReport(OtherPid(), &dumpId); + + Telemetry::Accumulate( + Telemetry::SUBPROCESS_ABNORMAL_ABORT, + nsDependentCString(XRE_GeckoProcessTypeToString(GeckoProcessType_GPU)), + 1); + + // Notify the Telemetry environment so that we can refresh and do a + // subsession split. This also notifies the crash reporter on geckoview. + if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) { + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsBool(u"abnormal"_ns, true); + props->SetPropertyAsAString(u"dumpID"_ns, dumpId); + obsvc->NotifyObservers((nsIPropertyBag2*)props, + "compositor:process-aborted", nullptr); + } + } + + gfxVars::RemoveReceiver(this); + mHost->OnChannelClosed(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateFeature( + const Feature& aFeature, const FeatureFailure& aChange) { + gfxConfig::SetFailed(aFeature, aChange.status(), aChange.message().get(), + aChange.failureId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUsedFallback(const Fallback& aFallback, + const nsCString& aMessage) { + gfxConfig::EnableFallback(aFallback, aMessage.get()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang( + const HangDetails& aDetails) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // Copy the HangDetails recieved over the network into a nsIHangDetails, and + // then fire our own observer notification. + // XXX: We should be able to avoid this potentially expensive copy here by + // moving our deserialized argument. + nsCOMPtr<nsIHangDetails> hangDetails = + new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No); + obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateMediaCodecsSupported( + const media::MediaCodecsSupported& aSupported) { + dom::ContentParent::BroadcastMediaCodecsSupportedUpdate( + RemoteDecodeIn::GpuProcess, aSupported); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvFOGData(ByteBuf&& aBuf) { + glean::FOGData(std::move(aBuf)); + return IPC_OK(); +} + +class DeferredDeleteGPUChild : public Runnable { + public: + explicit DeferredDeleteGPUChild(RefPtr<GPUChild>&& aChild) + : Runnable("gfx::DeferredDeleteGPUChild"), mChild(std::move(aChild)) {} + + NS_IMETHODIMP Run() override { return NS_OK; } + + private: + RefPtr<GPUChild> mChild; +}; + +/* static */ +void GPUChild::Destroy(RefPtr<GPUChild>&& aChild) { + NS_DispatchToMainThread(new DeferredDeleteGPUChild(std::move(aChild))); +} + +} // namespace gfx +} // namespace mozilla |