| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/gpu/gpu_child_thread.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/power_monitor/power_monitor.h" |
| #include "base/power_monitor/power_monitor_device_source.h" |
| #include "base/run_loop.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/threading/thread_checker.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "content/child/child_process.h" |
| #include "content/common/process_visibility_tracker.h" |
| #include "content/gpu/browser_exposed_gpu_interfaces.h" |
| #include "content/gpu/gpu_service_factory.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/gpu/content_gpu_client.h" |
| #include "gpu/command_buffer/common/shm_count.h" |
| #include "gpu/ipc/service/gpu_channel_manager.h" |
| #include "gpu/ipc/service/gpu_init.h" |
| #include "gpu/ipc/service/gpu_watchdog_thread.h" |
| #include "ipc/ipc_sync_message_filter.h" |
| #include "media/gpu/ipc/service/media_gpu_channel_manager.h" |
| #include "mojo/public/cpp/bindings/binder_map.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h" |
| #include "mojo/public/cpp/system/functions.h" |
| #include "services/metrics/public/cpp/mojo_ukm_recorder.h" |
| #include "services/metrics/public/mojom/ukm_interface.mojom.h" |
| #include "services/service_manager/public/cpp/binder_registry.h" |
| #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h" |
| #include "third_party/skia/include/core/SkGraphics.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "media/base/android/media_drm_bridge_client.h" |
| #include "media/mojo/clients/mojo_android_overlay.h" |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "components/services/font/public/cpp/font_loader.h" // nogncheck |
| #include "components/services/font/public/mojom/font_service.mojom.h" // nogncheck |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| #include "third_party/skia/include/ports/SkFontConfigInterface.h" |
| #endif |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #include "content/child/sandboxed_process_thread_type_handler.h" |
| #endif |
| |
| namespace content { |
| namespace { |
| |
| // Called when the GPU process receives a bad IPC message. |
| void HandleBadMessage(const std::string& error) { |
| LOG(ERROR) << "Mojo error in GPU process: " << error; |
| mojo::debug::ScopedMessageErrorCrashKey crash_key_value(error); |
| base::debug::DumpWithoutCrashing(); |
| } |
| |
| ChildThreadImpl::Options GetOptions( |
| const InProcessChildThreadParams* in_process_params = nullptr) { |
| ChildThreadImpl::Options::Builder builder; |
| builder.ConnectToBrowser(true); |
| builder.ExposesInterfacesToBrowser(); |
| if (in_process_params) |
| builder.InBrowserProcess(*in_process_params); |
| return builder.Build(); |
| } |
| |
| viz::VizMainImpl::ExternalDependencies CreateVizMainDependencies() { |
| viz::VizMainImpl::ExternalDependencies deps; |
| if (!base::PowerMonitor::IsInitialized()) { |
| deps.power_monitor_source = |
| std::make_unique<base::PowerMonitorDeviceSource>(); |
| } |
| if (GetContentClient()->gpu()) { |
| deps.sync_point_manager = GetContentClient()->gpu()->GetSyncPointManager(); |
| deps.shared_image_manager = |
| GetContentClient()->gpu()->GetSharedImageManager(); |
| deps.scheduler = GetContentClient()->gpu()->GetScheduler(); |
| deps.viz_compositor_thread_runner = |
| GetContentClient()->gpu()->GetVizCompositorThreadRunner(); |
| } |
| auto* process = ChildProcess::current(); |
| deps.shutdown_event = process->GetShutDownEvent(); |
| deps.io_thread_task_runner = process->io_task_runner(); |
| |
| mojo::Remote<ukm::mojom::UkmRecorderFactory> factory; |
| ChildThread::Get()->BindHostReceiver(factory.BindNewPipeAndPassReceiver()); |
| deps.ukm_recorder = ukm::MojoUkmRecorder::Create(*factory); |
| return deps; |
| } |
| |
| } // namespace |
| |
| GpuChildThread::GpuChildThread(base::RepeatingClosure quit_closure, |
| std::unique_ptr<gpu::GpuInit> gpu_init) |
| : GpuChildThread(std::move(quit_closure), |
| GetOptions(), |
| std::move(gpu_init)) {} |
| |
| GpuChildThread::GpuChildThread(const InProcessChildThreadParams& params, |
| std::unique_ptr<gpu::GpuInit> gpu_init) |
| : GpuChildThread(base::DoNothing(), |
| GetOptions(¶ms), |
| std::move(gpu_init)) {} |
| |
| GpuChildThread::GpuChildThread(base::RepeatingClosure quit_closure, |
| ChildThreadImpl::Options options, |
| std::unique_ptr<gpu::GpuInit> gpu_init) |
| : ChildThreadImpl(MakeQuitSafelyClosure(), std::move(options)), |
| viz_main_(this, CreateVizMainDependencies(), std::move(gpu_init)), |
| quit_closure_(std::move(quit_closure)) { |
| if (in_process_gpu()) { |
| DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kSingleProcess) || |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kInProcessGPU)); |
| } |
| } |
| |
| GpuChildThread::~GpuChildThread() = default; |
| |
| void GpuChildThread::Init(const base::TimeTicks& process_start_time) { |
| if (!in_process_gpu()) |
| mojo::SetDefaultProcessErrorHandler(base::BindRepeating(&HandleBadMessage)); |
| |
| viz_main_.gpu_service()->set_start_time(process_start_time); |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| SandboxedProcessThreadTypeHandler::NotifyMainChildThreadCreated(); |
| #endif |
| |
| // When running in in-process mode, this has been set in the browser at |
| // ChromeBrowserMainPartsAndroid::PreMainMessageLoopRun(). |
| #if BUILDFLAG(IS_ANDROID) |
| if (!in_process_gpu()) { |
| media::SetMediaDrmBridgeClient( |
| GetContentClient()->GetMediaDrmBridgeClient()); |
| } |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| if (!in_process_gpu()) { |
| mojo::PendingRemote<font_service::mojom::FontService> font_service; |
| BindHostReceiver(font_service.InitWithNewPipeAndPassReceiver()); |
| SkFontConfigInterface::SetGlobal( |
| sk_make_sp<font_service::FontLoader>(std::move(font_service))); |
| } |
| #endif |
| |
| memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>( |
| FROM_HERE, base::BindRepeating(&GpuChildThread::OnMemoryPressure, |
| base::Unretained(this))); |
| } |
| |
| bool GpuChildThread::in_process_gpu() const { |
| return viz_main_.gpu_service()->gpu_info().in_process_gpu; |
| } |
| |
| void GpuChildThread::OnInitializationFailed() { |
| OnChannelError(); |
| } |
| |
| void GpuChildThread::OnGpuServiceConnection(viz::GpuServiceImpl* gpu_service) { |
| media::AndroidOverlayMojoFactoryCB overlay_factory_cb; |
| #if BUILDFLAG(IS_ANDROID) |
| overlay_factory_cb = |
| base::BindRepeating(&GpuChildThread::CreateAndroidOverlay, |
| base::SingleThreadTaskRunner::GetCurrentDefault()); |
| gpu_service->media_gpu_channel_manager()->SetOverlayFactory( |
| overlay_factory_cb); |
| #endif |
| |
| if (!IsInBrowserProcess()) { |
| gpu_service->SetVisibilityChangedCallback( |
| base::BindRepeating([](bool visible) { |
| ProcessVisibilityTracker::GetInstance()->OnProcessVisibilityChanged( |
| visible); |
| })); |
| } |
| |
| // Only set once per process instance. |
| service_factory_ = std::make_unique<GpuServiceFactory>( |
| gpu_service->gpu_preferences(), |
| gpu_service->gpu_channel_manager()->gpu_driver_bug_workarounds(), |
| gpu_service->gpu_feature_info(), gpu_service->gpu_info(), |
| gpu_service->media_gpu_channel_manager()->AsWeakPtr(), |
| gpu_service->gpu_memory_buffer_factory(), std::move(overlay_factory_cb)); |
| for (auto& receiver : pending_service_receivers_) |
| BindServiceInterface(std::move(receiver)); |
| pending_service_receivers_.clear(); |
| |
| if (GetContentClient()->gpu()) // Null in tests. |
| GetContentClient()->gpu()->GpuServiceInitialized(); |
| |
| // Start allowing browser-exposed interfaces to be bound. |
| // |
| // NOTE: Do not add new binders within this method. Instead modify |
| // |ExposeGpuInterfacesToBrowser()| in browser_exposed_gpu_interfaces.cc, as |
| // that will ensure security review coverage. |
| mojo::BinderMap binders; |
| content::ExposeGpuInterfacesToBrowser( |
| gpu_service->gpu_preferences(), |
| gpu_service->gpu_channel_manager()->gpu_driver_bug_workarounds(), |
| &binders); |
| ExposeInterfacesToBrowser(std::move(binders)); |
| } |
| |
| void GpuChildThread::PostCompositorThreadCreated( |
| base::SingleThreadTaskRunner* task_runner) { |
| auto* gpu_client = GetContentClient()->gpu(); |
| if (gpu_client) |
| gpu_client->PostCompositorThreadCreated(task_runner); |
| } |
| |
| void GpuChildThread::QuitMainMessageLoop() { |
| quit_closure_.Run(); |
| } |
| |
| void GpuChildThread::OnMemoryPressure( |
| base::MemoryPressureListener::MemoryPressureLevel level) { |
| if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) |
| return; |
| |
| if (viz_main_.discardable_shared_memory_manager()) |
| viz_main_.discardable_shared_memory_manager()->ReleaseFreeMemory(); |
| SkGraphics::PurgeAllCaches(); |
| } |
| |
| void GpuChildThread::QuitSafelyHelper( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| // Post a new task (even if we're called on the |task_runner|'s thread) to |
| // ensure that we are post-init. |
| task_runner->PostTask( |
| FROM_HERE, base::BindOnce([]() { |
| ChildThreadImpl* current_child_thread = ChildThreadImpl::current(); |
| if (!current_child_thread) |
| return; |
| GpuChildThread* gpu_child_thread = |
| static_cast<GpuChildThread*>(current_child_thread); |
| gpu_child_thread->viz_main_.ExitProcess( |
| viz::ExitCode::RESULT_CODE_NORMAL_EXIT); |
| })); |
| } |
| |
| // Returns a closure which calls into the VizMainImpl to perform shutdown |
| // before quitting the main message loop. Must be called on the main thread. |
| base::RepeatingClosure GpuChildThread::MakeQuitSafelyClosure() { |
| return base::BindRepeating(&GpuChildThread::QuitSafelyHelper, |
| base::SingleThreadTaskRunner::GetCurrentDefault()); |
| } |
| |
| #if BUILDFLAG(IS_ANDROID) |
| // static |
| std::unique_ptr<media::AndroidOverlay> GpuChildThread::CreateAndroidOverlay( |
| scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, |
| const base::UnguessableToken& routing_token, |
| media::AndroidOverlayConfig config) { |
| mojo::PendingRemote<media::mojom::AndroidOverlayProvider> overlay_provider; |
| if (main_task_runner->RunsTasksInCurrentSequence()) { |
| ChildThread::Get()->BindHostReceiver( |
| overlay_provider.InitWithNewPipeAndPassReceiver()); |
| } else { |
| main_task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| [](mojo::PendingReceiver<media::mojom::AndroidOverlayProvider> |
| receiver) { |
| ChildThread::Get()->BindHostReceiver(std::move(receiver)); |
| }, |
| overlay_provider.InitWithNewPipeAndPassReceiver())); |
| } |
| |
| return std::make_unique<media::MojoAndroidOverlay>( |
| std::move(overlay_provider), std::move(config), routing_token); |
| } |
| #endif |
| |
| } // namespace content |