blob: 8b8a9a38866901ca72b8498f1bb9574314f4294f [file] [log] [blame]
// Copyright 2017 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 GARNET_LIB_UI_GFX_ENGINE_FRAME_SCHEDULER_H_
#define GARNET_LIB_UI_GFX_ENGINE_FRAME_SCHEDULER_H_
#include <queue>
#include <lib/async/dispatcher.h>
#include <lib/zx/time.h>
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_ptr.h"
#include "lib/fxl/memory/weak_ptr.h"
namespace scenic_impl {
namespace gfx {
class Display;
class FrameTimings;
using FrameTimingsPtr = fxl::RefPtr<FrameTimings>;
// Interface implemented by the engine to perform per-frame processing in
// response to a frame being scheduled.
class FrameSchedulerDelegate {
public:
virtual ~FrameSchedulerDelegate() = default;
// Called when it's time to apply changes to the scene graph and render
// a new frame. The FrameTimings object is used to accumulate timing
// for all swapchains that are used as render targets in that frame.
//
// If RenderFrame() returns true, the delegate is responsible for calling
// FrameTimings::OnFrameRendered/Presented/Dropped(). Otherwise, rendering
// did not occur for some reason, and the FrameScheduler should not expect to
// receive any timing information for that frame.
// TODO(SCN-1089): these return value semantics are not ideal. See comments
// in Engine::RenderFrame() regarding this same issue.
//
// TODO(MZ-225): We need to track backpressure so that the frame scheduler
// doesn't get too far ahead. With that in mind, Renderer::DrawFrame should
// have a callback which is invoked when the frame is fully flushed through
// the graphics pipeline. Then Engine::RenderFrame itself should have a
// callback which is invoked when all renderers finish work for that frame.
// Then FrameScheduler should listen to the callback to count how many
// frames are in flight and back off.
virtual bool RenderFrame(const FrameTimingsPtr& frame_timings,
uint64_t presentation_time,
uint64_t presentation_interval,
bool force_render) = 0;
};
// The FrameScheduler is responsible for scheduling frames to be drawn in
// response to requests from clients. When a frame is requested, the
// FrameScheduler will decide at which Vsync the frame should be displayed at.
// This time will be no earlier than the requested time, and will be as close
// as possible to the requested time, subject to various constraints. For
// example, if the requested time is earlier than the time that rendering would
// finish, were it started immediately, then the frame will be scheduled for a
// later Vsync.
class FrameScheduler {
public:
explicit FrameScheduler(Display* display);
~FrameScheduler();
void set_delegate(FrameSchedulerDelegate* delegate) { delegate_ = delegate; }
// Request a frame to be scheduled at or after |presentation_time|, which
// may be in the past.
void RequestFrame(zx_time_t presentation_time);
// If |render_continuously|, we keep rendering frames regardless of whether
// they're requested using RequestFrame().
void SetRenderContinuously(bool render_continuously);
// Helper method for ScheduleFrame(). Returns 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.
std::pair<zx_time_t, zx_time_t> ComputeTargetPresentationAndWakeupTimes(
zx_time_t requested_presentation_time) const;
private:
// 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, after this frame
// was scheduled, another frame was scheduled to be rendered at an earlier
// time, and not enough time has elapsed to render this frame. Etc.
void MaybeRenderFrame(zx_time_t presentation_time, zx_time_t wakeup_time);
// Schedule a frame for the earliest of |requested_presentation_times_|. The
// scheduled time will be the earliest achievable time, such that rendering
// can start early enough to hit the next Vsync.
void ScheduleFrame();
// Returns true to apply back-pressure when we cannot hit our target frame
// rate. Otherwise, return false to indicate that it is OK to immediately
// render a frame.
bool TooMuchBackPressure();
// Helper method for ScheduleFrame(). Returns the target presentation time
// for the next frame, and a wake-up time that is early enough to start
// rendering in order to hit the target presentation time.
std::pair<zx_time_t, zx_time_t> ComputeNextPresentationAndWakeupTimes() const;
// Return the predicted amount of time required to render a frame.
zx_time_t PredictRequiredFrameRenderTime() const;
// Called by the delegate when the frame drawn by RenderFrame() has been
// presented to the display.
friend class FrameTimings;
void OnFramePresented(FrameTimings* timings);
async_dispatcher_t* const dispatcher_;
FrameSchedulerDelegate* delegate_;
Display* const display_;
std::priority_queue<zx_time_t, std::vector<zx_time_t>,
std::greater<zx_time_t>>
requested_presentation_times_;
uint64_t frame_number_ = 0;
constexpr static size_t kMaxOutstandingFrames = 2;
std::vector<FrameTimingsPtr> outstanding_frames_;
bool back_pressure_applied_ = false;
bool render_continuously_ = false;
fxl::WeakPtrFactory<FrameScheduler> weak_factory_; // must be last
FXL_DISALLOW_COPY_AND_ASSIGN(FrameScheduler);
};
} // namespace gfx
} // namespace scenic_impl
#endif // GARNET_LIB_UI_GFX_ENGINE_FRAME_SCHEDULER_H_