[ios blink] Simplify external begin frames
Fork ExternalBeginFrameSourceMojo to an iOS specific variant so that we don't have to unnecessarily wait for previous frame's BeginFrameAck. This uses a new IssueExternalBeginFrameNoAck mojo API. With this new variant, Motionmark is able to see 60fps in its frame rate detection phase. Change-Id: I4a415f2388a3855db963ca8c6d8f825e20fff36b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6398086 Reviewed-by: Alex Gough <ajgo@chromium.org> Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org> Reviewed-by: Dave Tapuska <dtapuska@chromium.org> Reviewed-by: Jonathan Ross <jonross@chromium.org> Commit-Queue: Jonathan Ross <jonross@chromium.org> Cr-Commit-Position: refs/heads/main@{#1439376}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
e570133c2f
commit
6151353b2d
components/viz/service
BUILD.gn
frame_sinks
content/browser/renderer_host
services/viz/privileged/mojom/compositing
ui/compositor
@ -188,8 +188,6 @@ viz_component("service") {
|
||||
"frame_sinks/compositor_frame_sink_impl.h",
|
||||
"frame_sinks/compositor_frame_sink_support.cc",
|
||||
"frame_sinks/compositor_frame_sink_support.h",
|
||||
"frame_sinks/external_begin_frame_source_mojo.cc",
|
||||
"frame_sinks/external_begin_frame_source_mojo.h",
|
||||
"frame_sinks/frame_counter.cc",
|
||||
"frame_sinks/frame_counter.h",
|
||||
"frame_sinks/frame_sink_bundle_impl.cc",
|
||||
@ -386,6 +384,18 @@ viz_component("service") {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_ios) {
|
||||
sources += [
|
||||
"frame_sinks/external_begin_frame_source_mojo_ios.cc",
|
||||
"frame_sinks/external_begin_frame_source_mojo_ios.h",
|
||||
]
|
||||
} else {
|
||||
sources += [
|
||||
"frame_sinks/external_begin_frame_source_mojo.cc",
|
||||
"frame_sinks/external_begin_frame_source_mojo.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (is_android || use_ozone) {
|
||||
sources += [
|
||||
"display/overlay_combination_cache.cc",
|
||||
|
@ -0,0 +1,42 @@
|
||||
// Copyright 2025 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/frame_sinks/external_begin_frame_source_mojo_ios.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace viz {
|
||||
|
||||
ExternalBeginFrameSourceMojoIOS::ExternalBeginFrameSourceMojoIOS(
|
||||
mojo::PendingAssociatedReceiver<mojom::ExternalBeginFrameController>
|
||||
controller_receiver,
|
||||
mojo::PendingAssociatedRemote<mojom::ExternalBeginFrameControllerClient>
|
||||
controller_client_remote,
|
||||
uint32_t restart_id)
|
||||
: ExternalBeginFrameSource(this, restart_id),
|
||||
receiver_(this, std::move(controller_receiver)),
|
||||
remote_client_(std::move(controller_client_remote)) {}
|
||||
|
||||
ExternalBeginFrameSourceMojoIOS::~ExternalBeginFrameSourceMojoIOS() = default;
|
||||
|
||||
void ExternalBeginFrameSourceMojoIOS::IssueExternalBeginFrameNoAck(
|
||||
const BeginFrameArgs& args) {
|
||||
OnBeginFrame(args);
|
||||
}
|
||||
|
||||
void ExternalBeginFrameSourceMojoIOS::OnNeedsBeginFrames(
|
||||
bool needs_begin_frames) {
|
||||
if (remote_client_) {
|
||||
remote_client_->SetNeedsBeginFrame(needs_begin_frames);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalBeginFrameSourceMojoIOS::SetPreferredInterval(
|
||||
base::TimeDelta interval) {
|
||||
if (remote_client_) {
|
||||
remote_client_->SetPreferredInterval(interval);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace viz
|
@ -0,0 +1,53 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_IOS_H_
|
||||
#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_IOS_H_
|
||||
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "components/viz/common/frame_sinks/begin_frame_args.h"
|
||||
#include "components/viz/common/frame_sinks/begin_frame_source.h"
|
||||
#include "components/viz/service/viz_service_export.h"
|
||||
#include "mojo/public/cpp/bindings/associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/associated_remote.h"
|
||||
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
|
||||
#include "services/viz/privileged/mojom/compositing/external_begin_frame_controller.mojom.h"
|
||||
|
||||
namespace viz {
|
||||
|
||||
// Implementation of ExternalBeginFrameSource that's controlled by IPCs over the
|
||||
// mojom::ExternalBeginFrameController interface from UI/browser process on iOS.
|
||||
class VIZ_SERVICE_EXPORT ExternalBeginFrameSourceMojoIOS
|
||||
: public mojom::ExternalBeginFrameController,
|
||||
public ExternalBeginFrameSource,
|
||||
public ExternalBeginFrameSourceClient {
|
||||
public:
|
||||
// `controller_receiver` must be a valid mojo receiver.
|
||||
// `controller_client_remote` is optional and can be an invalid remote.
|
||||
ExternalBeginFrameSourceMojoIOS(
|
||||
mojo::PendingAssociatedReceiver<mojom::ExternalBeginFrameController>
|
||||
controller_receiver,
|
||||
mojo::PendingAssociatedRemote<mojom::ExternalBeginFrameControllerClient>
|
||||
controller_client_remote,
|
||||
uint32_t restart_id);
|
||||
~ExternalBeginFrameSourceMojoIOS() override;
|
||||
|
||||
// mojom::ExternalBeginFrameController implementation.
|
||||
void IssueExternalBeginFrameNoAck(const BeginFrameArgs& args) override;
|
||||
|
||||
private:
|
||||
// ExternalBeginFrameSourceClient implementation.
|
||||
void OnNeedsBeginFrames(bool needs_begin_frames) override;
|
||||
void SetPreferredInterval(base::TimeDelta interval) override;
|
||||
|
||||
mojo::AssociatedReceiver<mojom::ExternalBeginFrameController> receiver_;
|
||||
mojo::AssociatedRemote<mojom::ExternalBeginFrameControllerClient>
|
||||
remote_client_;
|
||||
};
|
||||
|
||||
} // namespace viz
|
||||
|
||||
#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_IOS_H_
|
@ -26,7 +26,6 @@
|
||||
#include "components/viz/service/display/output_surface.h"
|
||||
#include "components/viz/service/display_embedder/output_surface_provider.h"
|
||||
#include "components/viz/service/display_embedder/vsync_parameter_listener.h"
|
||||
#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo.h"
|
||||
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
|
||||
#include "components/viz/service/hit_test/hit_test_aggregator.h"
|
||||
#include "services/viz/public/mojom/compositing/layer_context.mojom.h"
|
||||
@ -39,6 +38,9 @@
|
||||
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
#include "components/viz/common/frame_sinks/external_begin_frame_source_ios.h"
|
||||
#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo_ios.h"
|
||||
#else
|
||||
#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
@ -134,7 +136,9 @@ RootCompositorFrameSinkImpl::Create(
|
||||
// |params|.
|
||||
std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source;
|
||||
std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source;
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
ExternalBeginFrameSourceMojo* external_begin_frame_source_mojo = nullptr;
|
||||
#endif
|
||||
bool hw_support_for_multiple_refresh_rates = false;
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
bool created_external_begin_frame_source_mac = false;
|
||||
@ -144,18 +148,23 @@ RootCompositorFrameSinkImpl::Create(
|
||||
#endif
|
||||
|
||||
if (params->external_begin_frame_controller) {
|
||||
auto owned_external_begin_frame_source_mojo =
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
hw_support_for_multiple_refresh_rates = true;
|
||||
external_begin_frame_source =
|
||||
std::make_unique<ExternalBeginFrameSourceMojoIOS>(
|
||||
std::move(params->external_begin_frame_controller),
|
||||
std::move(params->external_begin_frame_controller_client),
|
||||
restart_id);
|
||||
#else
|
||||
external_begin_frame_source =
|
||||
std::make_unique<ExternalBeginFrameSourceMojo>(
|
||||
frame_sink_manager,
|
||||
std::move(params->external_begin_frame_controller),
|
||||
std::move(params->external_begin_frame_controller_client),
|
||||
restart_id);
|
||||
external_begin_frame_source_mojo =
|
||||
owned_external_begin_frame_source_mojo.get();
|
||||
external_begin_frame_source =
|
||||
std::move(owned_external_begin_frame_source_mojo);
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
hw_support_for_multiple_refresh_rates = true;
|
||||
static_cast<ExternalBeginFrameSourceMojo*>(
|
||||
external_begin_frame_source.get());
|
||||
#endif
|
||||
} else {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
@ -238,8 +247,11 @@ RootCompositorFrameSinkImpl::Create(
|
||||
std::move(output_surface), std::move(overlay_processor),
|
||||
std::move(scheduler), std::move(task_runner));
|
||||
|
||||
if (external_begin_frame_source_mojo)
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
if (external_begin_frame_source_mojo) {
|
||||
external_begin_frame_source_mojo->SetDisplay(display.get());
|
||||
}
|
||||
#endif
|
||||
|
||||
// base::WrapUnique instead of std::make_unique because the ctor is private.
|
||||
auto impl = base::WrapUnique(new RootCompositorFrameSinkImpl(
|
||||
|
@ -39,13 +39,13 @@ class BeginFrameSourceIOS
|
||||
void SetPreferredInterval(base::TimeDelta interval) override;
|
||||
|
||||
private:
|
||||
void BeginFrameAck(const viz::BeginFrameAck&);
|
||||
const raw_ptr<ui::Compositor> compositor_;
|
||||
|
||||
raw_ptr<ui::Compositor> compositor_;
|
||||
viz::ExternalBeginFrameSourceIOS begin_frame_source_;
|
||||
|
||||
viz::BeginFrameArgs last_used_begin_frame_args_;
|
||||
bool send_begin_frame_ = true;
|
||||
bool added_observer_ = false;
|
||||
bool observing_begin_frame_source_ = false;
|
||||
|
||||
mojo::AssociatedReceiverSet<viz::mojom::ExternalBeginFrameControllerClient>
|
||||
receivers_;
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "content/browser/renderer_host/begin_frame_source_ios.h"
|
||||
|
||||
#include "base/functional/callback_helpers.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -12,6 +14,7 @@ namespace content {
|
||||
BeginFrameSourceIOS::BeginFrameSourceIOS(ui::Compositor* compositor)
|
||||
: compositor_(compositor),
|
||||
begin_frame_source_(viz::BackToBackBeginFrameSource::kNotRestartableId) {
|
||||
DCHECK(compositor_);
|
||||
compositor_->SetExternalBeginFrameControllerClientFactory(this);
|
||||
}
|
||||
|
||||
@ -20,19 +23,8 @@ BeginFrameSourceIOS::~BeginFrameSourceIOS() {
|
||||
}
|
||||
|
||||
void BeginFrameSourceIOS::OnBeginFrame(const viz::BeginFrameArgs& args) {
|
||||
if (!compositor_ || !send_begin_frame_) {
|
||||
return;
|
||||
}
|
||||
last_used_begin_frame_args_ = args;
|
||||
send_begin_frame_ = false;
|
||||
compositor_->IssueExternalBeginFrame(
|
||||
args, /*force=*/true,
|
||||
base::BindOnce(&BeginFrameSourceIOS::BeginFrameAck,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void BeginFrameSourceIOS::BeginFrameAck(const viz::BeginFrameAck&) {
|
||||
send_begin_frame_ = true;
|
||||
compositor_->IssueExternalBeginFrameNoAck(args);
|
||||
}
|
||||
|
||||
const viz::BeginFrameArgs& BeginFrameSourceIOS::LastUsedBeginFrameArgs() const {
|
||||
@ -58,17 +50,13 @@ BeginFrameSourceIOS::CreateExternalBeginFrameControllerClient() {
|
||||
}
|
||||
|
||||
void BeginFrameSourceIOS::SetNeedsBeginFrame(bool needs_begin_frames) {
|
||||
if (needs_begin_frames == observing_begin_frame_source_) {
|
||||
return;
|
||||
}
|
||||
observing_begin_frame_source_ = needs_begin_frames;
|
||||
if (needs_begin_frames) {
|
||||
if (added_observer_) {
|
||||
return;
|
||||
}
|
||||
added_observer_ = true;
|
||||
begin_frame_source_.AddObserver(this);
|
||||
} else {
|
||||
if (!added_observer_) {
|
||||
return;
|
||||
}
|
||||
added_observer_ = false;
|
||||
begin_frame_source_.RemoveObserver(this);
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,16 @@ interface ExternalBeginFrameController {
|
||||
// If force is set to true, the frame will either begin or fail immediately,
|
||||
// otherwise the frame will be deferred till one of the frame sinks indicates
|
||||
// it's interested in a frame.
|
||||
// This is only used for headless on desktop and Android XR compositor.
|
||||
[EnableIfNot=is_ios]
|
||||
IssueExternalBeginFrame(BeginFrameArgs args, bool force)
|
||||
=> (BeginFrameAck ack);
|
||||
|
||||
// Issue a begin frame to the compositor. This is only used on iOS where begin
|
||||
// frames originate in the browser process due to sandbox restrictions and we
|
||||
// don't care about BeginFrameAcks for frame throttling unlike headless.
|
||||
[EnableIf=is_ios]
|
||||
IssueExternalBeginFrameNoAck(BeginFrameArgs args);
|
||||
};
|
||||
|
||||
// Exposes a way to control when external BeginFrames are issued.
|
||||
@ -25,4 +33,4 @@ interface ExternalBeginFrameControllerClient {
|
||||
|
||||
// Update the preferred interval.
|
||||
SetPreferredInterval(mojo_base.mojom.TimeDelta interval);
|
||||
};
|
||||
};
|
||||
|
@ -75,19 +75,15 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Used to hold on to IssueExternalBeginFrame arguments if
|
||||
// |external_begin_frame_controller_| isn't ready yet.
|
||||
struct PendingBeginFrameArgs {
|
||||
PendingBeginFrameArgs(
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool force,
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback)
|
||||
: args(args), force(force), callback(std::move(callback)) {}
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
Compositor::PendingBeginFrameArgs::PendingBeginFrameArgs(
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool force,
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback)
|
||||
: args(args), force(force), callback(std::move(callback)) {}
|
||||
|
||||
viz::BeginFrameArgs args;
|
||||
bool force;
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback;
|
||||
};
|
||||
Compositor::PendingBeginFrameArgs::~PendingBeginFrameArgs() = default;
|
||||
#endif
|
||||
|
||||
Compositor::Compositor(const viz::FrameSinkId& frame_sink_id,
|
||||
ui::ContextFactory* context_factory,
|
||||
@ -388,9 +384,14 @@ void Compositor::SetExternalBeginFrameController(
|
||||
DCHECK(use_external_begin_frame_control());
|
||||
external_begin_frame_controller_ = std::move(external_begin_frame_controller);
|
||||
if (pending_begin_frame_args_) {
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
external_begin_frame_controller_->IssueExternalBeginFrameNoAck(
|
||||
*pending_begin_frame_args_);
|
||||
#else
|
||||
external_begin_frame_controller_->IssueExternalBeginFrame(
|
||||
pending_begin_frame_args_->args, pending_begin_frame_args_->force,
|
||||
std::move(pending_begin_frame_args_->callback));
|
||||
#endif
|
||||
pending_begin_frame_args_.reset();
|
||||
}
|
||||
}
|
||||
@ -726,6 +727,17 @@ bool Compositor::HasAnimationObserver(
|
||||
return animation_observer_list_.HasObserver(observer);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
void Compositor::IssueExternalBeginFrameNoAck(const viz::BeginFrameArgs& args) {
|
||||
if (!external_begin_frame_controller_) {
|
||||
// It's ok to call this repeatedly until |external_begin_frame_controller_|
|
||||
// is ready - we'll just update the |pending_begin_frame_args_|.
|
||||
pending_begin_frame_args_.emplace(args);
|
||||
return;
|
||||
}
|
||||
external_begin_frame_controller_->IssueExternalBeginFrameNoAck(args);
|
||||
}
|
||||
#else
|
||||
void Compositor::IssueExternalBeginFrame(
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool force,
|
||||
@ -734,13 +746,13 @@ void Compositor::IssueExternalBeginFrame(
|
||||
// IssueExternalBeginFrame() shouldn't be called again before the previous
|
||||
// begin frame is acknowledged.
|
||||
DCHECK(!pending_begin_frame_args_);
|
||||
pending_begin_frame_args_ = std::make_unique<PendingBeginFrameArgs>(
|
||||
args, force, std::move(callback));
|
||||
pending_begin_frame_args_.emplace(args, force, std::move(callback));
|
||||
return;
|
||||
}
|
||||
external_begin_frame_controller_->IssueExternalBeginFrame(
|
||||
args, force, std::move(callback));
|
||||
}
|
||||
#endif
|
||||
|
||||
CompositorMetricsTracker Compositor::RequestNewCompositorMetricsTracker() {
|
||||
return CompositorMetricsTracker(next_compositor_metrics_tracker_id_++,
|
||||
|
@ -100,7 +100,6 @@ class ScopedAnimationDurationScaleMode;
|
||||
class ScrollInputHandler;
|
||||
class CompositorMetricsTracker;
|
||||
class CompositorPropertyTreeDelegate;
|
||||
struct PendingBeginFrameArgs;
|
||||
|
||||
constexpr int kCompositorLockTimeoutMs = 67;
|
||||
|
||||
@ -376,10 +375,14 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
void RequestSuccessfulPresentationTimeForNextFrame(
|
||||
SuccessfulPresentationTimeCallback callback);
|
||||
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
void IssueExternalBeginFrameNoAck(const viz::BeginFrameArgs& args);
|
||||
#else
|
||||
void IssueExternalBeginFrame(
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool force,
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback);
|
||||
#endif
|
||||
|
||||
// Creates a CompositorMetricsTracker for tracking this Compositor.
|
||||
CompositorMetricsTracker RequestNewCompositorMetricsTracker();
|
||||
@ -603,7 +606,24 @@ class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver,
|
||||
raw_ptr<ExternalBeginFrameControllerClientFactory>
|
||||
external_begin_frame_controler_client_factory_;
|
||||
|
||||
std::unique_ptr<PendingBeginFrameArgs> pending_begin_frame_args_;
|
||||
// Used to hold on to IssueExternalBeginFrame(NoAck) arguments if
|
||||
// |external_begin_frame_controller_| isn't ready yet.
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
using PendingBeginFrameArgs = viz::BeginFrameArgs;
|
||||
#else
|
||||
struct PendingBeginFrameArgs {
|
||||
PendingBeginFrameArgs(
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool force,
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback);
|
||||
~PendingBeginFrameArgs();
|
||||
|
||||
const viz::BeginFrameArgs args;
|
||||
const bool force;
|
||||
base::OnceCallback<void(const viz::BeginFrameAck&)> callback;
|
||||
};
|
||||
#endif
|
||||
std::optional<PendingBeginFrameArgs> pending_begin_frame_args_;
|
||||
|
||||
ui::HostBeginFrameObserver::SimpleBeginFrameObserverList
|
||||
simple_begin_frame_observers_;
|
||||
|
Reference in New Issue
Block a user