From 8b9840b32f350455dbfe59542e4fc2c3a88f5351 Mon Sep 17 00:00:00 2001 From: v00863305 Date: Wed, 10 Jan 2024 14:00:33 +0300 Subject: [PATCH 1/3] Customize unlocked frame culling Fine-granted reduction of GPU texture memory consumption by browser application with multiple offscreen frames (tabs), could be achieved by using of custom frame eviction limits (periodic delay, explicit count limit) which provides possibility to release tile GPU backing resources (shared images) from evicted Viz surfaces (compositor frames). - periodic delay (5 mins) - enabled by default - explicit count limit (10 frames) - disabled by default To configure custom frame eviction please use the following features: --enabled-features=AggressiveFrameCulling:delay/5m, ExplicitFrameCullingLimit:max-count/10 Signed-off-by: Volykhin Andrei Change-Id: I6505f81b335d13edc146a26505805b04524658d5 --- components/viz/client/frame_eviction_manager.cc | 11 +++++++++-- components/viz/common/features.cc | 11 +++++++++++ components/viz/common/features.h | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/components/viz/client/frame_eviction_manager.cc b/components/viz/client/frame_eviction_manager.cc index bc70b77c66..8a722b997b 100644 --- a/components/viz/client/frame_eviction_manager.cc +++ b/components/viz/client/frame_eviction_manager.cc @@ -105,7 +105,7 @@ void FrameEvictionManager::RegisterUnlockedFrame( // Unretained: `idle_frames_culling_timer_` is a member of `this`, doesn't // outlive it, and cancels the task in its destructor. idle_frames_culling_timer_.Start( - FROM_HERE, kPeriodicCullingDelay, + FROM_HERE, features::kAggressiveFrameCullingDelay.Get(), base::BindRepeating(&FrameEvictionManager::CullOldUnlockedFrames, base::Unretained(this))); } @@ -161,7 +161,13 @@ FrameEvictionManager::FrameEvictionManager() switches::kMaxNumberOfSavedFrames)) { max_number_of_saved_frames_ = kMaxNumberOfSavedFrames; } +#endif +#if BUILDFLAG(IS_OHOS) + if (base::FeatureList::IsEnabled(features::kExplicitFrameCullingLimit)) { + max_number_of_saved_frames_ = + std::max(1, features::kExplicitFrameCullingLimitMaxCount.Get()); + } #endif } @@ -193,7 +199,8 @@ void FrameEvictionManager::CullOldUnlockedFrames() { auto now = clock_->NowTicks(); while (!unlocked_frames_.empty() && - now - unlocked_frames_.back().second >= kPeriodicCullingDelay) { + now - unlocked_frames_.back().second >= + features::kAggressiveFrameCullingDelay.Get()) { size_t old_size = unlocked_frames_.size(); auto* frame = unlocked_frames_.back().first; frame->EvictCurrentFrame(); diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index ebc18015f2..93fdfe7c76 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc @@ -197,6 +197,8 @@ BASE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip, BASE_FEATURE(kAggressiveFrameCulling, "AggressiveFrameCulling", base::FEATURE_ENABLED_BY_DEFAULT); +const base::FeatureParam kAggressiveFrameCullingDelay{ + &kAggressiveFrameCulling, "delay", base::Minutes(5)}; // If enabled, do not rely on surface garbage collection to happen // periodically, but trigger it eagerly, to avoid missing calls. @@ -259,6 +261,15 @@ BASE_FEATURE(kOnBeginFrameAllowLateAcks, "OnBeginFrameAllowLateAcks", base::FEATURE_DISABLED_BY_DEFAULT); +#if BUILDFLAG(IS_OHOS) +// If enabled, define explicit culling limit for *all* frames. +BASE_FEATURE(kExplicitFrameCullingLimit, + "ExplicitFrameCullingLimit", + base::FEATURE_DISABLED_BY_DEFAULT); +const base::FeatureParam kExplicitFrameCullingLimitMaxCount{ + &kExplicitFrameCullingLimit, "max-count", 10}; +#endif + bool IsDelegatedCompositingEnabled() { return base::FeatureList::IsEnabled(kDelegatedCompositing); } diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 2fc330a441..0fb69a339d 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h @@ -59,6 +59,8 @@ VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kDrawPredictedInkPoint); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAllowBypassRenderPassQuads); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAggressiveFrameCulling); +VIZ_COMMON_EXPORT extern const base::FeatureParam + kAggressiveFrameCullingDelay; VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEagerSurfaceGarbageCollection); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOverrideThrottledFrameRateParams); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kRendererAllocatesImages); @@ -67,6 +69,12 @@ VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEvictSubtree); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOnBeginFrameAcks); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOnBeginFrameAllowLateAcks); +#if BUILDFLAG(IS_OHOS) +VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kExplicitFrameCullingLimit); +VIZ_COMMON_EXPORT extern const base::FeatureParam + kExplicitFrameCullingLimitMaxCount; +#endif + VIZ_COMMON_EXPORT extern const char kDraw1Point12Ms[]; VIZ_COMMON_EXPORT extern const char kDraw2Points6Ms[]; VIZ_COMMON_EXPORT extern const char kDraw1Point6Ms[]; -- Gitee From e4af894d407f5df91841da7553233113659767f1 Mon Sep 17 00:00:00 2001 From: v00863305 Date: Fri, 2 Feb 2024 09:50:31 +0300 Subject: [PATCH 2/3] First frame activation deadline Allow to use the specified deadline policy on the first frame drawing (show with visibility) to synchronize web content with browser UI. Having HarmonyOS display a placeholder (optional) for a longer period of time is preferable to drawing nothing, and the first frame can take a while on low-end systems. --enable-features=FirstFrameActivationDeadline OHOS external begin frame source should adjust MISSED begin frame arguments to the current time otherwise surface activation deadline will be resolved incorrectly. VSync (MISSED) -> ....do nothing ... -> NeedsBeginFrame -> BeginFrame (with old MISSED begin frame args) -> ... -> CommitFrame (deadline resolved against old MISSED begin frame args) Signed-off-by: Volykhin Andrei Change-Id: I1cad430d838cff67ed735e2af2e901b68a4f2725 --- components/viz/common/features.cc | 8 +++++++ components/viz/common/features.h | 3 +++ .../common/frame_sinks/begin_frame_source.cc | 7 +++--- .../external_begin_frame_source_ohos.cc | 24 +++++++++++++++++++ .../external_begin_frame_source_ohos.h | 3 +++ components/viz/service/surfaces/surface.cc | 4 ++++ .../renderer_host/delegated_frame_host.cc | 21 ++++++++++++++++ 7 files changed, 66 insertions(+), 4 deletions(-) diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 93fdfe7c76..8b1908aab7 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc @@ -268,6 +268,14 @@ BASE_FEATURE(kExplicitFrameCullingLimit, base::FEATURE_DISABLED_BY_DEFAULT); const base::FeatureParam kExplicitFrameCullingLimitMaxCount{ &kExplicitFrameCullingLimit, "max-count", 10}; + +// If enabled, wait to activate a surface with dependencies on the first frame +// drawing with specified deadline. +BASE_FEATURE(kFirstFrameActivationDeadline, + "FirstFrameActivationDeadline", + base::FEATURE_ENABLED_BY_DEFAULT); +const base::FeatureParam kFirstFrameActivationDeadlineTimeout{ + &kFirstFrameActivationDeadline, "timeout", base::Seconds(5)}; #endif bool IsDelegatedCompositingEnabled() { diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 0fb69a339d..15b6f52c1a 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h @@ -73,6 +73,9 @@ VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kOnBeginFrameAllowLateAcks); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kExplicitFrameCullingLimit); VIZ_COMMON_EXPORT extern const base::FeatureParam kExplicitFrameCullingLimitMaxCount; +VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kFirstFrameActivationDeadline); +VIZ_COMMON_EXPORT extern const base::FeatureParam + kFirstFrameActivationDeadlineTimeout; #endif VIZ_COMMON_EXPORT extern const char kDraw1Point12Ms[]; diff --git a/components/viz/common/frame_sinks/begin_frame_source.cc b/components/viz/common/frame_sinks/begin_frame_source.cc index 2a33b47794..ac92c8bbcc 100644 --- a/components/viz/common/frame_sinks/begin_frame_source.cc +++ b/components/viz/common/frame_sinks/begin_frame_source.cc @@ -491,10 +491,9 @@ void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { return; } - TRACE_EVENT2( - "viz", "ExternalBeginFrameSource::OnBeginFrame", "frame_time", - last_begin_frame_args_.frame_time.since_origin().InMicroseconds(), - "interval", last_begin_frame_args_.interval.InMicroseconds()); + TRACE_EVENT2("viz", "ExternalBeginFrameSource::OnBeginFrame", "frame_time", + args.frame_time.since_origin().InMicroseconds(), "interval", + args.interval.InMicroseconds()); last_begin_frame_args_ = args; base::flat_set observers(observers_); diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_ohos.cc b/components/viz/service/frame_sinks/external_begin_frame_source_ohos.cc index d37717a485..3ce2f59504 100644 --- a/components/viz/service/frame_sinks/external_begin_frame_source_ohos.cc +++ b/components/viz/service/frame_sinks/external_begin_frame_source_ohos.cc @@ -229,6 +229,30 @@ ReportLossFrame::GetInstance()->SetVsyncPeriod(vsync_period_); } } +BeginFrameArgs ExternalBeginFrameSourceOHOS::GetMissedBeginFrameArgs( + BeginFrameObserver* obs) { + auto frame_time = last_begin_frame_args_.frame_time; + auto interval = last_begin_frame_args_.interval; + auto now = base::TimeTicks::Now(); + + if (last_begin_frame_args_.IsValid()) { + frame_time = now.SnappedToNextTick(frame_time, interval) - interval; + } else { + // Create BeginFrameArgs for now so that we don't have to wait until vsync. + frame_time = now; + interval = BeginFrameArgs::DefaultInterval(); + } + + // Don't create new args unless we've actually moved past the previous frame. + if (!last_begin_frame_args_.IsValid() || + frame_time > last_begin_frame_args_.frame_time) { + last_begin_frame_args_ = begin_frame_args_generator_.GenerateBeginFrameArgs( + source_id(), frame_time, frame_time + interval, interval); + } + + return ExternalBeginFrameSource::GetMissedBeginFrameArgs(obs); +} + void ExternalBeginFrameSourceOHOS::OnNeedsBeginFrames(bool needs_begin_frames) { SetEnabled(needs_begin_frames); } diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_ohos.h b/components/viz/service/frame_sinks/external_begin_frame_source_ohos.h index e56bf67ed6..7cb1304634 100644 --- a/components/viz/service/frame_sinks/external_begin_frame_source_ohos.h +++ b/components/viz/service/frame_sinks/external_begin_frame_source_ohos.h @@ -63,6 +63,9 @@ class VIZ_SERVICE_EXPORT ExternalBeginFrameSourceOHOS void ResetVSyncFrequency() override; private: + // ExternalBeginFrameSource overrides. + BeginFrameArgs GetMissedBeginFrameArgs(BeginFrameObserver* obs) override; + // ExternalBeginFrameSourceClient implementation. void OnNeedsBeginFrames(bool needs_begin_frames) override; diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc index 499c903155..e7282a7b0e 100644 --- a/components/viz/service/surfaces/surface.cc +++ b/components/viz/service/surfaces/surface.cc @@ -354,6 +354,10 @@ void Surface::OnActivationDependencyResolved( blocking_allocation_groups_.erase(group); if (!activation_dependencies_.empty()) return; + + TRACE_EVENT_NESTABLE_ASYNC_END0("viz", "SurfaceQueuedPending", + TRACE_ID_LOCAL(this)); + // All blockers have been cleared. The surface can be activated now. ActivatePendingFrame(); } diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc index 65766e9570..7b5062a283 100644 --- a/content/browser/renderer_host/delegated_frame_host.cc +++ b/content/browser/renderer_host/delegated_frame_host.cc @@ -39,6 +39,21 @@ namespace { // factor. constexpr float kFrameContentCaptureQuality = 0.4f; +#if BUILDFLAG(IS_OHOS) +cc::DeadlinePolicy FirstFrameDeadlinePolicy() { + if (base::FeatureList::IsEnabled( + features::kFirstFrameActivationDeadline)) { + // Wait up to deadline timeout for the first frame to be produced. + int64_t deadline_in_frames = base::ClampRound( + features::kFirstFrameActivationDeadlineTimeout.Get() / + viz::BeginFrameArgs::DefaultInterval()); + return cc::DeadlinePolicy::UseSpecifiedDeadline(deadline_in_frames); + } + + return cc::DeadlinePolicy::UseDefaultDeadline(); +} +#endif + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -96,10 +111,16 @@ void DelegatedFrameHost::WasShown( std::move(record_tab_switch_time_request))); } +#if BUILDFLAG(IS_OHOS) + // Use the specified deadline to synchronize web content with browser UI. + EmbedSurface( + new_local_surface_id, new_dip_size, FirstFrameDeadlinePolicy()); +#else // Use the default deadline to synchronize web content with browser UI. // TODO(fsamuel): Investigate if there is a better deadline to use here. EmbedSurface(new_local_surface_id, new_dip_size, cc::DeadlinePolicy::UseDefaultDeadline()); +#endif // Remove stale content that might be displayed. if (stale_content_layer_->has_external_content()) { -- Gitee From 5b93a584507decb793d22aa4597fcb3de7537cc2 Mon Sep 17 00:00:00 2001 From: v00863305 Date: Tue, 6 Feb 2024 16:52:30 +0300 Subject: [PATCH 3/3] Allow UI compositor root surface eviction Allows to evict root surface (UI compositor) by submission empty local compositor frame to unref resources associated with previous presented aggregated frame from viz compositor (including web contents resources from referenced surfaces to embedded renderers which are marked for destruction). Option "preserve-output-content" will allow to preserve the latest drawn content on output surface on view hide. On chromium level (enabled by default): --enabled-features=EvictRootSurface:preserve-output-content/true On system level (enabled by default): persist.web.root_surface_eviction.enable true Related upstream issue: https://issues.chromium.org/issues/40273186 Signed-off-by: Volykhin Andrei Change-Id: Idf5d4d25b86788f5600e9ff2c5afa0bcfa245d87 --- components/viz/common/features.cc | 7 ++ components/viz/common/features.h | 3 + .../common/quads/compositor_frame_metadata.h | 12 +- .../viz/service/display/direct_renderer.cc | 6 + .../viz/service/display/direct_renderer.h | 8 ++ components/viz/service/display/display.cc | 11 ++ components/viz/service/display/display.h | 13 +++ .../viz/service/display/display_unittest.cc | 3 +- .../viz/service/display/null_renderer.h | 3 + .../viz/service/display/skia_renderer.cc | 30 +++++ .../viz/service/display/skia_renderer.h | 3 + .../viz/service/display/software_renderer.cc | 6 + .../viz/service/display/software_renderer.h | 3 + .../compositor_frame_sink_support.cc | 58 ++++++++-- .../compositor_frame_sink_support.h | 25 +++- .../compositor_frame_sink_support_unittest.cc | 9 +- .../frame_sinks/frame_sink_manager_impl.cc | 18 ++- .../frame_sink_manager_unittest.cc | 6 + .../root_compositor_frame_sink_impl.cc | 108 +++++++++++++++++- .../root_compositor_frame_sink_impl.h | 8 +- components/viz/service/surfaces/surface.cc | 5 +- .../viz/service/surfaces/surface_unittest.cc | 4 +- .../viz/test/fake_host_frame_sink_client.h | 3 +- .../renderer_host/delegated_frame_host.cc | 20 ++++ .../renderer_host/delegated_frame_host.h | 4 + 25 files changed, 336 insertions(+), 40 deletions(-) diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 8b1908aab7..740acd9e25 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc @@ -276,6 +276,13 @@ BASE_FEATURE(kFirstFrameActivationDeadline, base::FEATURE_ENABLED_BY_DEFAULT); const base::FeatureParam kFirstFrameActivationDeadlineTimeout{ &kFirstFrameActivationDeadline, "timeout", base::Seconds(5)}; + +// If enabled, should include the root surface for eviction. +BASE_FEATURE(kEvictRootSurface, + "EvictRootSurface", + base::FEATURE_ENABLED_BY_DEFAULT); +const base::FeatureParam kPreserveOutputContentOnEvictRootSurface{ + &kEvictRootSurface, "preserve-output-content", true}; #endif bool IsDelegatedCompositingEnabled() { diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 15b6f52c1a..33d3749218 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h @@ -76,6 +76,9 @@ VIZ_COMMON_EXPORT extern const base::FeatureParam VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kFirstFrameActivationDeadline); VIZ_COMMON_EXPORT extern const base::FeatureParam kFirstFrameActivationDeadlineTimeout; +VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kEvictRootSurface); +VIZ_COMMON_EXPORT extern const base::FeatureParam + kPreserveOutputContentOnEvictRootSurface; #endif VIZ_COMMON_EXPORT extern const char kDraw1Point12Ms[]; diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h index 98dc3d2da9..a81bfdb936 100644 --- a/components/viz/common/quads/compositor_frame_metadata.h +++ b/components/viz/common/quads/compositor_frame_metadata.h @@ -35,6 +35,11 @@ namespace viz { +// A frame token value of 0 indicates an invalid or local frame token. A +// local frame token is used inside viz when it creates its own CompositorFrame +// for a surface. +inline constexpr uint32_t kInvalidOrLocalFrameToken = 0; + // Compares two frame tokens, handling cases where the token wraps around the // 32-bit max value. inline bool FrameTokenGT(uint32_t token1, uint32_t token2) { @@ -46,15 +51,16 @@ inline bool FrameTokenGT(uint32_t token1, uint32_t token2) { class VIZ_COMMON_EXPORT FrameTokenGenerator { public: inline uint32_t operator++() { - if (++frame_token_ == 0) + if (++frame_token_ == kInvalidOrLocalFrameToken) { ++frame_token_; + } return frame_token_; } inline uint32_t operator*() const { return frame_token_; } private: - uint32_t frame_token_ = 0; + uint32_t frame_token_ = kInvalidOrLocalFrameToken; }; class VIZ_COMMON_EXPORT CompositorFrameMetadata { @@ -143,7 +149,7 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata { // after the 32-bit max value. // TODO(crbug.com/850386): A custom type would be better to avoid incorrect // comparisons. - uint32_t frame_token = 0; + uint32_t frame_token = kInvalidOrLocalFrameToken; // Once the display compositor processes a frame with // |send_frame_token_to_embedder| flag turned on, the |frame_token| for the diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc index 9f6a5f6eda..3869ec1530 100644 --- a/components/viz/service/display/direct_renderer.cc +++ b/components/viz/service/display/direct_renderer.cc @@ -1171,4 +1171,10 @@ gpu::Mailbox DirectRenderer::GetPrimaryPlaneOverlayTestingMailbox() { return gpu::Mailbox(); } +#if BUILDFLAG(IS_OHOS) +void DirectRenderer::EvictRenderPassAllocations() { + RemoveRenderPassResources(); +} +#endif + } // namespace viz diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h index 93ea1359d1..5305ad0dfd 100644 --- a/components/viz/service/display/direct_renderer.h +++ b/components/viz/service/display/direct_renderer.h @@ -199,6 +199,11 @@ class VIZ_SERVICE_EXPORT DirectRenderer { // Return the bounding rect of previously drawn delegated ink trail. gfx::Rect GetDelegatedInkTrailDamageRect(); +#if BUILDFLAG(IS_OHOS) + // Evicts allocated resources for render passes. + void EvictRenderPassAllocations(); +#endif + protected: friend class BspWalkActionDrawPolygon; friend class SkiaDelegatedInkRendererTest; @@ -279,6 +284,9 @@ class VIZ_SERVICE_EXPORT DirectRenderer { virtual void AllocateRenderPassResourceIfNeeded( const AggregatedRenderPassId& render_pass_id, const RenderPassRequirements& requirements) = 0; +#if BUILDFLAG(IS_OHOS) + virtual void RemoveRenderPassResources() = 0; +#endif virtual bool IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const = 0; virtual gfx::Size GetRenderPassBackingPixelSize( diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc index 288c27e59d..eacf72a518 100644 --- a/components/viz/service/display/display.cc +++ b/components/viz/service/display/display.cc @@ -960,6 +960,10 @@ bool Display::DrawAndSwap(const DrawAndSwapParams& params) { bool should_draw = have_copy_requests || (have_damage && size_matches); client_->DisplayWillDrawAndSwap(should_draw, &frame.render_pass_list); +#if BUILDFLAG(IS_OHOS) + should_draw &= !skip_draw_and_evict_resources_on_current_frame_; +#endif + absl::optional draw_timer; if (should_draw) { TRACE_EVENT_ASYNC_STEP_INTO0("viz,benchmark", @@ -1009,6 +1013,13 @@ bool Display::DrawAndSwap(const DrawAndSwapParams& params) { std::move(frame.surface_damage_rect_list_)); } else { TRACE_EVENT_INSTANT0("viz", "Draw skipped.", TRACE_EVENT_SCOPE_THREAD); + +#if BUILDFLAG(IS_OHOS) + if (skip_draw_and_evict_resources_on_current_frame_) { + skip_draw_and_evict_resources_on_current_frame_ = false; + renderer_->EvictRenderPassAllocations(); + } +#endif } bool should_swap = !disable_swap_until_resize_ && should_draw && size_matches; diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h index 0b39f0e762..986c3bfd5b 100644 --- a/components/viz/service/display/display.h +++ b/components/viz/service/display/display.h @@ -147,6 +147,13 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient, // may be run immediately. void DisableSwapUntilResize(base::OnceClosure no_pending_swaps_callback); +#if BUILDFLAG(IS_OHOS) + // Stop drawing the current frame and evict the allocated resources. + void SetSkipDrawAndEvictResourcesOnCurrentFrame() { + skip_draw_and_evict_resources_on_current_frame_ = true; + } +#endif + #if defined(OHOS_COMPOSITE_RENDER) void SetShouldFrameSubmissionBeforeDraw(bool should); void SetDrawRect(const gfx::Rect& new_rect); @@ -196,6 +203,8 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient, mojom::CompositorFrameSinkType* type) override; bool has_scheduler() const { return !!scheduler_; } + bool visible() const { return visible_; } + const RendererSettings& settings() const { return settings_; } DirectRenderer* renderer_for_testing() const { return renderer_.get(); } bool resize_based_on_root_surface() const { @@ -340,6 +349,10 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient, bool disable_swap_until_resize_ = true; +#if BUILDFLAG(IS_OHOS) + bool skip_draw_and_evict_resources_on_current_frame_ = false; +#endif + // Callback that will be run after all pending swaps have acked. base::OnceClosure no_pending_swaps_callback_; diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc index 70cb6c821f..2eb5999226 100644 --- a/components/viz/service/display/display_unittest.cc +++ b/components/viz/service/display/display_unittest.cc @@ -3432,7 +3432,8 @@ TEST_P(OnBeginFrameAcksDisplayTest, CompositorFrameWithPresentationToken) { display_->Resize(display_size); const gfx::Size sub_surface_size(32, 32); - uint32_t frame_token_1 = 0, frame_token_2 = 0; + uint32_t frame_token_1 = kInvalidOrLocalFrameToken; + uint32_t frame_token_2 = kInvalidOrLocalFrameToken; { CompositorFrame frame = CompositorFrameBuilder() diff --git a/components/viz/service/display/null_renderer.h b/components/viz/service/display/null_renderer.h index faa9c2d9a9..b57ad3560d 100644 --- a/components/viz/service/display/null_renderer.h +++ b/components/viz/service/display/null_renderer.h @@ -32,6 +32,9 @@ class VIZ_SERVICE_EXPORT NullRenderer : public DirectRenderer { void AllocateRenderPassResourceIfNeeded( const AggregatedRenderPassId& render_pass_id, const RenderPassRequirements& requirements) override {} +#if BUILDFLAG(IS_OHOS) + void RemoveRenderPassResources() override {} +#endif bool IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const override; gfx::Size GetRenderPassBackingPixelSize( diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 933e26ac7d..b8293061be 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc @@ -3295,6 +3295,36 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded( requirements.scanout_dcomp_surface})); } +#if BUILDFLAG(IS_OHOS) +void SkiaRenderer::RemoveRenderPassResources() { + std::vector passes_to_delete; + for (const auto& [backing_id, backing] : render_pass_backings_) { + // Buffer queue's root manages the root pass backing and its bookkeeping + // separately from other render pass backings. + if (!(buffer_queue_ && backing.is_root)) { + passes_to_delete.push_back(backing_id); + } + } + + // Delete RenderPass backings that will not be used. + for (size_t i = 0; i < passes_to_delete.size(); ++i) { + auto it = render_pass_backings_.find(passes_to_delete[i]); + auto& backing = it->second; + // Root render pass backings managed by |buffer_queue_| are not managed by + // DisplayResourceProvider, so we should not destroy them here. This + // reallocation is done in Reshape before drawing the frame + if (!(buffer_queue_ && backing.is_root)) { + skia_output_surface_->DestroySharedImage(backing.mailbox); + } + render_pass_backings_.erase(it); + } + + if (!passes_to_delete.empty()) { + skia_output_surface_->RemoveRenderPassResource(std::move(passes_to_delete)); + } +} +#endif + void SkiaRenderer::FlushOutputSurface() { auto sync_token = skia_output_surface_->Flush(); lock_set_for_external_use_->UnlockResources(sync_token); diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h index 03bd3b7ffd..e7579b8f87 100644 --- a/components/viz/service/display/skia_renderer.h +++ b/components/viz/service/display/skia_renderer.h @@ -86,6 +86,9 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer { void AllocateRenderPassResourceIfNeeded( const AggregatedRenderPassId& render_pass_id, const RenderPassRequirements& requirements) override; +#if BUILDFLAG(IS_OHOS) + void RemoveRenderPassResources() override; +#endif bool IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const override; gfx::Size GetRenderPassBackingPixelSize( diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc index f084ef7865..697e8e8c11 100644 --- a/components/viz/service/display/software_renderer.cc +++ b/components/viz/service/display/software_renderer.cc @@ -976,6 +976,12 @@ void SoftwareRenderer::AllocateRenderPassResourceIfNeeded( render_pass_bitmaps_.emplace(render_pass_id, std::move(bitmap)); } +#if BUILDFLAG(IS_OHOS) +void SoftwareRenderer::RemoveRenderPassResources() { + render_pass_bitmaps_.clear(); +} +#endif + bool SoftwareRenderer::IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const { auto it = render_pass_bitmaps_.find(render_pass_id); diff --git a/components/viz/service/display/software_renderer.h b/components/viz/service/display/software_renderer.h index 94bc3b7a2a..59efca387f 100644 --- a/components/viz/service/display/software_renderer.h +++ b/components/viz/service/display/software_renderer.h @@ -53,6 +53,9 @@ class VIZ_SERVICE_EXPORT SoftwareRenderer : public DirectRenderer { void AllocateRenderPassResourceIfNeeded( const AggregatedRenderPassId& render_pass_id, const RenderPassRequirements& requirements) override; +#if BUILDFLAG(IS_OHOS) + void RemoveRenderPassResources() override; +#endif bool IsRenderPassResourceAllocated( const AggregatedRenderPassId& render_pass_id) const override; gfx::Size GetRenderPassBackingPixelSize( diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index 7f0ec5fc3c..0b709054ea 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc @@ -368,16 +368,16 @@ void CompositorFrameSinkSupport::ReturnResources( return; // When features::OnBeginFrameAcks is disabled we attempt to return resources - // in DidReceiveCompositorFrameAck. However if there is no - // `ack_pending_from_surface_count_` then we don't expect that signal soon. In - // which case we return the resources to the `client_` now. + // in DidReceiveCompositorFrameAck. However if there are no pending frames + // then we don't expect that signal soon. In which case we return the + // resources to the `client_` now. // // When features::OnBeginFrameAcks is enabled we attempt to return resources // during the next OnBeginFrame. However if we currently do not // `needs_begin_frame_` or if we have been disconnected from a // `begin_frame_source_` then we don't expect that signal soon. In which case // we return the resources to the `client_` now. - if (!ack_pending_from_surface_count_ && client_ && + if (pending_frames_.empty() && client_ && (!ShouldMergeBeginFrameWithAcks() || (!needs_begin_frame_ || !begin_frame_source_))) { client_->ReclaimResources(std::move(resources)); @@ -555,6 +555,34 @@ void CompositorFrameSinkSupport::DidDeleteSharedBitmap( owned_bitmaps_.erase(id); } +void CompositorFrameSinkSupport::SubmitCompositorFrameLocally( + const SurfaceId& surface_id, + CompositorFrame frame, + const RendererSettings& settings) { + CHECK_EQ(surface_id, last_created_surface_id_); + + pending_frames_.push_back(FrameData{.local_frame = true}); + Surface* surface = surface_manager_->GetSurfaceForId(surface_id); + + auto frame_rejected_callback = + base::ScopedClosureRunner(base::BindOnce([] { NOTREACHED(); })); + auto frame_index = ++last_frame_index_; + Surface::QueueFrameResult result = surface->QueueFrame( + std::move(frame), frame_index, std::move(frame_rejected_callback)); + // Currently, frames are only queued on Android, and we don't need to use + // `SubmitCompositorFrameLocally` for evicting resources on Android. + CHECK_EQ(result, Surface::QueueFrameResult::ACCEPTED_ACTIVE); + + // Make sure this surface will be stretched to match the display size. If + // `auto_resize_output_surface` is false, then swap will not occur meaning + // that the content of this compositor frame will not be presented. If it is + // not, then we won't properly push out existing resources. A mismatch between + // root surface size and display size can happen. For example, there is a race + // condition if `Display` is resized after it is set not visible but before + // any compositor frame with that new size is submitted. + CHECK(settings.auto_resize_output_surface); +} + SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame( const LocalSurfaceId& local_surface_id, CompositorFrame frame, @@ -588,7 +616,7 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame( #endif begin_frame_tracker_.ReceivedAck(frame.metadata.begin_frame_ack); - ++ack_pending_from_surface_count_; + pending_frames_.push_back(FrameData{.local_frame = false}); if (frame.metadata.begin_frame_ack.frame_id.source_id == BeginFrameArgs::kManualSourceId) { @@ -778,16 +806,26 @@ void CompositorFrameSinkSupport::HandleCallback() { } void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() { - DCHECK_GT(ack_pending_from_surface_count_, 0); + DCHECK(!pending_frames_.empty()); bool was_pending_manual_begin_frame_source_ = pending_manual_begin_frame_source_; - ack_pending_from_surface_count_--; - if (!ack_pending_from_surface_count_) { + bool was_local_frame = pending_frames_.front().local_frame; + pending_frames_.pop_front(); + + if (pending_frames_.empty()) { pending_manual_begin_frame_source_ = false; } if (!client_) return; + // If this frame came from viz directly and not from the client, don't send + // the client an ack, since it didn't do anything. Just return the resources. + if (was_local_frame) { + client_->ReclaimResources(std::move(surface_returned_resources_)); + surface_returned_resources_.clear(); + return; + } + // If we have a callback, we only return the resource on onBeginFrame. if (compositor_frame_callback_) { callback_received_receive_ack_ = true; @@ -822,7 +860,7 @@ void CompositorFrameSinkSupport::DidPresentCompositorFrame( base::TimeTicks draw_start_timestamp, const gfx::SwapTimings& swap_timings, const gfx::PresentationFeedback& feedback) { - DCHECK(frame_token); + CHECK_NE(frame_token, kInvalidOrLocalFrameToken); DCHECK((feedback.flags & gfx::PresentationFeedback::kFailure) || (!draw_start_timestamp.is_null() && !swap_timings.is_null())); @@ -941,7 +979,7 @@ void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) { if (ShouldMergeBeginFrameWithAcks()) { bool frame_ack = ack_queued_for_client_count_ > 0; ack_pending_during_on_begin_frame_ = - !frame_ack && ack_pending_from_surface_count_; + !frame_ack && !pending_frames_.empty(); client_->OnBeginFrame(adjusted_args, std::move(frame_timing_details_), frame_ack, std::move(surface_returned_resources_)); if (frame_ack) { diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h index e4702891d1..14d6d06f54 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h @@ -9,6 +9,7 @@ #include #include +#include "base/containers/circular_deque.h" #include "base/containers/flat_set.h" #include "base/containers/queue.h" #include "base/functional/callback.h" @@ -193,6 +194,15 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport // doesn't have to exist at the time of calling. void EvictSurface(const LocalSurfaceId& id); + void GarbageCollectSurfaces() { surface_manager_->GarbageCollectSurfaces(); } + + // Submits a compositor frame not from the client but from viz itself. For + // example, this is used to submit empty compositor frames to unref + // resources on root surface eviction. + void SubmitCompositorFrameLocally(const SurfaceId& surface_id, + CompositorFrame frame, + const RendererSettings& settings); + // Attempts to submit a new CompositorFrame to |local_surface_id| and returns // whether the frame was accepted or the reason why it was rejected. If // |local_surface_id| hasn't been submitted before then a new Surface will be @@ -350,9 +360,17 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport // This has a HitTestAggregator if and only if |is_root_| is true. std::unique_ptr hit_test_aggregator_; - // Counts the number of CompositorFrames that have been submitted and have not + struct FrameData { + // True if this frame was submitted from viz itself. This happens during + // root surface eviction when an empty compositor frame is submitted to + // deref existing resources. + bool local_frame; + }; + + // Keeps track of CompositorFrames that have been submitted and have not // yet received an ACK from their Surface. - int ack_pending_from_surface_count_ = 0; + base::circular_deque pending_frames_; + // Counts the number of ACKs that have been received from a Surface and have // not yet been sent to the CompositorFrameSinkClient. int ack_queued_for_client_count_ = 0; @@ -360,7 +378,8 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport // When `true` we have received frames from a client using its own // BeginFrameSource. While dealing with frames from multiple sources we cannot - // rely on `ack_pending_from_surface_count_` to throttle frame production. + // rely on checking the number of pending frames in `pending_frames_` to + // throttle frame production. // // TODO(crbug.com/1396081): Track acks, presentation feedback, and resources // being returned, on a per BeginFrameSource basis. For diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index 1707b7f853..4a32b1742b 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc @@ -324,9 +324,8 @@ class OnBeginFrameAcksCompositorFrameSinkSupportTest bool BeginFrameAcksEnabled() const { return GetParam(); } - int ack_pending_from_surface_count( - const CompositorFrameSinkSupport* support) const { - return support->ack_pending_from_surface_count_; + int num_pending_frames(const CompositorFrameSinkSupport* support) const { + return support->pending_frames_.size(); } private: @@ -598,7 +597,7 @@ TEST_P(OnBeginFrameAcksCompositorFrameSinkSupportTest, ResourceLifetime) { // This test relied on CompositorFrameSinkSupport::ReturnResources to not send // as long as there has been no DidReceiveCompositorFrameAck. Such that - // `ack_pending_from_surface_count_` is always greater than 1. + // the number of pending frames is always greater than 1. // // With features::kOnBeginFrameAcks we now return the resources during // OnBeginFrame, however that is throttled while we await any ack. @@ -745,7 +744,7 @@ TEST_P(OnBeginFrameAcksCompositorFrameSinkSupportTest, AddDuringEviction) { testing::Mock::VerifyAndClearExpectations(&mock_client); } - EXPECT_EQ(1, ack_pending_from_surface_count(support.get())); + EXPECT_EQ(1, num_pending_frames(support.get())); } // Verifies that only monotonically increasing LocalSurfaceIds are accepted. diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc index 3ce6bb505a..4ffea4789e 100644 --- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc +++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc @@ -353,12 +353,18 @@ void FrameSinkManagerImpl::EvictSurfaces( auto it = support_map_.find(surface_id.frame_sink_id()); if (it == support_map_.end()) continue; - it->second->EvictSurface(surface_id.local_surface_id()); - if (!it->second->is_root()) - continue; - auto root_it = root_sink_map_.find(surface_id.frame_sink_id()); - if (root_it != root_sink_map_.end()) - root_it->second->DidEvictSurface(surface_id); + + bool should_evict = true; + if (it->second->is_root()) { + auto root_it = root_sink_map_.find(surface_id.frame_sink_id()); + if (root_it != root_sink_map_.end()) { + should_evict = root_it->second->WillEvictSurface(surface_id); + } + } + + if (should_evict) { + it->second->EvictSurface(surface_id.local_surface_id()); + } } // Trigger garbage collection immediately, otherwise the surface may not be diff --git a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc index bbce18b254..1ae72a1265 100644 --- a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc +++ b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc @@ -670,6 +670,8 @@ TEST_F(FrameSinkManagerTest, EvictRootSurfaceId) { manager_.CreateRootCompositorFrameSink( root_data.BuildParams(kFrameSinkIdRoot)); + GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20)); + ParentLocalSurfaceIdAllocator allocator; allocator.GenerateId(); const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId(); @@ -690,6 +692,8 @@ TEST_F(FrameSinkManagerTest, EvictNewerRootSurfaceId) { manager_.CreateRootCompositorFrameSink( root_data.BuildParams(kFrameSinkIdRoot)); + GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20)); + ParentLocalSurfaceIdAllocator allocator; allocator.GenerateId(); const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId(); @@ -713,6 +717,8 @@ TEST_F(FrameSinkManagerTest, SubmitCompositorFrameWithEvictedSurfaceId) { manager_.CreateRootCompositorFrameSink( root_data.BuildParams(kFrameSinkIdRoot)); + GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20)); + ParentLocalSurfaceIdAllocator allocator; allocator.GenerateId(); const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId(); diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc index 40c6f57fb3..75e6a0eefd 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc @@ -16,7 +16,9 @@ #include "base/time/time.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/service/display/display.h" #include "components/viz/service/display/output_surface.h" #include "components/viz/service/display_embedder/output_surface_provider.h" @@ -241,16 +243,82 @@ RootCompositorFrameSinkImpl::~RootCompositorFrameSinkImpl() { begin_frame_source()); } -void RootCompositorFrameSinkImpl::DidEvictSurface(const SurfaceId& surface_id) { +bool RootCompositorFrameSinkImpl::WillEvictSurface( + const SurfaceId& surface_id) { const SurfaceId& current_surface_id = display_->CurrentSurfaceId(); if (!current_surface_id.is_valid()) - return; - DCHECK_EQ(surface_id.frame_sink_id(), surface_id.frame_sink_id()); - // This matches CompositorFrameSinkSupport's eviction logic. + return true; // Okay to evict immediately. + DCHECK_EQ(surface_id.frame_sink_id(), current_surface_id.frame_sink_id()); + CHECK(!display_->visible()); + DCHECK(display_->has_scheduler()); + + // This matches CompositorFrameSinkSupport's eviction logic, which wil evict + // `surface_id` or matching but older ones. Avoid overwriting the contents + // of `current_surface_id` if it's newer here by doing the same check. if (surface_id.local_surface_id().parent_sequence_number() >= current_surface_id.local_surface_id().parent_sequence_number()) { - display_->InvalidateCurrentSurfaceId(); + // Push empty compositor frame to root surface. This is so the resources + // can be unreffed from both viz and the OS compositor (if required). + CompositorFrame frame; + + auto& metadata = frame.metadata; + metadata.frame_token = kInvalidOrLocalFrameToken; + + // The given `surface_id` may be newer than `current_surface_id`, so use the + // one we actually have. + auto* surface = + support_->frame_sink_manager()->surface_manager()->GetSurfaceForId( + current_surface_id); + CHECK(surface); + metadata.device_scale_factor = surface->device_scale_factor(); + frame.metadata.begin_frame_ack = BeginFrameAck::CreateManualAckWithDamage(); + + frame.render_pass_list.push_back(CompositorRenderPass::Create()); + const std::unique_ptr& render_pass = + frame.render_pass_list.back(); + + const CompositorRenderPassId kRenderPassId{1}; + auto surface_rect = gfx::Rect(surface->size_in_pixels()); + DCHECK(!surface_rect.IsEmpty()); + render_pass->SetNew(kRenderPassId, /*output_rect=*/surface_rect, + /*damage_rect=*/surface_rect, gfx::Transform()); +#if BUILDFLAG(IS_OHOS) + render_pass->has_transparent_background = false; +#endif + + SharedQuadState* quad_state = render_pass->CreateAndAppendSharedQuadState(); + + quad_state->SetAll(gfx::Transform(), /*layer_rect=*/surface_rect, + /*visible_layer_rect=*/surface_rect, + /*filter_info=*/gfx::MaskFilterInfo(), + /*clip=*/absl::nullopt, + /*contents_opaque=*/true, /*opacity_f=*/1.f, + /*blend=*/SkBlendMode::kSrcOver, /*sorting_context=*/0); + + SolidColorDrawQuad* solid_quad = + render_pass->CreateAndAppendDrawQuad(); +#if BUILDFLAG(IS_OHOS) + solid_quad->SetNew(quad_state, surface_rect, surface_rect, SkColors::kWhite, + /*anti_aliasing_off=*/false); +#else + solid_quad->SetNew(quad_state, surface_rect, surface_rect, SkColors::kBlack, + /*anti_aliasing_off=*/false); +#endif + + support_->SubmitCompositorFrameLocally(current_surface_id, std::move(frame), + display_->settings()); + + // Complete the eviction on next draw and swap. + to_evict_on_next_draw_and_swap_ = surface_id.local_surface_id(); + display_->SetVisible(true); + display_->ForceImmediateDrawAndSwapIfPossible(); + // Don't evict immediately. + // Delay eviction until the next draw to make sure that the draw is + // successful (requires the surface not to be evicted). We need the draw (of + // an empty CF) to be successful to push out and free resources. + return false; } + return true; // Okay to evict immediately. } const SurfaceId& RootCompositorFrameSinkImpl::CurrentSurfaceId() const { @@ -258,6 +326,9 @@ const SurfaceId& RootCompositorFrameSinkImpl::CurrentSurfaceId() const { } void RootCompositorFrameSinkImpl::SetDisplayVisible(bool visible) { + if (visible) { + to_evict_on_next_draw_and_swap_ = LocalSurfaceId(); + } display_->SetVisible(visible); } @@ -612,6 +683,15 @@ void RootCompositorFrameSinkImpl::DisplayWillDrawAndSwap( AggregatedRenderPassList* render_passes) { DCHECK(support_->GetHitTestAggregator()); support_->GetHitTestAggregator()->Aggregate(display_->CurrentSurfaceId()); + +#if BUILDFLAG(IS_OHOS) + if (to_evict_on_next_draw_and_swap_.is_valid()) { + if (base::FeatureList::IsEnabled(features::kEvictRootSurface) && + features::kPreserveOutputContentOnEvictRootSurface.Get()) { + display_->SetSkipDrawAndEvictResourcesOnCurrentFrame(); + } + } +#endif } base::ScopedClosureRunner RootCompositorFrameSinkImpl::GetCacheBackBufferCb() { @@ -740,7 +820,23 @@ RootCompositorFrameSinkImpl::GetPreferredFrameIntervalForFrameSinkId( ->GetPreferredFrameIntervalForFrameSinkId(id, type); } -void RootCompositorFrameSinkImpl::DisplayDidDrawAndSwap() {} +void RootCompositorFrameSinkImpl::DisplayDidDrawAndSwap() { + if (to_evict_on_next_draw_and_swap_.is_valid()) { + display_->SetVisible(false); + display_->InvalidateCurrentSurfaceId(); + + support_->EvictSurface(to_evict_on_next_draw_and_swap_); + + // Trigger garbage collection immediately, otherwise the surface may not be + // evicted for a long time (e.g. not before a frame is produced). + if (base::FeatureList::IsEnabled( + features::kEagerSurfaceGarbageCollection)) { + support_->GarbageCollectSurfaces(); + } + } + + to_evict_on_next_draw_and_swap_ = LocalSurfaceId(); +} BeginFrameSource* RootCompositorFrameSinkImpl::begin_frame_source() { if (external_begin_frame_source_) diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h index 8357e95d89..2cc7e4bc75 100644 --- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h +++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h @@ -63,7 +63,8 @@ class VIZ_SERVICE_EXPORT RootCompositorFrameSinkImpl ~RootCompositorFrameSinkImpl() override; - void DidEvictSurface(const SurfaceId& surface_id); + // Returns true iff it is okay to evict the root surface immediately. + bool WillEvictSurface(const SurfaceId& surface_id); const SurfaceId& CurrentSurfaceId() const; @@ -229,6 +230,11 @@ void SetHandledTouchEvent(bool handledTouchEvent) override {} // TODO(http://crbug.com/1153404): Remove this field when experiment is over. bool apply_simple_frame_rate_throttling_ = false; + // If we evict the root surface, we want to push an empty compositor frame to + // it first to unref its resources. This requires a draw and swap to complete + // to actually unref. + LocalSurfaceId to_evict_on_next_draw_and_swap_ = LocalSurfaceId(); + // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch // of lacros-chrome is complete. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_OHOS) diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc index e7282a7b0e..7472a2d7e8 100644 --- a/components/viz/service/surfaces/surface.cc +++ b/components/viz/service/surfaces/surface.cc @@ -70,7 +70,7 @@ void Surface::PresentationHelper::DidPresent( base::TimeTicks draw_start_timestamp, const gfx::SwapTimings& swap_timings, const gfx::PresentationFeedback& feedback) { - if (surface_client_ && frame_token_) { + if (surface_client_ && frame_token_ != kInvalidOrLocalFrameToken) { surface_client_->OnSurfacePresented(frame_token_, draw_start_timestamp, swap_timings, feedback); } @@ -839,7 +839,8 @@ void Surface::UnrefFrameResourcesAndRunCallbacks( // If we won't be getting a presented notification, we'll notify the client // when the frame is unref'd. - if (!frame_data->will_be_notified_of_presentation && surface_client_) { + if (!frame_data->will_be_notified_of_presentation && surface_client_ && + frame_data->frame.metadata.frame_token != kInvalidOrLocalFrameToken) { surface_client_->OnSurfacePresented(frame_data->frame.metadata.frame_token, base::TimeTicks(), gfx::SwapTimings(), gfx::PresentationFeedback::Failure()); diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc index e8d7b4038c..9a2d51d049 100644 --- a/components/viz/service/surfaces/surface_unittest.cc +++ b/components/viz/service/surfaces/surface_unittest.cc @@ -79,7 +79,7 @@ TEST_P(OnBeginFrameAcksSurfaceTest, PresentationCallback) { if (BeginFrameAcksEnabled()) { support->SetWantsBeginFrameAcks(); } - uint32_t frame_token = 0; + uint32_t frame_token = kInvalidOrLocalFrameToken; { CompositorFrame frame = CompositorFrameBuilder() @@ -87,7 +87,7 @@ TEST_P(OnBeginFrameAcksSurfaceTest, PresentationCallback) { .SetBeginFrameSourceId(kBeginFrameSourceId) .Build(); frame_token = frame.metadata.frame_token; - ASSERT_NE(frame_token, 0u); + ASSERT_NE(frame_token, kInvalidOrLocalFrameToken); EXPECT_CALL(client, DidReceiveCompositorFrameAck(testing::_)) .Times(BeginFrameAcksEnabled() ? 0 : 1); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); diff --git a/components/viz/test/fake_host_frame_sink_client.h b/components/viz/test/fake_host_frame_sink_client.h index e4691fb50b..4ca2a27dae 100644 --- a/components/viz/test/fake_host_frame_sink_client.h +++ b/components/viz/test/fake_host_frame_sink_client.h @@ -6,6 +6,7 @@ #define COMPONENTS_VIZ_TEST_FAKE_HOST_FRAME_SINK_CLIENT_H_ #include "base/time/time.h" +#include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/surfaces/surface_info.h" #include "components/viz/host/host_frame_sink_client.h" @@ -28,7 +29,7 @@ class FakeHostFrameSinkClient : public HostFrameSinkClient { uint32_t last_frame_token_seen() const { return last_frame_token_seen_; } private: - uint32_t last_frame_token_seen_ = 0u; + uint32_t last_frame_token_seen_ = kInvalidOrLocalFrameToken; }; } // namespace viz diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc index 7b5062a283..81a93d924c 100644 --- a/content/browser/renderer_host/delegated_frame_host.cc +++ b/content/browser/renderer_host/delegated_frame_host.cc @@ -31,6 +31,10 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/dip_util.h" +#if BUILDFLAG(IS_OHOS) +#include "third_party/ohos_ndk/includes/ohos_adapter/ohos_adapter_helper.h" +#endif + namespace content { namespace { @@ -630,4 +634,20 @@ void DelegatedFrameHost::TakeFallbackContentFrom(DelegatedFrameHost* other) { desired_fallback); } +#if BUILDFLAG(IS_OHOS) +// static +bool DelegatedFrameHost::ShouldIncludeUiCompositorForEviction() { + static bool persist_web_root_surface_eviction_enabled = + OHOS::NWeb::OhosAdapterHelper::GetInstance() + .GetSystemPropertiesInstance() + .GetBoolParameter("persist.web.root_surface_eviction.enable", + true); + if (!persist_web_root_surface_eviction_enabled) { + return false; + } + + return base::FeatureList::IsEnabled(features::kEvictRootSurface); +} +#endif + } // namespace content diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h index dce7d340a5..a81ddfbc80 100644 --- a/content/browser/renderer_host/delegated_frame_host.h +++ b/content/browser/renderer_host/delegated_frame_host.h @@ -209,6 +209,10 @@ class CONTENT_EXPORT DelegatedFrameHost return frame_evictor_.get(); } +#if BUILDFLAG(IS_OHOS) + static bool ShouldIncludeUiCompositorForEviction(); +#endif + private: friend class DelegatedFrameHostClient; FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraBrowserTest, -- Gitee