| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/service/display_embedder/compositor_gpu_thread.h" |
| |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/bind_post_task.h" |
| #include "build/build_config.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/gpu/vulkan_context_provider.h" |
| #include "components/viz/common/switches.h" |
| #include "gpu/command_buffer/service/feature_info.h" |
| #include "gpu/config/gpu_finch_features.h" |
| #include "gpu/ipc/common/gpu_client_ids.h" |
| #include "gpu/ipc/service/gpu_channel_manager.h" |
| #include "gpu/vulkan/buildflags.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_features.h" |
| #include "ui/gl/gl_surface_egl.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| #if BUILDFLAG(ENABLE_VULKAN) |
| #include "components/viz/common/gpu/vulkan_in_process_context_provider.h" |
| #include "gpu/vulkan/vulkan_device_queue.h" |
| #include "gpu/vulkan/vulkan_implementation.h" |
| #endif |
| |
| #if BUILDFLAG(SKIA_USE_DAWN) |
| #include "gpu/command_buffer/service/dawn_context_provider.h" |
| #endif |
| |
| namespace viz { |
| |
| // static |
| std::unique_ptr<CompositorGpuThread> CompositorGpuThread::Create( |
| const CreateParams& params) { |
| DCHECK(params.gpu_channel_manager); |
| |
| #if DCHECK_IS_ON() |
| #if BUILDFLAG(IS_ANDROID) |
| // When using angle via enabling passthrough command decoder on android, angle |
| // context virtualization group extension should be enabled. Also since angle |
| // currently always enables this extension with GL backend, we are adding |
| // DCHECK() to ensure that instead of enabling/disabling DrDc based on the |
| // extension. |
| if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE && |
| gl::GetANGLEImplementation() == gl::ANGLEImplementation::kOpenGLES) { |
| gl::GLDisplayEGL* display_egl = params.display->GetAs<gl::GLDisplayEGL>(); |
| DCHECK(display_egl->ext->b_EGL_ANGLE_context_virtualization); |
| } |
| #endif |
| #endif // DCHECK_IS_ON() |
| |
| auto compositor_gpu_thread = base::WrapUnique(new CompositorGpuThread( |
| params.gpu_channel_manager, params.display, params.enable_watchdog)); |
| |
| #if BUILDFLAG(ENABLE_VULKAN) |
| // Create a VulkanContextProvider. |
| if (params.vulkan_implementation && params.device_queue) { |
| auto* device_queue = params.device_queue.get(); |
| auto compositor_thread_device_queue = |
| std::make_unique<gpu::VulkanDeviceQueue>( |
| device_queue->GetVulkanInstance()); |
| compositor_thread_device_queue->InitializeForCompositorGpuThread( |
| device_queue->GetVulkanPhysicalDevice(), |
| device_queue->GetVulkanDevice(), device_queue->GetVulkanQueue(), |
| device_queue->GetVulkanQueueLockContext(), |
| device_queue->GetVulkanQueueIndex(), device_queue->enabled_extensions(), |
| device_queue->enabled_device_features_2(), |
| device_queue->vma_allocator()); |
| compositor_gpu_thread->vulkan_context_provider_ = |
| VulkanInProcessContextProvider::CreateForCompositorGpuThread( |
| params.vulkan_implementation, |
| std::move(compositor_thread_device_queue), |
| params.gpu_channel_manager->gpu_preferences() |
| .vulkan_sync_cpu_memory_limit); |
| } |
| #endif |
| |
| #if BUILDFLAG(SKIA_USE_DAWN) |
| if (params.gpu_channel_manager->gpu_preferences().gr_context_type == |
| gpu::GrContextType::kGraphiteDawn) { |
| compositor_gpu_thread->dawn_context_provider_ = |
| gpu::DawnContextProvider::CreateWithSharedDevice( |
| params.dawn_context_provider); |
| } |
| #endif |
| |
| if (!compositor_gpu_thread->Initialize()) { |
| return nullptr; |
| } |
| return compositor_gpu_thread; |
| } |
| |
| CompositorGpuThread::CompositorGpuThread( |
| gpu::GpuChannelManager* gpu_channel_manager, |
| gl::GLDisplay* display, |
| bool enable_watchdog) |
| : base::Thread("CompositorGpuThread"), |
| gpu_channel_manager_(gpu_channel_manager), |
| enable_watchdog_(enable_watchdog), |
| display_(display), |
| weak_ptr_factory_(this) {} |
| |
| CompositorGpuThread::~CompositorGpuThread() { |
| base::Thread::Stop(); |
| } |
| |
| scoped_refptr<gpu::SharedContextState> |
| CompositorGpuThread::GetSharedContextState() { |
| DCHECK(task_runner()->BelongsToCurrentThread()); |
| |
| if (shared_context_state_ && !shared_context_state_->context_lost()) |
| return shared_context_state_; |
| |
| // Cleanup the previous context if any. |
| shared_context_state_.reset(); |
| |
| // Create a new share group. Note that this share group is different from the |
| // share group which gpu main thread uses. |
| auto share_group = base::MakeRefCounted<gl::GLShareGroup>(); |
| auto surface = gl::init::CreateOffscreenGLSurface(display_, gfx::Size()); |
| |
| const auto& gpu_preferences = gpu_channel_manager_->gpu_preferences(); |
| |
| const bool use_passthrough_decoder = |
| gpu_preferences.use_passthrough_cmd_decoder; |
| gl::GLContextAttribs attribs = |
| gpu::gles2::GenerateGLContextAttribsForCompositor( |
| use_passthrough_decoder); |
| attribs.angle_context_virtualization_group_number = |
| gl::AngleContextVirtualizationGroup::kDrDc; |
| |
| attribs.can_skip_validation = !features::IsANGLEValidationEnabled(); |
| |
| // Compositor thread context doesn't need access textures and semaphores |
| // created with other contexts. |
| attribs.global_texture_share_group = false; |
| attribs.global_semaphore_share_group = false; |
| |
| // Create a new gl context. Note that this gl context is not part of same |
| // share group which gpu main thread uses. Hence this context does not share |
| // GL resources with the contexts created on gpu main thread. |
| auto context = |
| gl::init::CreateGLContext(share_group.get(), surface.get(), attribs); |
| if (!context) { |
| LOG(ERROR) << "Failed to create shared context"; |
| return nullptr; |
| } |
| |
| const auto& gpu_feature_info = gpu_channel_manager_->gpu_feature_info(); |
| gpu_feature_info.ApplyToGLContext(context.get()); |
| |
| if (!context->MakeCurrent(surface.get())) { |
| LOG(ERROR) << "Failed to make context current"; |
| return nullptr; |
| } |
| |
| const auto& workarounds = gpu_channel_manager_->gpu_driver_bug_workarounds(); |
| |
| // Create a SharedContextState. |
| auto shared_context_state = base::MakeRefCounted<gpu::SharedContextState>( |
| std::move(share_group), std::move(surface), std::move(context), |
| /*use_virtualized_gl_contexts=*/false, |
| gpu_channel_manager_->GetContextLostCallback(), |
| gpu_preferences.gr_context_type, |
| #if BUILDFLAG(ENABLE_VULKAN) |
| vulkan_context_provider_.get(), |
| #else |
| /*vulkan_context_provider=*/nullptr, |
| #endif |
| /*metal_context_provider=*/nullptr, |
| #if BUILDFLAG(SKIA_USE_DAWN) |
| dawn_context_provider_.get(), |
| #else |
| /*dawn_context_provider=*/nullptr, |
| #endif |
| /*peak_memory_monitor=*/ |
| gpu_channel_manager_->peak_memory_monitor(), |
| /*direct_rendering_display_compositor_enabled=*/true, |
| /*created_on_compositor_gpu_thread=*/true); |
| |
| auto gles2_feature_info = base::MakeRefCounted<gpu::gles2::FeatureInfo>( |
| workarounds, gpu_feature_info); |
| |
| // Initialize GL. |
| if (!shared_context_state->InitializeGL(gpu_preferences, |
| std::move(gles2_feature_info))) { |
| LOG(ERROR) << "Failed to initialize GL for DrDC SharedContextState"; |
| return nullptr; |
| } |
| |
| // Initialize Skia. |
| if (!shared_context_state->InitializeSkia( |
| gpu_preferences, workarounds, gpu_channel_manager_->gr_shader_cache(), |
| gpu_channel_manager_->use_shader_cache_shm_count(), |
| /*progress_reporter=*/nullptr)) { |
| LOG(ERROR) << "Failed to Initialize Skia for DrDC SharedContextState"; |
| } |
| shared_context_state_ = std::move(shared_context_state); |
| return shared_context_state_; |
| } |
| |
| bool CompositorGpuThread::Initialize() { |
| // Setup thread options. |
| base::Thread::Options thread_options(base::MessagePumpType::DEFAULT, 0); |
| thread_options.thread_type = base::ThreadType::kDisplayCritical; |
| |
| #if BUILDFLAG(IS_MAC) |
| thread_options.message_pump_type = base::MessagePumpType::NS_RUNLOOP; |
| |
| // Note: The WorkBatchSize is different from GpuMain thread set. Revisit the |
| // following code if any regression is found. See GpuMain() and |
| // crbug.com/40668161. |
| // std::unique_ptr<base::SingleThreadTaskExecutor> thread_task_executor; |
| // thread_task_executor->SetWorkBatchSize(2); |
| #endif |
| StartWithOptions(std::move(thread_options)); |
| |
| // Wait until thread is started and Init() is executed in order to return |
| // updated |init_succeeded_|. |
| WaitUntilThreadStarted(); |
| return init_succeeded_; |
| } |
| |
| void CompositorGpuThread::OnMemoryPressure( |
| base::MemoryPressureLevel memory_pressure_level) { |
| DCHECK(task_runner()->BelongsToCurrentThread()); |
| |
| // Context should be current for cache/memory cleanup. |
| if (shared_context_state_ && |
| shared_context_state_->MakeCurrent(nullptr, /*needs_gl=*/true)) { |
| shared_context_state_->PurgeMemory(memory_pressure_level); |
| } |
| } |
| |
| void CompositorGpuThread::Init() { |
| const auto& gpu_preferences = gpu_channel_manager_->gpu_preferences(); |
| if (enable_watchdog_ && gpu_channel_manager_->watchdog()) { |
| watchdog_thread_ = gpu::GpuWatchdogThread::Create( |
| gpu_preferences.watchdog_starts_backgrounded, |
| gpu_channel_manager_->watchdog(), "GpuWatchdog_Compositor"); |
| watchdog_thread_->OnInitComplete(); |
| } |
| |
| // Making sure to create the |memory_pressure_listener_| on |
| // CompositorGpuThread since this callback will be called on the thread it was |
| // created on. |
| memory_pressure_listener_registration_ = |
| std::make_unique<base::AsyncMemoryPressureListenerRegistration>( |
| FROM_HERE, base::MemoryPressureListenerTag::kCompositorGpuThread, |
| this), |
| init_succeeded_ = true; |
| } |
| |
| void CompositorGpuThread::CleanUp() { |
| // Destroying |memory_pressure_listener_| here to ensure its destroyed on the |
| // same thread on which it was created on. |
| memory_pressure_listener_registration_.reset(); |
| if (watchdog_thread_) |
| watchdog_thread_->OnGpuProcessTearDown(); |
| |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| if (shared_context_state_) { |
| shared_context_state_->MakeCurrent(nullptr); |
| shared_context_state_ = nullptr; |
| } |
| |
| // WatchDogThread destruction should happen on the CompositorGpuThread. |
| watchdog_thread_.reset(); |
| } |
| |
| void CompositorGpuThread::OnBackgrounded() { |
| if (watchdog_thread_) |
| watchdog_thread_->OnBackgrounded(); |
| |
| task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CompositorGpuThread::OnBackgroundedOnCompositorGpuThread, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void CompositorGpuThread::OnBackgroundedOnCompositorGpuThread() { |
| DCHECK(task_runner()->BelongsToCurrentThread()); |
| |
| if (shared_context_state_) { |
| shared_context_state_->PurgeMemory(base::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| } |
| } |
| |
| void CompositorGpuThread::OnBackgroundCleanup() { |
| LoseContext(); |
| } |
| |
| void CompositorGpuThread::OnForegrounded() { |
| if (watchdog_thread_) |
| watchdog_thread_->OnForegrounded(); |
| } |
| |
| void CompositorGpuThread::LoseContext() { |
| if (!task_runner()->BelongsToCurrentThread()) { |
| task_runner()->PostTask(FROM_HERE, |
| base::BindOnce(&CompositorGpuThread::LoseContext, |
| weak_ptr_factory_.GetWeakPtr())); |
| return; |
| } |
| |
| if (shared_context_state_) { |
| shared_context_state_->MarkContextLost(); |
| shared_context_state_.reset(); |
| } |
| } |
| |
| void CompositorGpuThread::AddVideoMemoryUsageStatsOnCompositorGpu( |
| GetVideoMemoryUsageStatsCallback callback, |
| gpu::VideoMemoryUsageStats video_memory_usage_stats) { |
| if (!task_runner()->BelongsToCurrentThread()) { |
| task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &CompositorGpuThread::AddVideoMemoryUsageStatsOnCompositorGpu, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback), |
| video_memory_usage_stats)); |
| return; |
| } |
| |
| uint64_t size = GetSharedContextState()->GetMemoryUsage(); |
| video_memory_usage_stats.process_map[base::GetCurrentProcId()].video_memory += |
| size; |
| video_memory_usage_stats.bytes_allocated += size; |
| std::move(callback).Run(video_memory_usage_stats); |
| } |
| |
| } // namespace viz |