/* -*- 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 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 mappings; LayerTreeOwnerTracker::Get()->Iterate( [&](LayersId aLayersId, base::ProcessId aProcessId) { mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); }); nsCOMPtr gfxInfo = components::GfxInfo::Service(); nsTArray features; if (gfxInfo) { auto* gfxInfoRaw = static_cast(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 obsSvc = mozilla::services::GetObserverService(); MOZ_ASSERT(obsSvc); if (obsSvc) { obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); } return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildHistograms( nsTArray&& aAccumulations) { TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Gpu, aAccumulations); return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildKeyedHistograms( nsTArray&& aAccumulations) { TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Gpu, aAccumulations); return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvUpdateChildScalars( nsTArray&& aScalarActions) { TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Gpu, aScalarActions); return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvUpdateChildKeyedScalars( nsTArray&& aScalarActions) { TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Gpu, aScalarActions); return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvRecordChildEvents( nsTArray&& 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::RecvNotifyDisableRemoteCanvas() { gfxPlatform::DisableRemoteCanvas(); return IPC_OK(); } mozilla::ipc::IPCResult GPUChild::RecvFlushMemory(const nsString& aReason) { nsCOMPtr 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& aDMDFile) { mMemoryReportRequest = MakeUnique(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 obsvc = services::GetObserverService()) { RefPtr 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 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 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&& aChild) : Runnable("gfx::DeferredDeleteGPUChild"), mChild(std::move(aChild)) {} NS_IMETHODIMP Run() override { return NS_OK; } private: RefPtr mChild; }; /* static */ void GPUChild::Destroy(RefPtr&& aChild) { NS_DispatchToMainThread(new DeferredDeleteGPUChild(std::move(aChild))); } } // namespace gfx } // namespace mozilla