blob: 7e80077cf59d815c70769b018c68fb9e11d35963 [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 "lib/inspect/cpp/inspect.h"
#include "src/lib/cobalt/cpp/cobalt_logger.h"
#include "src/lib/fxl/macros.h"
#include "src/lib/fxl/memory/weak_ptr.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"
#include "src/ui/scenic/lib/utils/sequential_fence_signaller.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::Node inspect_node = inspect::Node(),
std::shared_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|
PresentId RegisterPresent(SessionId session_id,
std::variant<OnPresentedCallback, Present2Info> present_information,
std::vector<zx::event> release_fences,
PresentId present_id = 0) 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 requested_presentation_time,
SchedulingIdPair id_pair) 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;
// |FrameScheduler|
//
// Remove all state associated with a given session_id.
void RemoveSession(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;
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(zx::time requested_presentation_time);
// Check if there are pending updates, and if there are then find the next lowest requested
// presentation time and uses it to request another frame.
void HandleNextFrameRequest();
// 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.
bool ApplyUpdates(zx::time target_presentation_time, zx::time latched_time,
uint64_t frame_number);
// Return true if there are any scheduled session updates that have not yet been applied.
bool HaveUpdatableSessions() const { return !pending_present_requests_.empty(); }
// Signal all callbacks prepared on frames up to |frame_number|.
void SignalPresentCallbacksUpTo(uint64_t frame_number,
fuchsia::images::PresentationInfo presentation_info);
// Signal all Present1 callbacks for |id_pair.session| up to |id_pair.present_id|.
void SignalPresent1CallbacksUpTo(SchedulingIdPair id_pair,
fuchsia::images::PresentationInfo presentation_info);
// Signal all Present2 callbacks for |id_pair.session| up to |id_pair.present_id|.
void SignalPresent2CallbackForInfosUpTo(SchedulingIdPair id_pair, zx::time presented_time);
// Set all unset latched times for each Present2Info of |session_id|, up to and including
// |present_id|.
void SetLatchedTimeForPresent2Infos(SchedulingIdPair id_pair, zx::time latched_time);
// Move all fences before |present_id| to the signaller to be signalled at next OnFrameRendered.
void MoveReleaseFencesToSignaller(SchedulingIdPair id_pair, uint64_t frame_number);
// Extracts all presents that should be updated this frame and returns them as a map of SessionIds
// to the last PresentId that should be updated for that session.
std::unordered_map<SessionId, PresentId> CollectUpdatesForThisFrame(
zx::time target_presentation_time);
// Prepares all per-present data for later OnFrameRendered and OnFramePresented events.
void PrepareUpdates(const std::unordered_map<SessionId, PresentId>& updates,
zx::time latched_time, uint64_t frame_number);
// Cycles through SessionUpdaters, applies updates to each and coalesces their responses.
SessionUpdater::UpdateResults ApplyUpdatesToEachUpdater(
const std::unordered_map<SessionId, PresentId>& sessions_to_update, uint64_t frame_number);
// Removes all references to each session passed in and calls their
// OnSessionUpdateFailedCallbacks.
void RemoveFailedSessions(const std::unordered_set<SessionId>& sessions_with_failed_updates);
// Map of all pending Present calls ordered by SessionId and then PresentId. Maps to requested
// presentation time for each present.
std::map<SchedulingIdPair, zx::time> pending_present_requests_;
struct FrameUpdate {
uint64_t frame_number;
std::unordered_map<SessionId, PresentId> updated_sessions;
};
// Queue of session updates mapped to frame numbers. Used when triggering callbacks in
// OnFramePresented.
std::queue<FrameUpdate> handled_updates_;
// Ordered maps of per-present data ordered by SessionId and then PresentId.
// Per-present callbacks for Present1 and ImagePipe clients.
std::map<SchedulingIdPair, OnPresentedCallback> present1_callbacks_;
// Per-present info for Present2 clients, to be coalesced before used in callback.
std::map<SchedulingIdPair, Present2Info> present2_infos_;
// Per-present release fences. To be released as each subsequent present for each session is
// rendered.
std::map<SchedulingIdPair, std::vector<zx::event>> release_fences_;
// Map of registered callbacks for Present2 sessions.
std::unordered_map<SessionId, OnFramePresentedCallback> present2_callback_map_;
// Map of callbacks to fire when a Session update fails.
std::unordered_map<SessionId, FrameScheduler::OnSessionUpdateFailedCallback>
update_failed_callback_map_;
utils::SequentialFenceSignaller release_fence_signaller_;
// 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.
// Exists to protect against when new session updaters are added mid-update.
std::list<fxl::WeakPtr<SessionUpdater>> new_session_updaters_;
// 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_target_presentation_time_;
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::Node inspect_node_;
inspect::UintProperty inspect_frame_number_;
inspect::UintProperty inspect_last_successful_update_start_time_;
inspect::UintProperty inspect_last_successful_render_start_time_;
FrameStats stats_;
// For tracing.
uint64_t frame_render_trace_id_ = 0;
// Maps wakeup time to trace IDs, to properly match up renders for frames >1 vsyncs away.
uint64_t request_to_render_count_ = 0;
std::multimap<zx::time, uint64_t> render_wakeup_map_ = {};
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_