| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/command_buffer/service/gr_cache_controller.h" |
| |
| #include <chrono> |
| |
| #include "base/functional/bind.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "gpu/command_buffer/service/shared_context_state.h" |
| #include "gpu/config/gpu_finch_features.h" |
| #include "gpu/ipc/common/gpu_client_ids.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| |
| namespace gpu { |
| namespace raster { |
| |
| GrCacheController::GrCacheController(SharedContextState* context_state) |
| : context_state_(context_state), |
| task_runner_(base::SingleThreadTaskRunner::HasCurrentDefault() |
| ? base::SingleThreadTaskRunner::GetCurrentDefault() |
| : nullptr) {} |
| |
| GrCacheController::GrCacheController( |
| SharedContextState* context_state, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : context_state_(context_state), task_runner_(std::move(task_runner)) {} |
| |
| GrCacheController::~GrCacheController() = default; |
| |
| void GrCacheController::ScheduleGrContextCleanup() { |
| DCHECK(context_state_->IsCurrent(nullptr)); |
| |
| if (!context_state_->gr_context()) |
| return; |
| |
| if (!task_runner_) { |
| // No way to estimate idle. Just periodically call PerformDeferredCleanup. |
| PerformDeferredCleanupThrottled(); |
| return; |
| } |
| |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| |
| current_idle_id_++; |
| if (!purge_gr_cache_cb_.IsCancelled()) |
| return; |
| |
| PerformDeferredCleanup(); |
| |
| constexpr int kIdleCleanupDelaySeconds = 1; |
| purge_gr_cache_cb_.Reset(base::BindOnce(&GrCacheController::PurgeGrCache, |
| base::Unretained(this), |
| current_idle_id_)); |
| task_runner_->PostDelayedTask(FROM_HERE, purge_gr_cache_cb_.callback(), |
| base::Seconds(kIdleCleanupDelaySeconds)); |
| } |
| |
| void GrCacheController::PerformDeferredCleanupThrottled() { |
| if (!base::FeatureList::IsEnabled( |
| features::kAggressiveSkiaGpuResourcePurge)) { |
| return; |
| } |
| constexpr base::TimeDelta kTimeout = base::Seconds(1); |
| base::TimeTicks now = base::TimeTicks::Now(); |
| if (now - last_cleanup_timestamp_ < kTimeout) { |
| return; |
| } |
| last_cleanup_timestamp_ = now; |
| PerformDeferredCleanup(); |
| } |
| |
| void GrCacheController::PerformDeferredCleanup() { |
| int old_resource_delay_seconds = 5; |
| if (base::FeatureList::IsEnabled(features::kAggressiveSkiaGpuResourcePurge)) { |
| old_resource_delay_seconds = 1; |
| } |
| // Here we ask GrContext to free any resources that haven't been used in |
| // a long while even if it is under budget. Below we set a call back to |
| // purge all possible GrContext resources if the context itself is not being |
| // used. |
| context_state_->set_need_context_state_reset(true); |
| context_state_->gr_context()->performDeferredCleanup( |
| std::chrono::seconds(old_resource_delay_seconds)); |
| } |
| |
| void GrCacheController::PurgeGrCache(uint64_t idle_id) { |
| purge_gr_cache_cb_.Cancel(); |
| |
| // We don't care which surface is current. This improves |
| // performance. https://crbug.com/457431 |
| if (!context_state_->MakeCurrent(nullptr)) |
| return; |
| |
| // If the idle id changed, the context was used after this callback was |
| // posted. Schedule another one. |
| if (idle_id != current_idle_id_) { |
| ScheduleGrContextCleanup(); |
| return; |
| } |
| |
| context_state_->set_need_context_state_reset(true); |
| |
| // Force Skia to check fences to determine what can be freed. |
| context_state_->gr_context()->checkAsyncWorkCompletion(); |
| { |
| absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; |
| // ScopedCacheUse is to avoid the empty/invalid client id DCHECKS caused |
| // while accessing GrShaderCache. Note that since the actual client_id here |
| // does not matter, we are using gpu::kDisplayCompositorClientId. |
| context_state_->UseShaderCache(cache_use, gpu::kDisplayCompositorClientId); |
| context_state_->gr_context()->freeGpuResources(); |
| } |
| |
| // Skia may have released resources, but the driver may not process that |
| // without a flush. |
| if (context_state_->GrContextIsGL()) { |
| auto* api = gl::g_current_gl_context; |
| api->glFlushFn(); |
| } |
| |
| // Skia store VkPipeline cache only on demand. We do it when we're idle idle |
| // as it might take time. |
| context_state_->StoreVkPipelineCacheIfNeeded(); |
| } |
| |
| } // namespace raster |
| } // namespace gpu |