| // 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_ |