GPUChild.cpp (14752B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "GPUChild.h" 7 8 #include "GPUProcessHost.h" 9 #include "GPUProcessManager.h" 10 #include "GfxInfoBase.h" 11 #include "TelemetryProbesReporter.h" 12 #include "VideoUtils.h" 13 #include "VRProcessManager.h" 14 #include "gfxConfig.h" 15 #include "gfxPlatform.h" 16 #include "mozilla/Components.h" 17 #include "mozilla/FOGIPC.h" 18 #include "mozilla/StaticPrefs_dom.h" 19 #include "mozilla/StaticPrefs_media.h" 20 #include "mozilla/glean/GfxMetrics.h" 21 #include "mozilla/glean/IpcMetrics.h" 22 #include "mozilla/Telemetry.h" 23 #include "mozilla/TelemetryIPC.h" 24 #include "mozilla/dom/CheckerboardReportService.h" 25 #include "mozilla/dom/ContentParent.h" 26 #include "mozilla/dom/MemoryReportRequest.h" 27 #include "mozilla/gfx/Logging.h" 28 #include "mozilla/gfx/gfxVars.h" 29 #include "mozilla/HangDetails.h" 30 #include "mozilla/RemoteMediaManagerChild.h" // For RemoteMediaIn 31 #include "mozilla/ipc/Endpoint.h" 32 #include "mozilla/layers/APZInputBridgeChild.h" 33 #include "mozilla/layers/LayerTreeOwnerTracker.h" 34 #include "nsHashPropertyBag.h" 35 #include "nsIGfxInfo.h" 36 #include "nsIObserverService.h" 37 #include "nsIPropertyBag2.h" 38 #include "ProfilerParent.h" 39 40 namespace mozilla { 41 namespace gfx { 42 43 using namespace layers; 44 45 GPUChild::GPUChild(GPUProcessHost* aHost) : mHost(aHost), mGPUReady(false) {} 46 47 GPUChild::~GPUChild() = default; 48 49 RefPtr<GPUChild::InitPromiseType> GPUChild::Init() { 50 nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); 51 52 DevicePrefs devicePrefs; 53 devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); 54 devicePrefs.d3d11Compositing() = 55 gfxConfig::GetValue(Feature::D3D11_COMPOSITING); 56 devicePrefs.oglCompositing() = 57 gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); 58 devicePrefs.d3d11HwAngle() = gfxConfig::GetValue(Feature::D3D11_HW_ANGLE); 59 60 nsTArray<LayerTreeIdMapping> mappings; 61 LayerTreeOwnerTracker::Get()->Iterate( 62 [&](LayersId aLayersId, base::ProcessId aProcessId) { 63 mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); 64 }); 65 66 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); 67 nsTArray<GfxInfoFeatureStatus> features; 68 if (gfxInfo) { 69 auto* gfxInfoRaw = static_cast<widget::GfxInfoBase*>(gfxInfo.get()); 70 features = gfxInfoRaw->GetAllFeatures(); 71 } 72 73 RefPtr<InitPromiseType> promise = 74 SendInit(updates, devicePrefs, mappings, features, 75 GPUProcessManager::Get()->AllocateNamespace()) 76 ->Then( 77 GetCurrentSerialEventTarget(), __func__, 78 [self = RefPtr{this}](GPUDeviceData&& aData) { 79 self->OnInitComplete(aData); 80 return InitPromiseType::CreateAndResolve(Ok{}, __func__); 81 }, 82 [](ipc::ResponseRejectReason) { 83 return InitPromiseType::CreateAndReject(Ok{}, __func__); 84 }); 85 86 gfxVars::AddReceiver(this); 87 88 (void)SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid())); 89 90 return promise; 91 } 92 93 void GPUChild::OnVarChanged(const nsTArray<GfxVarUpdate>& aVar) { 94 SendUpdateVar(aVar); 95 } 96 97 bool GPUChild::EnsureGPUReady(bool aForceSync /* = false */) { 98 // On our initial process launch, we want to block on the GetDeviceStatus 99 // message. Additionally, we may have updated our compositor configuration 100 // through the gfxVars after fallback, in which case we want to ensure the 101 // GPU process has handled any updates before creating compositor sessions. 102 if (mGPUReady && !aForceSync) { 103 return true; 104 } 105 106 GPUDeviceData data; 107 if (!SendGetDeviceStatus(&data)) { 108 return false; 109 } 110 111 OnInitComplete(data); 112 return true; 113 } 114 115 void GPUChild::OnUnexpectedShutdown() { mUnexpectedShutdown = true; } 116 117 void GPUChild::GeneratePairedMinidump() { 118 // At most generate the paired minidumps twice per session in order to 119 // avoid accumulating a large number of unsubmitted minidumps on disk. 120 if (mCrashReporter && mNumPairedMinidumpsCreated < 2) { 121 nsAutoCString additionalDumps("browser"); 122 mCrashReporter->AddAnnotationNSCString( 123 CrashReporter::Annotation::additional_minidumps, additionalDumps); 124 125 nsAutoCString reason("GPUProcessKill"); 126 mCrashReporter->AddAnnotationNSCString( 127 CrashReporter::Annotation::ipc_channel_error, reason); 128 129 if (mCrashReporter->GenerateMinidumpAndPair(mHost, "browser"_ns)) { 130 mCrashReporter->FinalizeCrashReport(); 131 mCreatedPairedMinidumps = true; 132 mNumPairedMinidumpsCreated++; 133 } 134 } 135 } 136 137 void GPUChild::DeletePairedMinidump() { 138 if (mCrashReporter && mCreatedPairedMinidumps) { 139 mCrashReporter->DeleteCrashReport(); 140 mCreatedPairedMinidumps = false; 141 } 142 } 143 144 void GPUChild::OnInitComplete(const GPUDeviceData& aData) { 145 // This function may be called multiple times, for example if we synchronously 146 // requested GPU parameters before the asynchronous SendInit completed, or if 147 // EnsureGPUReady is called after launch to force a synchronization after a 148 // configuration change. We only want to import device data and collect 149 // telemetry for the initial GPU process launch. 150 if (mGPUReady) { 151 return; 152 } 153 154 gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); 155 glean::gpu_process::launch_time.AccumulateRawDuration(TimeStamp::Now() - 156 mHost->GetLaunchTime()); 157 mGPUReady = true; 158 } 159 160 mozilla::ipc::IPCResult GPUChild::RecvDeclareStable() { 161 mHost->mListener->OnProcessDeclaredStable(); 162 return IPC_OK(); 163 } 164 165 mozilla::ipc::IPCResult GPUChild::RecvReportCheckerboard( 166 const uint32_t& aSeverity, const nsCString& aLog) { 167 layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get())); 168 return IPC_OK(); 169 } 170 171 mozilla::ipc::IPCResult GPUChild::RecvGraphicsError(const nsCString& aError) { 172 gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); 173 if (lf) { 174 std::stringstream message; 175 message << "GP+" << aError.get(); 176 lf->UpdateStringsVector(message.str()); 177 } 178 return IPC_OK(); 179 } 180 181 mozilla::ipc::IPCResult GPUChild::RecvCreateVRProcess() { 182 // Make sure create VR process at the main process 183 MOZ_ASSERT(XRE_IsParentProcess()); 184 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { 185 VRProcessManager::Initialize(); 186 VRProcessManager* vr = VRProcessManager::Get(); 187 MOZ_ASSERT(vr, "VRProcessManager must be initialized first."); 188 189 if (vr) { 190 vr->LaunchVRProcess(); 191 } 192 } 193 194 return IPC_OK(); 195 } 196 197 mozilla::ipc::IPCResult GPUChild::RecvShutdownVRProcess() { 198 // Make sure stopping VR process at the main process 199 MOZ_ASSERT(XRE_IsParentProcess()); 200 if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { 201 VRProcessManager::Shutdown(); 202 } 203 204 return IPC_OK(); 205 } 206 207 mozilla::ipc::IPCResult GPUChild::RecvNotifyUiObservers( 208 const nsCString& aTopic) { 209 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); 210 MOZ_ASSERT(obsSvc); 211 if (obsSvc) { 212 obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); 213 } 214 return IPC_OK(); 215 } 216 217 mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildHistograms( 218 nsTArray<HistogramAccumulation>&& aAccumulations) { 219 TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Gpu, 220 aAccumulations); 221 return IPC_OK(); 222 } 223 224 mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildKeyedHistograms( 225 nsTArray<KeyedHistogramAccumulation>&& aAccumulations) { 226 TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Gpu, 227 aAccumulations); 228 return IPC_OK(); 229 } 230 231 mozilla::ipc::IPCResult GPUChild::RecvUpdateChildScalars( 232 nsTArray<ScalarAction>&& aScalarActions) { 233 TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Gpu, aScalarActions); 234 return IPC_OK(); 235 } 236 237 mozilla::ipc::IPCResult GPUChild::RecvUpdateChildKeyedScalars( 238 nsTArray<KeyedScalarAction>&& aScalarActions) { 239 TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Gpu, 240 aScalarActions); 241 return IPC_OK(); 242 } 243 244 mozilla::ipc::IPCResult GPUChild::RecvRecordChildEvents( 245 nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) { 246 TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Gpu, aEvents); 247 return IPC_OK(); 248 } 249 250 mozilla::ipc::IPCResult GPUChild::RecvRecordDiscardedData( 251 const mozilla::Telemetry::DiscardedData& aDiscardedData) { 252 TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Gpu, aDiscardedData); 253 return IPC_OK(); 254 } 255 256 mozilla::ipc::IPCResult GPUChild::RecvNotifyDeviceReset( 257 const GPUDeviceData& aData, const DeviceResetReason& aReason, 258 const DeviceResetDetectPlace& aPlace) { 259 gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); 260 mHost->mListener->OnRemoteProcessDeviceReset(mHost, aReason, aPlace); 261 return IPC_OK(); 262 } 263 264 mozilla::ipc::IPCResult GPUChild::RecvNotifyOverlayInfo( 265 const OverlayInfo aInfo) { 266 gfxPlatform::GetPlatform()->SetOverlayInfo(aInfo); 267 return IPC_OK(); 268 } 269 270 mozilla::ipc::IPCResult GPUChild::RecvNotifySwapChainInfo( 271 const SwapChainInfo aInfo) { 272 gfxPlatform::GetPlatform()->SetSwapChainInfo(aInfo); 273 return IPC_OK(); 274 } 275 276 mozilla::ipc::IPCResult GPUChild::RecvNotifyDisableRemoteCanvas() { 277 gfxPlatform::DisableRemoteCanvas(); 278 return IPC_OK(); 279 } 280 281 mozilla::ipc::IPCResult GPUChild::RecvFlushMemory(const nsString& aReason) { 282 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 283 if (os) { 284 os->NotifyObservers(nullptr, "memory-pressure", aReason.get()); 285 } 286 return IPC_OK(); 287 } 288 289 bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, 290 const bool& aAnonymize, 291 const bool& aMinimizeMemoryUsage, 292 const Maybe<FileDescriptor>& aDMDFile) { 293 mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); 294 295 PGPUChild::SendRequestMemoryReport( 296 aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, 297 [&](const uint32_t& aGeneration2) { 298 if (GPUProcessManager* gpm = GPUProcessManager::Get()) { 299 if (GPUChild* child = gpm->GetGPUChild()) { 300 if (child->mMemoryReportRequest) { 301 child->mMemoryReportRequest->Finish(aGeneration2); 302 child->mMemoryReportRequest = nullptr; 303 } 304 } 305 } 306 }, 307 [&](mozilla::ipc::ResponseRejectReason) { 308 if (GPUProcessManager* gpm = GPUProcessManager::Get()) { 309 if (GPUChild* child = gpm->GetGPUChild()) { 310 child->mMemoryReportRequest = nullptr; 311 } 312 } 313 }); 314 315 return true; 316 } 317 318 mozilla::ipc::IPCResult GPUChild::RecvAddMemoryReport( 319 const MemoryReport& aReport) { 320 if (mMemoryReportRequest) { 321 mMemoryReportRequest->RecvReport(aReport); 322 } 323 return IPC_OK(); 324 } 325 326 void GPUChild::ActorDestroy(ActorDestroyReason aWhy) { 327 if (aWhy == AbnormalShutdown || mUnexpectedShutdown) { 328 nsAutoCString processType( 329 XRE_GeckoProcessTypeToString(GeckoProcessType_GPU)); 330 glean::subprocess::abnormal_abort.Get(processType).Add(1); 331 332 nsAutoString dumpId; 333 if (!mCreatedPairedMinidumps) { 334 GenerateCrashReport(&dumpId); 335 } else if (mCrashReporter) { 336 dumpId = mCrashReporter->MinidumpID(); 337 } 338 339 // Notify the Telemetry environment so that we can refresh and do a 340 // subsession split. This also notifies the crash reporter on geckoview. 341 if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) { 342 RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); 343 props->SetPropertyAsBool(u"abnormal"_ns, true); 344 props->SetPropertyAsAString(u"dumpID"_ns, dumpId); 345 props->SetPropertyAsACString(u"processType"_ns, processType); 346 obsvc->NotifyObservers((nsIPropertyBag2*)props, 347 "compositor:process-aborted", nullptr); 348 } 349 } 350 351 gfxVars::RemoveReceiver(this); 352 mHost->OnChannelClosed(); 353 } 354 355 mozilla::ipc::IPCResult GPUChild::RecvUpdateFeature( 356 const Feature& aFeature, const FeatureFailure& aChange) { 357 gfxConfig::SetFailed(aFeature, aChange.status(), aChange.message().get(), 358 aChange.failureId()); 359 return IPC_OK(); 360 } 361 362 mozilla::ipc::IPCResult GPUChild::RecvUsedFallback(const Fallback& aFallback, 363 const nsCString& aMessage) { 364 gfxConfig::EnableFallback(aFallback, aMessage.get()); 365 return IPC_OK(); 366 } 367 368 mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang( 369 const HangDetails& aDetails) { 370 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 371 if (obs) { 372 // Copy the HangDetails recieved over the network into a nsIHangDetails, and 373 // then fire our own observer notification. 374 // XXX: We should be able to avoid this potentially expensive copy here by 375 // moving our deserialized argument. 376 nsCOMPtr<nsIHangDetails> hangDetails = 377 new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No); 378 obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr); 379 } 380 return IPC_OK(); 381 } 382 383 mozilla::ipc::IPCResult GPUChild::RecvUpdateMediaCodecsSupported( 384 const media::MediaCodecsSupported& aSupported) { 385 if (ContainHardwareCodecsSupported(aSupported)) { 386 mozilla::TelemetryProbesReporter::ReportDeviceMediaCodecSupported( 387 aSupported); 388 } 389 #if defined(XP_WIN) 390 // Do not propagate HEVC support if the pref is off 391 media::MediaCodecsSupported trimedSupported = aSupported; 392 if (aSupported.contains( 393 mozilla::media::MediaCodecsSupport::HEVCHardwareDecode) && 394 !StaticPrefs::media_hevc_enabled()) { 395 trimedSupported -= mozilla::media::MediaCodecsSupport::HEVCHardwareDecode; 396 } 397 dom::ContentParent::BroadcastMediaCodecsSupportedUpdate( 398 RemoteMediaIn::GpuProcess, trimedSupported); 399 #else 400 dom::ContentParent::BroadcastMediaCodecsSupportedUpdate( 401 RemoteMediaIn::GpuProcess, aSupported); 402 #endif 403 return IPC_OK(); 404 } 405 406 mozilla::ipc::IPCResult GPUChild::RecvFOGData(ByteBuf&& aBuf) { 407 glean::FOGData(std::move(aBuf)); 408 return IPC_OK(); 409 } 410 411 class DeferredDeleteGPUChild : public Runnable { 412 public: 413 explicit DeferredDeleteGPUChild(RefPtr<GPUChild>&& aChild) 414 : Runnable("gfx::DeferredDeleteGPUChild"), mChild(std::move(aChild)) {} 415 416 NS_IMETHODIMP Run() override { return NS_OK; } 417 418 private: 419 RefPtr<GPUChild> mChild; 420 }; 421 422 /* static */ 423 void GPUChild::Destroy(RefPtr<GPUChild>&& aChild) { 424 NS_DispatchToMainThread(new DeferredDeleteGPUChild(std::move(aChild))); 425 } 426 427 } // namespace gfx 428 } // namespace mozilla