blob: bb05773caabc841ebb3d5f5e9784c139e930f42f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_SCENIC_LIB_SCHEDULING_DEFAULT_FRAME_SCHEDULER_H_
#define SRC_UI_SCENIC_LIB_SCHEDULING_DEFAULT_FRAME_SCHEDULER_H_
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/zx/time.h>
#include <list>
#include "src/lib/cobalt/cpp/cobalt_logger.h"
#include "src/lib/fxl/macros.h"
#include "src/lib/fxl/memory/weak_ptr.h"
#include "src/lib/inspect_deprecated/inspect.h"
#include "src/ui/scenic/lib/scheduling/frame_predictor.h"
#include "src/ui/scenic/lib/scheduling/frame_scheduler.h"
#include "src/ui/scenic/lib/scheduling/frame_stats.h"
#include "src/ui/scenic/lib/scheduling/id.h"
#include "src/ui/scenic/lib/scheduling/vsync_timing.h"
namespace scheduling {
// TODOs can be found in the frame scheduler epic: SCN-1202. Any new bugs filed concerning the frame
// scheduler should be added to it as well.
class DefaultFrameScheduler : public FrameScheduler {
public:
explicit DefaultFrameScheduler(std::shared_ptr<const VsyncTiming> vsync_timing,
std::unique_ptr<FramePredictor> predictor,
inspect_deprecated::Node inspect_node = inspect_deprecated::Node(),
std::unique_ptr<cobalt::CobaltLogger> cobalt_logger = nullptr);
~DefaultFrameScheduler();
// |FrameScheduler|
void SetFrameRenderer(fxl::WeakPtr<FrameRenderer> frame_renderer) override;
// |FrameScheduler|
void AddSessionUpdater(fxl::WeakPtr<SessionUpdater> session_updater) override;
// |FrameScheduler|
//
// If |render_continuously|, we keep rendering frames regardless of whether they're requested
// using RequestFrame().
void SetRenderContinuously(bool render_continuously) override;
// |FrameScheduler|
void SetOnUpdateFailedCallbackForSession(
SessionId session, OnSessionUpdateFailedCallback update_failed_callback) override;
// |FrameScheduler|
//
// Tell the FrameScheduler to schedule a frame. This is also used for updates triggered by
// something other than a Session update i.e. an ImagePipe with a new Image to present.
void ScheduleUpdateForSession(zx::time presentation_time, SessionId session) override;
// |FrameScheduler|
//
// Sets the |fuchsia::ui::scenic::Session::OnFramePresented| event handler. This should only be
// called once per session.
void SetOnFramePresentedCallbackForSession(
SessionId session, OnFramePresentedCallback frame_presented_callback) override;
// |FrameScheduler|
void GetFuturePresentationInfos(
zx::duration requested_prediction_span,
FrameScheduler::GetFuturePresentationInfosCallback presentation_infos_callback) override;
void ClearCallbacksForSession(SessionId session_id) override;
constexpr static zx::duration kMinPredictedFrameDuration = zx::msec(0);
constexpr static zx::duration kInitialRenderDuration = zx::msec(5);
constexpr static zx::duration kInitialUpdateDuration = zx::msec(1);
// Public for testing.
constexpr static size_t kMaxOutstandingFrames = 2;
// Helper class that manages:
// - registration of SessionUpdaters
// - tracking callbacks that need to be invoked.
class UpdateManager {
public:
UpdateManager() = default;
// Add |session_updater| to the list of updaters on which |UpdateSessions()| and
// |PrepareFrame()| will be invoked.
void AddSessionUpdater(fxl::WeakPtr<SessionUpdater> session_updater);
// Schedules an update for the specified session. All updaters registered by
// |AddSessionUpdater()| are notified when |ApplyUpdates()| is called with an equal or later
// presentation time.
void ScheduleUpdate(zx::time presentation_time, SessionId session);
// Returned by |ApplyUpdates()|; used by a |FrameScheduler| to decide whether to render a frame
// and/or schedule another frame to be rendered.
struct ApplyUpdatesResult {
bool needs_render;
bool needs_reschedule;
};
// Calls |SessionUpdater::UpdateSessions()| on all updaters, and uses the returned
// |SessionUpdater::UpdateResults| to generate the returned |ApplyUpdatesResult|.
ApplyUpdatesResult ApplyUpdates(zx::time target_presentation_time, zx::time latched_time,
zx::duration vsync_interval, uint64_t frame_number);
// Return true if there are any scheduled session updates that have not yet been applied.
bool HasUpdatableSessions() const { return !updatable_sessions_.empty(); }
zx::time EarliestRequestedPresentationTime() const {
FXL_DCHECK(HasUpdatableSessions());
return updatable_sessions_.top().requested_presentation_time;
}
// Creates a ratchet point for the updater. All present calls that were updated before this
// point will be signaled with the next call to |SignalPresentCallbacks()|.
void RatchetPresentCallbacks(zx::time presentation_time, uint64_t frame_number);
// Signal that all updates before the last ratchet point have been presented. The signaled
// callbacks are every successful present between the last time |SignalPresentCallbacks()| was
// called and the most recent call to |RatchetPresentCallbacks()|.
void SignalPresentCallbacks(fuchsia::images::PresentationInfo info);
// Sets a callback to handle a failed session update. This should only be
// called once per session.
void SetOnUpdateFailedCallbackForSession(
SessionId session, FrameScheduler::OnSessionUpdateFailedCallback update_failed_callback);
// Sets the |fuchsia::ui::scenic::Session::OnFramePresented| event handler. This should only be
// called once per session.
void SetOnFramePresentedCallbackForSession(SessionId session,
OnFramePresentedCallback frame_presented_callback);
// Clears the cached callbacks set per-session.
void ClearCallbacksForSession(SessionId session_id);
private:
// Remove all state associated with a given session_id.
void RemoveSession(SessionId session_id);
// Sessions that have updates to apply, sorted by requested presentation time from earliest to
// latest.
struct SessionUpdate {
SessionId session_id;
zx::time requested_presentation_time;
bool operator>(const SessionUpdate& rhs) const {
return requested_presentation_time > rhs.requested_presentation_time;
}
};
std::priority_queue<SessionUpdate, std::vector<SessionUpdate>, std::greater<SessionUpdate>>
updatable_sessions_;
std::multimap<SessionId, OnPresentedCallback> present1_callbacks_this_frame_;
std::multimap<SessionId, OnPresentedCallback> pending_present1_callbacks_;
std::multimap<SessionId, Present2Info> present2_infos_this_frame_;
std::multimap<SessionId, Present2Info> pending_present2_infos_;
std::map<SessionId, OnFramePresentedCallback> present2_callback_map_;
std::map<SessionId, FrameScheduler::OnSessionUpdateFailedCallback> update_failed_callback_map_;
// Set of SessionUpdaters to update. Stored as a WeakPtr: when the updaters become
// invalid, the WeakPtr is removed from this list.
std::vector<fxl::WeakPtr<SessionUpdater>> session_updaters_;
// Stores SessionUpdaters we added to the DefaultFrameScheduler. Upon
// ApplyUpdates() is called, these SessionUpdaters will be moved to
// the |session_updaters_| vector.
std::list<fxl::WeakPtr<SessionUpdater>> new_session_updaters_;
};
protected:
void OnFramePresented(const FrameTimings& timings);
void OnFrameRendered(const FrameTimings& timings);
private:
// Requests a new frame to be drawn, which schedules the next wake up time for rendering. If we've
// already scheduled a wake up time, it checks if it needs rescheduling and deals with it
// appropriately.
void RequestFrame();
// Update the global scene and then draw it... maybe. There are multiple reasons why this might
// not happen. For example, the swapchain might apply back-pressure if we can't hit our target
// frame rate. Or, the frame before this one has yet to finish rendering. Etc.
void MaybeRenderFrame(async_dispatcher_t*, async::TaskBase*, zx_status_t);
// Computes the target presentation time for the requested presentation time, and a wake-up time
// that is early enough to start rendering in order to hit the target presentation time. These
// times are guaranteed to be in the future.
std::pair<zx::time, zx::time> ComputePresentationAndWakeupTimesForTargetTime(
zx::time requested_presentation_time) const;
// Executes updates that are scheduled up to and including a given presentation time.
UpdateManager::ApplyUpdatesResult ApplyUpdates(zx::time target_presentation_time,
zx::time latched_time);
// References.
async_dispatcher_t* const dispatcher_;
const std::shared_ptr<const VsyncTiming> vsync_timing_;
fxl::WeakPtr<FrameRenderer> frame_renderer_;
// State.
uint64_t frame_number_ = 0;
std::vector<std::unique_ptr<FrameTimings>> outstanding_frames_;
bool render_continuously_ = false;
bool currently_rendering_ = false;
bool render_pending_ = false;
zx::time wakeup_time_;
zx::time next_presentation_time_;
UpdateManager update_manager_;
std::unique_ptr<FramePredictor> frame_predictor_;
// The async task that wakes up to start rendering.
async::TaskMethod<DefaultFrameScheduler, &DefaultFrameScheduler::MaybeRenderFrame>
frame_render_task_{this};
inspect_deprecated::Node inspect_node_;
inspect_deprecated::UIntMetric inspect_frame_number_;
inspect_deprecated::UIntMetric inspect_last_successful_update_start_time_;
inspect_deprecated::UIntMetric inspect_last_successful_render_start_time_;
FrameStats stats_;
fxl::WeakPtrFactory<DefaultFrameScheduler> weak_factory_; // must be last
FXL_DISALLOW_COPY_AND_ASSIGN(DefaultFrameScheduler);
};
} // namespace scheduling
#endif // SRC_UI_SCENIC_LIB_SCHEDULING_DEFAULT_FRAME_SCHEDULER_H_