blob: b1db9637231c19cc027514d354723f3b7af93504 [file] [log] [blame]
// Copyright 2014 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_COMMON_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_
#define COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/viz/common/display/update_vsync_parameters_callback.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/frame_sinks/delay_based_time_source.h"
namespace perfetto {
class EventContext;
namespace protos {
namespace pbzero {
class BeginFrameObserverStateV2;
class BeginFrameSourceStateV2;
} // namespace pbzero
} // namespace protos
} // namespace perfetto
namespace viz {
// (Pure) Interface for observing BeginFrame messages from BeginFrameSource
// objects.
class VIZ_COMMON_EXPORT BeginFrameObserver {
public:
virtual ~BeginFrameObserver() = default;
// The |args| given to OnBeginFrame is guaranteed to have
// |args|.IsValid()==true. If |args|.frame_id.source_id did not change
// between invocations, |args|.frame_id.sequence_number is guaranteed to be
// be strictly greater than the previous call. Further, |args|.frame_time is
// guaranteed to be greater than or equal to the previous call even if the
// source_id changes.
//
// Side effects: This function can (and most of the time *will*) change the
// return value of the LastUsedBeginFrameArgs method. See the documentation
// on that method for more information.
//
// The observer is required call BeginFrameSource::DidFinishFrame() as soon as
// it has completed handling the BeginFrame.
virtual void OnBeginFrame(const BeginFrameArgs& args) = 0;
// Returns the last BeginFrameArgs used by the observer. This method's
// return value is affected by the OnBeginFrame method!
//
// - Before the first call of OnBeginFrame, this method should return a
// BeginFrameArgs on which IsValid() returns false.
//
// - If the |args| passed to OnBeginFrame is (or *will be*) used, then
// LastUsedBeginFrameArgs return value should become the |args| given to
// OnBeginFrame.
//
// - If the |args| passed to OnBeginFrame is dropped, then
// LastUsedBeginFrameArgs return value should *not* change.
//
// These requirements are designed to allow chaining and nesting of
// BeginFrameObservers which filter the incoming BeginFrame messages while
// preventing "double dropping" and other bad side effects.
virtual const BeginFrameArgs& LastUsedBeginFrameArgs() const = 0;
virtual void OnBeginFrameSourcePausedChanged(bool paused) = 0;
// Whether the observer also wants to receive animate_only BeginFrames.
virtual bool WantsAnimateOnlyBeginFrames() const = 0;
};
// Simple base class which implements a BeginFrameObserver which checks the
// incoming values meet the BeginFrameObserver requirements and implements the
// required LastUsedBeginFrameArgs behaviour.
//
// Users of this class should;
// - Implement the OnBeginFrameDerivedImpl function.
// - Recommended (but not required) to call
// BeginFrameObserverBase::OnValueInto in their overridden OnValueInto
// function.
class VIZ_COMMON_EXPORT BeginFrameObserverBase : public BeginFrameObserver {
public:
BeginFrameObserverBase();
BeginFrameObserverBase(const BeginFrameObserverBase&) = delete;
BeginFrameObserverBase& operator=(const BeginFrameObserverBase&) = delete;
~BeginFrameObserverBase() override;
// BeginFrameObserver
// Traces |args| and DCHECK |args| satisfies pre-conditions then calls
// OnBeginFrameDerivedImpl and updates the last_begin_frame_args_ value on
// true.
void OnBeginFrame(const BeginFrameArgs& args) override;
const BeginFrameArgs& LastUsedBeginFrameArgs() const override;
bool WantsAnimateOnlyBeginFrames() const override;
protected:
// Return true if the given argument is (or will be) used.
virtual bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) = 0;
void AsProtozeroInto(
perfetto::EventContext& ctx,
perfetto::protos::pbzero::BeginFrameObserverStateV2* state) const;
BeginFrameArgs last_begin_frame_args_;
int64_t dropped_begin_frame_args_ = 0;
bool wants_animate_only_begin_frames_ = false;
};
class VIZ_COMMON_EXPORT DynamicBeginFrameDeadlineOffsetSource {
public:
virtual ~DynamicBeginFrameDeadlineOffsetSource() = default;
virtual base::TimeDelta GetDeadlineOffset(base::TimeDelta interval) const = 0;
};
// Interface for a class which produces BeginFrame calls to a
// BeginFrameObserver.
//
// BeginFrame calls *normally* occur just after a vsync interrupt when input
// processing has been finished and provide information about the time values
// of the vsync times. *However*, these values can be heavily modified or even
// plain made up (when no vsync signal is available or vsync throttling is
// turned off). See the BeginFrameObserver for information about the guarantees
// all BeginFrameSources *must* provide.
class VIZ_COMMON_EXPORT BeginFrameSource {
public:
// The `SchedulerClient` will be notified of the `BeginFrame` after all
// `BeginFrameObservers` have first been notified. Thus guaranteeing that we
// will know the expected state of the observers for making scheduling
// decisions.
class VIZ_COMMON_EXPORT SchedulerClient {
public:
virtual ~SchedulerClient() = default;
virtual void OnBeginFrameForScheduling(const BeginFrameArgs& args) = 0;
};
BeginFrameSource();
class VIZ_COMMON_EXPORT BeginFrameArgsGenerator {
public:
BeginFrameArgsGenerator() = default;
~BeginFrameArgsGenerator() = default;
BeginFrameArgs GenerateBeginFrameArgs(uint64_t source_id,
base::TimeTicks frame_time,
base::TimeTicks deadline,
base::TimeDelta vsync_interval);
private:
static uint64_t EstimateTickCountsBetween(
base::TimeTicks frame_time,
base::TimeTicks next_expected_frame_time,
base::TimeDelta vsync_interval);
// Used for determining what the sequence number should be on
// CreateBeginFrameArgs.
base::TimeTicks next_expected_frame_time_;
// This is what the sequence number should be for any args created between
// |next_expected_frame_time_| to |next_expected_frame_time_| + vsync
// interval. Args created outside of this range will have their sequence
// number assigned relative to this, based on how many intervals the frame
// time is off.
uint64_t next_sequence_number_ = BeginFrameArgs::kStartingFrameNumber;
};
// This restart_id should be used for BeginFrameSources that don't have to
// worry about process restart. For example, if a BeginFrameSource won't
// generate and forward BeginFrameArgs to another process or the process can't
// crash then this constant is appropriate to use.
static constexpr uint32_t kNotRestartableId = 0;
// If the BeginFrameSource will generate BeginFrameArgs that are forwarded to
// another processes *and* this process has crashed then |restart_id| should
// be incremented. This ensures that |source_id_| is still unique after
// process restart.
explicit BeginFrameSource(uint32_t restart_id);
BeginFrameSource(const BeginFrameSource&) = delete;
BeginFrameSource& operator=(const BeginFrameSource&) = delete;
virtual ~BeginFrameSource();
// Returns an identifier for this BeginFrameSource. Guaranteed unique within a
// process, but not across processes. This is used to create BeginFrames that
// originate at this source. Note that BeginFrameSources may pass on
// BeginFrames created by other sources, with different IDs.
uint64_t source_id() const { return source_id_; }
// Sets whether the gpu is busy or not. See below the documentation for
// RequestCallbackOnGpuAvailable() for more details.
void SetIsGpuBusy(bool busy);
void SetSchedulerClient(SchedulerClient* scheduler_client);
// BeginFrameObservers use DidFinishFrame to provide back pressure to a frame
// source about frame processing (rather than toggling SetNeedsBeginFrames
// every frame). For example, the BackToBackFrameSource uses them to make sure
// only one frame is pending at a time.
virtual void DidFinishFrame(BeginFrameObserver* obs) = 0;
// Add/Remove an observer from the source. When no observers are added the BFS
// should shut down its timers, disable vsync, etc.
virtual void AddObserver(BeginFrameObserver* obs) = 0;
virtual void RemoveObserver(BeginFrameObserver* obs) = 0;
virtual void AsProtozeroInto(
perfetto::EventContext& ctx,
perfetto::protos::pbzero::BeginFrameSourceStateV2* state) const;
// Update the display ID for the source. This can change, e.g, as a window
// moves across displays.
virtual void SetVSyncDisplayID(int64_t display_id) {}
virtual void SetUpdateVSyncParametersCallback(
UpdateVSyncParametersCallback callback) {}
protected:
// Returns whether begin-frames to clients should be withheld (because the gpu
// is still busy, for example). If this returns true, then OnGpuNoLongerBusy()
// will be called once the gpu becomes available and the begin-frames can be
// dispatched to clients again.
bool RequestCallbackOnGpuAvailable();
virtual void OnGpuNoLongerBusy() = 0;
#if BUILDFLAG(IS_MAC)
void RecordBeginFrameSourceAccuracy(base::TimeDelta delta);
#endif
// Notify the `SchedulerClient` of the `BeginFrame`. This is to be called by
// subclasses only after having first called all observers.
void IssueBeginFrameToSchedulerClient(const BeginFrameArgs& args);
private:
// The higher 32 bits are used for a process restart id that changes if a
// process allocating BeginFrameSources has been restarted. The lower 32 bits
// are allocated from an atomic sequence.
const uint64_t source_id_;
// The BeginFrameSource should not send the begin-frame messages to clients if
// gpu is busy.
bool is_gpu_busy_ = false;
base::TimeTicks gpu_busy_start_time_;
// Keeps track of whether a begin-frame was paused, and whether
// OnGpuNoLongerBusy() should be invoked when the gpu is no longer busy.
enum class GpuBusyThrottlingState {
// No BeginFrames ticks were received since gpu was marked busy.
kIdle,
// One BeginFrame has been dispatched since gpu was marked busy.
kOneBeginFrameAfterBusySent,
// At least one BeginFrame was throttled since gpu was marked busy. If set
// to throttled state, the sub-class is informed to send the throttled
// BeginFrame once gpu is marked not busy.
kThrottled
};
GpuBusyThrottlingState gpu_busy_response_state_ =
GpuBusyThrottlingState::kIdle;
#if BUILDFLAG(IS_MAC)
base::TimeDelta total_delta_;
// The frame count since this histogram was recorded last time. It is recorded
// every 3600 frames, which is equivalent to every minute on a 60Hz monitors .
int frames_since_last_recording_ = 0;
#endif
raw_ptr<SchedulerClient> scheduler_client_ = nullptr;
};
// A BeginFrameSource that does nothing.
class VIZ_COMMON_EXPORT StubBeginFrameSource : public BeginFrameSource {
public:
StubBeginFrameSource();
void DidFinishFrame(BeginFrameObserver* obs) override {}
void AddObserver(BeginFrameObserver* obs) override {}
void RemoveObserver(BeginFrameObserver* obs) override {}
void OnGpuNoLongerBusy() override {}
};
// A frame source which ticks itself independently.
class VIZ_COMMON_EXPORT SyntheticBeginFrameSource : public BeginFrameSource {
public:
explicit SyntheticBeginFrameSource(uint32_t restart_id);
~SyntheticBeginFrameSource() override;
virtual void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) = 0;
// Sets the maximum interval allowable for use with VRR (variable refresh
// rates). When set, this value should correspond to the maximum vsync
// interval supported by the display. Absent when VRR is not enabled.
virtual void SetMaxVrrInterval(
const std::optional<base::TimeDelta>& max_vrr_interval) = 0;
};
// A frame source which calls BeginFrame (at the next possible time) as soon as
// an observer acknowledges the prior BeginFrame.
class VIZ_COMMON_EXPORT BackToBackBeginFrameSource
: public SyntheticBeginFrameSource,
public DelayBasedTimeSourceClient {
public:
explicit BackToBackBeginFrameSource(
std::unique_ptr<DelayBasedTimeSource> time_source);
BackToBackBeginFrameSource(const BackToBackBeginFrameSource&) = delete;
BackToBackBeginFrameSource& operator=(const BackToBackBeginFrameSource&) =
delete;
~BackToBackBeginFrameSource() override;
// BeginFrameSource implementation.
void AddObserver(BeginFrameObserver* obs) override;
void RemoveObserver(BeginFrameObserver* obs) override;
void DidFinishFrame(BeginFrameObserver* obs) override;
void OnGpuNoLongerBusy() override;
// SyntheticBeginFrameSource implementation.
void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override;
void SetMaxVrrInterval(
const std::optional<base::TimeDelta>& max_vrr_interval) override;
// DelayBasedTimeSourceClient implementation.
void OnTimerTick() override;
private:
std::unique_ptr<DelayBasedTimeSource> time_source_;
base::flat_set<raw_ptr<BeginFrameObserver, CtnExperimental>> observers_;
base::flat_set<raw_ptr<BeginFrameObserver, CtnExperimental>>
pending_begin_frame_observers_;
uint64_t next_sequence_number_;
base::TimeDelta vsync_interval_ = BeginFrameArgs::DefaultInterval();
std::optional<base::TimeDelta> max_vrr_interval_ = std::nullopt;
base::WeakPtrFactory<BackToBackBeginFrameSource> weak_factory_{this};
};
// A frame source which is locked to an external parameters provides from a
// vsync source and generates BeginFrameArgs for it.
class VIZ_COMMON_EXPORT DelayBasedBeginFrameSource
: public SyntheticBeginFrameSource,
public DelayBasedTimeSourceClient {
public:
DelayBasedBeginFrameSource(std::unique_ptr<DelayBasedTimeSource> time_source,
uint32_t restart_id);
DelayBasedBeginFrameSource(const DelayBasedBeginFrameSource&) = delete;
DelayBasedBeginFrameSource& operator=(const DelayBasedBeginFrameSource&) =
delete;
~DelayBasedBeginFrameSource() override;
// BeginFrameSource implementation.
void AddObserver(BeginFrameObserver* obs) override;
void RemoveObserver(BeginFrameObserver* obs) override;
void DidFinishFrame(BeginFrameObserver* obs) override {}
void OnGpuNoLongerBusy() override;
// SyntheticBeginFrameSource implementation.
void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override;
void SetMaxVrrInterval(
const std::optional<base::TimeDelta>& max_vrr_interval) override;
// DelayBasedTimeSourceClient implementation.
void OnTimerTick() override;
const BeginFrameArgs& last_begin_frame_args() const {
return last_begin_frame_args_;
}
const DelayBasedTimeSource* time_source() const { return time_source_.get(); }
private:
// The created BeginFrameArgs' sequence_number is calculated based on what
// interval |frame_time| is in. For example, if |last_frame_time_| is 100,
// |next_sequence_number_| is 5, |last_timebase_| is 110 and the interval
// is 20, then a |frame_time| of 175 would result in the sequence number
// being 8 (3 intervals since 110).
BeginFrameArgs CreateBeginFrameArgs(base::TimeTicks frame_time);
void IssueBeginFrameToObserver(BeginFrameObserver* obs,
const BeginFrameArgs& args);
void SetActive(bool active);
std::unique_ptr<DelayBasedTimeSource> time_source_;
base::flat_set<raw_ptr<BeginFrameObserver, CtnExperimental>> observers_;
base::TimeTicks last_timebase_;
base::TimeDelta last_vsync_interval_;
std::optional<base::TimeDelta> max_vrr_interval_ = std::nullopt;
int vrr_tick_count_ = 0;
BeginFrameArgs last_begin_frame_args_;
BeginFrameArgsGenerator begin_frame_args_generator_;
};
class VIZ_COMMON_EXPORT ExternalBeginFrameSourceClient {
public:
// Only called when changed. Assumed false by default.
virtual void OnNeedsBeginFrames(bool needs_begin_frames) = 0;
};
// A BeginFrameSource that is only ticked manually. Usually the endpoint
// of messages from some other thread/process that send OnBeginFrame and
// receive SetNeedsBeginFrame messages. This turns such messages back into
// an observable BeginFrameSource.
class VIZ_COMMON_EXPORT ExternalBeginFrameSource : public BeginFrameSource {
public:
// Client lifetime must be preserved by owner for the lifetime of the class.
// In order to allow derived classes to implement the client interface, no
// calls to |client| are made during construction / destruction.
explicit ExternalBeginFrameSource(ExternalBeginFrameSourceClient* client,
uint32_t restart_id = kNotRestartableId);
ExternalBeginFrameSource(const ExternalBeginFrameSource&) = delete;
ExternalBeginFrameSource& operator=(const ExternalBeginFrameSource&) = delete;
~ExternalBeginFrameSource() override;
// BeginFrameSource implementation.
void AddObserver(BeginFrameObserver* obs) override;
void RemoveObserver(BeginFrameObserver* obs) override;
void DidFinishFrame(BeginFrameObserver* obs) override {}
void AsProtozeroInto(
perfetto::EventContext& ctx,
perfetto::protos::pbzero::BeginFrameSourceStateV2* state) const override;
void OnGpuNoLongerBusy() override;
void OnSetBeginFrameSourcePaused(bool paused);
void OnBeginFrame(const BeginFrameArgs& args);
const BeginFrameArgs& last_begin_frame_args() const {
return last_begin_frame_args_;
}
#if BUILDFLAG(IS_ANDROID)
// Notifies when the refresh rate of the display is updated. |refresh_rate| is
// the rate in frames per second.
virtual void UpdateRefreshRate(float refresh_rate) {}
#endif
// Notifies the begin frame source of the desired frame interval for the
// observers.
virtual void SetPreferredInterval(base::TimeDelta interval) {}
// Returns the minimium supported frame interval for a given BFS.
// This gives the maximium refresh rate that can be requested.
virtual base::TimeDelta GetMinimumFrameInterval();
virtual base::flat_set<base::TimeDelta> GetSupportedFrameIntervals(
base::TimeDelta interval);
protected:
// Called on AddObserver and gets missed BeginFrameArgs for the given
// observer. The missed BeginFrame is sent only if the returned
// BeginFrameArgs is valid.
virtual BeginFrameArgs GetMissedBeginFrameArgs(BeginFrameObserver* obs);
BeginFrameArgs last_begin_frame_args_;
base::flat_set<raw_ptr<BeginFrameObserver, CtnExperimental>> observers_;
raw_ptr<ExternalBeginFrameSourceClient> client_;
bool paused_ = false;
private:
BeginFrameArgs pending_begin_frame_args_;
base::MetricsSubSampler metrics_sub_sampler_;
};
} // namespace viz
#endif // COMPONENTS_VIZ_COMMON_FRAME_SINKS_BEGIN_FRAME_SOURCE_H_