[scenic] Isolate FrameScheduler
This CL:
* Collect scheduling logic in FrameScheduler
* Decouple Session updates from calls to RenderFrame()
* Simplify program flow from Present() to RenderFrame():
Previously: Client Session -> SessionHandler -> gfx::Session
-> SessionManager -> Engine -> FrameScheduler -> Engine
-> SessionManager -> gfx::Session
Now: Client Session -> SessionHandler -> gfx::Session
-> FrameScheduler -> Engine -> gfx::Session
Change-Id: I3000b19fe7836a34595354eea57fe34744fa45eb
diff --git a/garnet/lib/ui/gfx/BUILD.gn b/garnet/lib/ui/gfx/BUILD.gn
index aea4a5c..67837d5 100644
--- a/garnet/lib/ui/gfx/BUILD.gn
+++ b/garnet/lib/ui/gfx/BUILD.gn
@@ -67,13 +67,14 @@
"displays/display_manager.h",
"displays/display_watcher.cc",
"displays/display_watcher.h",
+ "engine/default_frame_scheduler.cc",
+ "engine/default_frame_scheduler.h",
"engine/engine.cc",
"engine/engine.h",
"engine/engine_renderer.cc",
"engine/engine_renderer.h",
"engine/engine_renderer_visitor.cc",
"engine/engine_renderer_visitor.h",
- "engine/frame_scheduler.cc",
"engine/frame_scheduler.h",
"engine/frame_timings.cc",
"engine/frame_timings.h",
@@ -98,7 +99,6 @@
"engine/session_manager.h",
"engine/unresolved_imports.cc",
"engine/unresolved_imports.h",
- "engine/update_scheduler.h",
"gfx_system.cc",
"gfx_system.h",
"id.cc",
diff --git a/garnet/lib/ui/gfx/displays/display.h b/garnet/lib/ui/gfx/displays/display.h
index e3072176..511bf83 100644
--- a/garnet/lib/ui/gfx/displays/display.h
+++ b/garnet/lib/ui/gfx/displays/display.h
@@ -47,7 +47,7 @@
// Temporary friendship to allow FrameScheduler to feed back the Vsync timings
// gleaned from EventTimestamper. This should go away once we receive real
// VSync times from the display driver.
- friend class FrameScheduler;
+ friend class DefaultFrameScheduler;
void set_last_vsync_time(zx_time_t vsync_time);
zx_time_t last_vsync_time_;
diff --git a/garnet/lib/ui/gfx/engine/frame_scheduler.cc b/garnet/lib/ui/gfx/engine/default_frame_scheduler.cc
similarity index 79%
rename from garnet/lib/ui/gfx/engine/frame_scheduler.cc
rename to garnet/lib/ui/gfx/engine/default_frame_scheduler.cc
index 9631f77f..907b0e7 100644
--- a/garnet/lib/ui/gfx/engine/frame_scheduler.cc
+++ b/garnet/lib/ui/gfx/engine/default_frame_scheduler.cc
@@ -1,8 +1,8 @@
-// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// 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.
-#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
+#include "garnet/lib/ui/gfx/engine/default_frame_scheduler.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
@@ -17,16 +17,16 @@
namespace scenic_impl {
namespace gfx {
-FrameScheduler::FrameScheduler(Display* display)
+DefaultFrameScheduler::DefaultFrameScheduler(Display* display)
: dispatcher_(async_get_default_dispatcher()),
display_(display),
weak_factory_(this) {
outstanding_frames_.reserve(kMaxOutstandingFrames);
}
-FrameScheduler::~FrameScheduler() {}
+DefaultFrameScheduler::~DefaultFrameScheduler() {}
-void FrameScheduler::RequestFrame(zx_time_t presentation_time) {
+void DefaultFrameScheduler::RequestFrame(zx_time_t presentation_time) {
const bool should_schedule_frame =
requested_presentation_times_.empty() ||
requested_presentation_times_.top() > presentation_time;
@@ -36,14 +36,14 @@
}
}
-void FrameScheduler::SetRenderContinuously(bool render_continuously) {
+void DefaultFrameScheduler::SetRenderContinuously(bool render_continuously) {
render_continuously_ = render_continuously;
if (render_continuously_) {
RequestFrame(0);
}
}
-zx_time_t FrameScheduler::PredictRequiredFrameRenderTime() const {
+zx_time_t DefaultFrameScheduler::PredictRequiredFrameRenderTime() const {
// TODO(MZ-400): more sophisticated prediction. This might require more info,
// e.g. about how many compositors will be rendering scenes, at what
// resolutions, etc.
@@ -52,14 +52,14 @@
}
std::pair<zx_time_t, zx_time_t>
-FrameScheduler::ComputeNextPresentationAndWakeupTimes() const {
+DefaultFrameScheduler::ComputeNextPresentationAndWakeupTimes() const {
FXL_DCHECK(!requested_presentation_times_.empty());
return ComputeTargetPresentationAndWakeupTimes(
requested_presentation_times_.top());
}
std::pair<zx_time_t, zx_time_t>
-FrameScheduler::ComputeTargetPresentationAndWakeupTimes(
+DefaultFrameScheduler::ComputeTargetPresentationAndWakeupTimes(
const zx_time_t requested_presentation_time) const {
const zx_time_t last_vsync_time = display_->GetLastVsyncTime();
const zx_time_t vsync_interval = display_->GetVsyncInterval();
@@ -140,7 +140,7 @@
#endif
}
-void FrameScheduler::ScheduleFrame() {
+void DefaultFrameScheduler::ScheduleFrame() {
FXL_DCHECK(!requested_presentation_times_.empty());
auto times = ComputeNextPresentationAndWakeupTimes();
@@ -156,8 +156,8 @@
zx::time(0) + zx::nsec(wakeup_time));
}
-void FrameScheduler::MaybeRenderFrame(zx_time_t presentation_time,
- zx_time_t wakeup_time) {
+void DefaultFrameScheduler::MaybeRenderFrame(zx_time_t presentation_time,
+ zx_time_t wakeup_time) {
if (requested_presentation_times_.empty()) {
// No frame was requested, so none needs to be rendered. More precisely, a
// frame must have been requested (otherwise ScheduleFrame() would not
@@ -169,8 +169,9 @@
if (TooMuchBackPressure()) {
// No need to request another frame; ScheduleFrame() will be called
// when the back-pressure is relieved.
- FXL_VLOG(2) << "FrameScheduler::MaybeRenderFrame(): dropping frame, too "
- "much back-pressure.";
+ FXL_VLOG(2)
+ << "DefaultFrameScheduler::MaybeRenderFrame(): dropping frame, too "
+ "much back-pressure.";
return;
}
@@ -193,14 +194,24 @@
requested_presentation_times_.pop();
}
- // Go render the frame.
- if (delegate_) {
+ if (delegate_.frame_renderer && delegate_.session_updater) {
FXL_DCHECK(outstanding_frames_.size() < kMaxOutstandingFrames);
+
auto frame_timings = fxl::MakeRefCounted<FrameTimings>(
this, ++frame_number_, presentation_time);
- if (delegate_->RenderFrame(frame_timings, presentation_time,
- display_->GetVsyncInterval(),
- render_continuously_)) {
+
+ // Apply all updates
+ bool any_updates_were_applied = ApplyScheduledSessionUpdates(
+ frame_timings->frame_number(), presentation_time,
+ display_->GetVsyncInterval());
+
+ if (!any_updates_were_applied && !render_continuously_) {
+ return;
+ }
+
+ // Render the frame
+ if (delegate_.frame_renderer->RenderFrame(frame_timings, presentation_time,
+ display_->GetVsyncInterval())) {
outstanding_frames_.push_back(frame_timings);
}
}
@@ -211,7 +222,40 @@
}
}
-void FrameScheduler::OnFramePresented(FrameTimings* timings) {
+void DefaultFrameScheduler::ScheduleUpdateForSession(
+ uint64_t presentation_time, scenic_impl::SessionId session_id) {
+ updatable_sessions_.push({presentation_time, session_id});
+ RequestFrame(presentation_time);
+}
+
+bool DefaultFrameScheduler::ApplyScheduledSessionUpdates(
+ uint64_t frame_number, uint64_t presentation_time,
+ uint64_t presentation_interval) {
+ FXL_DCHECK(delegate_.session_updater);
+
+ TRACE_DURATION("gfx", "ApplyScheduledSessionUpdates", "time",
+ presentation_time, "interval", presentation_interval);
+
+ std::vector<scenic_impl::SessionId> sessions_to_update;
+ while (!updatable_sessions_.empty()) {
+ auto& top = updatable_sessions_.top();
+
+ if (top.first > presentation_time) {
+ break;
+ }
+
+ sessions_to_update.push_back(std::move(top.second));
+ updatable_sessions_.pop();
+ }
+
+ bool needs_render = delegate_.session_updater->UpdateSessions(
+ std::move(sessions_to_update), frame_number, presentation_time,
+ presentation_interval);
+
+ return needs_render;
+}
+
+void DefaultFrameScheduler::OnFramePresented(FrameTimings* timings) {
FXL_DCHECK(!outstanding_frames_.empty());
// TODO(MZ-400): how should we handle this case? It is theoretically
// possible, but if if it happens then it means that the EventTimestamper is
@@ -267,7 +311,7 @@
}
}
-bool FrameScheduler::TooMuchBackPressure() {
+bool DefaultFrameScheduler::TooMuchBackPressure() {
if (outstanding_frames_.size() >= kMaxOutstandingFrames) {
back_pressure_applied_ = true;
return true;
diff --git a/garnet/lib/ui/gfx/engine/default_frame_scheduler.h b/garnet/lib/ui/gfx/engine/default_frame_scheduler.h
new file mode 100644
index 0000000..3eb0ba0
--- /dev/null
+++ b/garnet/lib/ui/gfx/engine/default_frame_scheduler.h
@@ -0,0 +1,148 @@
+// 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 GARNET_LIB_UI_GFX_ENGINE_DEFAULT_FRAME_SCHEDULER_H_
+#define GARNET_LIB_UI_GFX_ENGINE_DEFAULT_FRAME_SCHEDULER_H_
+
+#include <queue>
+
+#include <lib/async/dispatcher.h>
+#include <lib/zx/time.h>
+#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
+#include "garnet/lib/ui/gfx/id.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 DefaultFrameScheduler : public FrameScheduler {
+ public:
+ explicit DefaultFrameScheduler(Display* display);
+ ~DefaultFrameScheduler();
+
+ // |FrameScheduler|
+ //
+ // 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 override;
+
+ // |FrameScheduler|
+ void SetDelegate(FrameSchedulerDelegate delegate) override {
+ delegate_ = delegate;
+ };
+
+ // |FrameScheduler|
+ //
+ // If |render_continuously|, we keep rendering frames regardless of whether
+ // they're requested using RequestFrame().
+ void SetRenderContinuously(bool render_continuously) 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(uint64_t presentation_time,
+ scenic_impl::SessionId session) override;
+
+ protected:
+ // |FrameScheduler|
+ void OnFramePresented(FrameTimings* timings) override;
+
+ private:
+ // Used to compare presentation times so that the priority_queue acts as a min
+ // heap, placing the earliest PresentationTime at the top
+ class UpdatableSessionsComparator {
+ public:
+ bool operator()(
+ std::pair<PresentationTime, scenic_impl::SessionId> updatable_session1,
+ std::pair<PresentationTime, scenic_impl::SessionId>
+ updatable_session2) {
+ return updatable_session1.first > updatable_session2.first;
+ }
+ };
+
+ // Request a frame to be scheduled at or after |presentation_time|, which
+ // may be in the past.
+ void RequestFrame(zx_time_t presentation_time);
+
+ // 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.
+ // 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
+ 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;
+
+ // Executes updates that are scheduled up to and including a given
+ // presentation time. Returns true if rendering is needed.
+ bool ApplyScheduledSessionUpdates(uint64_t frame_number,
+ uint64_t presentation_time,
+ uint64_t presentation_interval);
+
+ // Apply updates to all sessions who have updates and have acquired all
+ // fences. Return true if there were any updates applied.
+ bool UpdateSessions(uint64_t presentation_time,
+ uint64_t presentation_interval,
+ uint64_t frame_number_for_tracing);
+
+ async_dispatcher_t* const dispatcher_;
+ 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;
+
+ // Lists all Session that have updates to apply, sorted by the earliest
+ // requested presentation time of each update.
+ std::priority_queue<
+ std::pair<PresentationTime, scenic_impl::SessionId>,
+ std::vector<std::pair<PresentationTime, scenic_impl::SessionId>>,
+ UpdatableSessionsComparator>
+ updatable_sessions_;
+
+ FrameSchedulerDelegate delegate_;
+
+ fxl::WeakPtrFactory<DefaultFrameScheduler> weak_factory_; // must be last
+
+ FXL_DISALLOW_COPY_AND_ASSIGN(DefaultFrameScheduler);
+};
+
+} // namespace gfx
+} // namespace scenic_impl
+
+#endif // GARNET_LIB_UI_GFX_ENGINE_DEFAULT_FRAME_SCHEDULER_H_
\ No newline at end of file
diff --git a/garnet/lib/ui/gfx/engine/engine.cc b/garnet/lib/ui/gfx/engine/engine.cc
index 2a4c14f..65e3321 100644
--- a/garnet/lib/ui/gfx/engine/engine.cc
+++ b/garnet/lib/ui/gfx/engine/engine.cc
@@ -13,7 +13,6 @@
#include <trace/event.h>
#include <zx/time.h>
-#include "garnet/lib/ui/gfx/engine/engine_renderer.h"
#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
#include "garnet/lib/ui/gfx/engine/frame_timings.h"
#include "garnet/lib/ui/gfx/engine/hardware_layer_assignment.h"
@@ -29,6 +28,19 @@
namespace scenic_impl {
namespace gfx {
+CommandContext::CommandContext(
+ std::unique_ptr<escher::BatchGpuUploader> uploader)
+ : batch_gpu_uploader_(std::move(uploader)) {}
+
+void CommandContext::Flush() {
+ if (batch_gpu_uploader_) {
+ // Submit regardless of whether or not there are updates to release the
+ // underlying CommandBuffer so the pool and sequencer don't stall out.
+ // TODO(ES-115) to remove this restriction.
+ batch_gpu_uploader_->Submit();
+ }
+}
+
Engine::Engine(std::unique_ptr<FrameScheduler> frame_scheduler,
DisplayManager* display_manager,
escher::EscherWeakPtr weak_escher)
@@ -47,13 +59,11 @@
escher()->vk_physical_device(), escher()->vk_device())),
has_vulkan_(escher_ && escher_->vk_device()),
weak_factory_(this) {
+ FXL_DCHECK(frame_scheduler_);
FXL_DCHECK(display_manager_);
FXL_DCHECK(escher_);
- // TODO(SCN-1092): make |frame_scheduler_| non-nullable. For testing, this
- // might entail plugging in a dummy Display. Relates to SCN-452.
- if (frame_scheduler_)
- frame_scheduler_->set_delegate(this);
+ InitializeFrameScheduler();
}
Engine::Engine(
@@ -73,49 +83,16 @@
: 0),
has_vulkan_(escher_ && escher_->vk_device()),
weak_factory_(this) {
+ FXL_DCHECK(frame_scheduler_);
FXL_DCHECK(display_manager_);
- // TODO(SCN-1092): make |frame_scheduler_| non-nullable. For testing, this
- // might entail plugging in a dummy Display. Relates to SCN-452.
- if (frame_scheduler_)
- frame_scheduler_->set_delegate(this);
+ InitializeFrameScheduler();
}
-Engine::~Engine() = default;
-
-void Engine::ScheduleUpdate(uint64_t presentation_time) {
- // TODO(SCN-1092): make |frame_scheduler_| non-nullable. This is feasible now
- // that we can use TestLoopFixture::RunLoopFor() to cause the scheduler to
- // render.
- if (frame_scheduler_) {
- frame_scheduler_->RequestFrame(presentation_time);
- } else {
- // Apply update immediately. This is done for tests.
- FXL_LOG(WARNING)
- << "No FrameScheduler available; applying update immediately";
- RenderFrame(FrameTimingsPtr(), presentation_time, 0, false);
- }
-}
-
-CommandContext InitializeCommandContext(bool has_vulkan,
- escher::EscherWeakPtr escher,
- uint64_t frame_number_for_tracing) {
- return CommandContext(has_vulkan ? escher::BatchGpuUploader::New(
- escher, frame_number_for_tracing)
- : nullptr);
-}
-
-bool Engine::UpdateSessions(uint64_t presentation_time,
- uint64_t presentation_interval,
- uint64_t frame_number_for_tracing) {
- CommandContext command_context =
- InitializeCommandContext(has_vulkan(), escher_, frame_number_for_tracing);
- bool any_updates_were_applied =
- session_manager_->ApplyScheduledSessionUpdates(
- &command_context, presentation_time, presentation_interval);
- command_context.Flush();
-
- return any_updates_were_applied;
+void Engine::InitializeFrameScheduler() {
+ auto weak = weak_factory_.GetWeakPtr();
+ frame_scheduler_->SetDelegate(FrameSchedulerDelegate{
+ /* FrameRenderer */ weak, /* SessionUpdater */ weak});
}
// Helper for RenderFrame(). Generate a mapping between a Compositor's Layer
@@ -141,9 +118,53 @@
};
}
+CommandContext Engine::CreateCommandContext(uint64_t frame_number_for_tracing) {
+ return CommandContext(has_vulkan() ? escher::BatchGpuUploader::New(
+ escher_, frame_number_for_tracing)
+ : nullptr);
+}
+
+// Applies scheduled updates to a session. If the update fails, the session is
+// killed. Returns true if a new render is needed, false otherwise.
+bool Engine::UpdateSessions(std::vector<SessionId> sessions,
+ uint64_t frame_number, uint64_t presentation_time,
+ uint64_t presentation_interval) {
+ auto command_context = CreateCommandContext(frame_number);
+
+ bool needs_render = false;
+ for (auto session_id : sessions) {
+ auto session_handler = session_manager_->FindSessionHandler(session_id);
+ if (!session_handler) {
+ // This means the session that requested the update died after the
+ // request. Requiring the scene to be re-rendered to reflect the session's
+ // disappearance is probably desirable. ImagePipe also relies on this to
+ // be true, since it calls ScheduleUpdate() in it's destructor.
+ needs_render = true;
+ continue;
+ }
+
+ auto session = session_handler->session();
+
+ auto update_results = session->ApplyScheduledUpdates(
+ &command_context, presentation_time, presentation_interval);
+
+ // If update fails, kill the entire client session.
+ if (!update_results.success) {
+ session_manager_->KillSession(session->id());
+ }
+
+ needs_render |= update_results.needs_render;
+ }
+
+ // Flush work to the gpu
+ command_context.Flush();
+
+ return needs_render;
+}
+
bool Engine::RenderFrame(const FrameTimingsPtr& timings,
uint64_t presentation_time,
- uint64_t presentation_interval, bool force_render) {
+ uint64_t presentation_interval) {
// NOTE: this name is important for benchmarking. Do not remove or modify it
// without also updating the "process_scenic_trace.go" script.
TRACE_DURATION("gfx", "RenderFrame", "frame_number", timings->frame_number(),
@@ -153,13 +174,6 @@
// timings->frame_number() below. When this is done, uncomment the following
// line:
// FXL_DCHECK(timings);
-
- // TODO(SCN-1108): consider applying updates as each fence is signalled.
- if (!UpdateSessions(presentation_time, presentation_interval,
- timings ? timings->frame_number() : 0) &&
- !force_render) {
- return false;
- }
UpdateAndDeliverMetrics(presentation_time);
// Some updates were applied; we interpret this to mean that the scene may
@@ -248,7 +262,7 @@
// TODO(MZ-216): Traversing the whole graph just to compute this is pretty
// inefficient. We should optimize this.
- ::fuchsia::ui::gfx::Metrics metrics;
+ fuchsia::ui::gfx::Metrics metrics;
metrics.scale_x = 1.f;
metrics.scale_y = 1.f;
metrics.scale_z = 1.f;
@@ -263,7 +277,7 @@
// have some kind of backpointer from a session to its handler.
for (auto node : updated_nodes) {
if (node->session()) {
- auto event = ::fuchsia::ui::gfx::Event();
+ fuchsia::ui::gfx::Event event;
event.set_metrics(::fuchsia::ui::gfx::MetricsEvent());
event.metrics().node_id = node->id();
event.metrics().metrics = node->reported_metrics();
@@ -273,21 +287,21 @@
}
// TODO(mikejurka): move this to appropriate util file
-bool MetricsEquals(const ::fuchsia::ui::gfx::Metrics& a,
- const ::fuchsia::ui::gfx::Metrics& b) {
+bool MetricsEquals(const fuchsia::ui::gfx::Metrics& a,
+ const fuchsia::ui::gfx::Metrics& b) {
return a.scale_x == b.scale_x && a.scale_y == b.scale_y &&
a.scale_z == b.scale_z;
}
void Engine::UpdateMetrics(Node* node,
- const ::fuchsia::ui::gfx::Metrics& parent_metrics,
+ const fuchsia::ui::gfx::Metrics& parent_metrics,
std::vector<Node*>* updated_nodes) {
- ::fuchsia::ui::gfx::Metrics local_metrics;
+ fuchsia::ui::gfx::Metrics local_metrics;
local_metrics.scale_x = parent_metrics.scale_x * node->scale().x;
local_metrics.scale_y = parent_metrics.scale_y * node->scale().y;
local_metrics.scale_z = parent_metrics.scale_z * node->scale().z;
- if ((node->event_mask() & ::fuchsia::ui::gfx::kMetricsEventMask) &&
+ if ((node->event_mask() & fuchsia::ui::gfx::kMetricsEventMask) &&
!MetricsEquals(node->reported_metrics(), local_metrics)) {
node->set_reported_metrics(local_metrics);
updated_nodes->push_back(node);
diff --git a/garnet/lib/ui/gfx/engine/engine.h b/garnet/lib/ui/gfx/engine/engine.h
index d91a314..1e10fbd 100644
--- a/garnet/lib/ui/gfx/engine/engine.h
+++ b/garnet/lib/ui/gfx/engine/engine.h
@@ -17,24 +17,26 @@
#include "lib/escher/vk/image_factory.h"
#include "garnet/lib/ui/gfx/displays/display_manager.h"
+#include "garnet/lib/ui/gfx/engine/engine_renderer.h"
#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
#include "garnet/lib/ui/gfx/engine/object_linker.h"
#include "garnet/lib/ui/gfx/engine/resource_linker.h"
#include "garnet/lib/ui/gfx/engine/scene_graph.h"
#include "garnet/lib/ui/gfx/engine/session_context.h"
#include "garnet/lib/ui/gfx/engine/session_manager.h"
-#include "garnet/lib/ui/gfx/engine/update_scheduler.h"
#include "garnet/lib/ui/gfx/id.h"
#include "garnet/lib/ui/gfx/resources/import.h"
#include "garnet/lib/ui/gfx/resources/nodes/scene.h"
#include "garnet/lib/ui/gfx/util/event_timestamper.h"
#include "garnet/lib/ui/scenic/event_reporter.h"
+#include "lib/escher/renderer/batch_gpu_uploader.h"
namespace scenic_impl {
namespace gfx {
class Compositor;
-class EngineRenderer;
+class FrameTimings;
+using FrameTimingsPtr = fxl::RefPtr<FrameTimings>;
class Session;
class SessionHandler;
class View;
@@ -42,16 +44,34 @@
using ViewLinker = ObjectLinker<ViewHolder, View>;
+// Graphical context for a set of session updates.
+// The CommandContext is only valid during RenderFrame() and should not be
+// accessed outside of that.
+class CommandContext {
+ public:
+ CommandContext(std::unique_ptr<escher::BatchGpuUploader> uploader);
+
+ escher::BatchGpuUploader* batch_gpu_uploader() const {
+ return batch_gpu_uploader_.get();
+ }
+
+ // Flush any work accumulated during command processing.
+ void Flush();
+
+ private:
+ std::unique_ptr<escher::BatchGpuUploader> batch_gpu_uploader_;
+};
+
// Owns a group of sessions which can share resources with one another
// using the same resource linker and which coexist within the same timing
// domain using the same frame scheduler. It is not possible for sessions
// which belong to different engines to communicate with one another.
-class Engine : public UpdateScheduler, private FrameSchedulerDelegate {
+class Engine : public SessionUpdater, public FrameRenderer {
public:
Engine(std::unique_ptr<FrameScheduler> frame_scheduler,
DisplayManager* display_manager, escher::EscherWeakPtr escher);
- ~Engine() override;
+ ~Engine() override = default;
escher::Escher* escher() const { return escher_.get(); }
escher::EscherWeakPtr GetEscherWeakPtr() const { return escher_; }
@@ -86,28 +106,34 @@
event_timestamper(),
session_manager(),
frame_scheduler(),
- static_cast<UpdateScheduler*>(this),
display_manager_,
scene_graph(),
resource_linker(),
view_linker()};
}
- // |UpdateScheduler|
- //
- // 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 ScheduleUpdate(uint64_t presentation_time) override;
-
- // Dumps the contents of all scene graphs.
- std::string DumpScenes() const;
-
// Invoke Escher::Cleanup(). If more work remains afterward, post a delayed
// task to try again; this is typically because cleanup couldn't finish due
// to unfinished GPU work.
void CleanupEscher();
+ // Dumps the contents of all scene graphs.
+ std::string DumpScenes() const;
+
+ // |SessionUpdater|
+ //
+ // Applies scheduled updates to a session. If the update fails, the session is
+ // killed. Returns true if a new render is needed, false otherwise.
+ bool UpdateSessions(std::vector<SessionId> sessions, uint64_t frame_number,
+ uint64_t presentation_time,
+ uint64_t presentation_interval) override;
+
+ // |FrameRenderer|
+ //
+ // Renders a new frame. Returns true if successful, false otherwise.
+ bool RenderFrame(const FrameTimingsPtr& frame, uint64_t presentation_time,
+ uint64_t presentation_interval) override;
+
protected:
// Only used by subclasses used in testing.
Engine(std::unique_ptr<FrameScheduler> frame_scheduler,
@@ -117,6 +143,11 @@
escher::EscherWeakPtr escher);
private:
+ void InitializeFrameScheduler();
+
+ // Creates a command context.
+ CommandContext CreateCommandContext(uint64_t frame_number_for_tracing);
+
// Used by GpuMemory to import VMOs from clients.
uint32_t imported_memory_type_index() const {
return imported_memory_type_index_;
@@ -138,18 +169,8 @@
return release_fence_signaller_.get();
}
- // |FrameSchedulerDelegate|:
- bool RenderFrame(const FrameTimingsPtr& frame, uint64_t presentation_time,
- uint64_t presentation_interval, bool force_render) override;
-
void InitializeShaderFs();
- // Apply updates to all sessions who have updates and have acquired all
- // fences. Return true if there were any updates applied.
- bool UpdateSessions(uint64_t presentation_time,
- uint64_t presentation_interval,
- uint64_t frame_number_for_tracing);
-
// Update and deliver metrics for all nodes which subscribe to metrics
// events.
void UpdateAndDeliverMetrics(uint64_t presentation_time);
diff --git a/garnet/lib/ui/gfx/engine/frame_scheduler.h b/garnet/lib/ui/gfx/engine/frame_scheduler.h
index 8b8a9a3..25931b2 100644
--- a/garnet/lib/ui/gfx/engine/frame_scheduler.h
+++ b/garnet/lib/ui/gfx/engine/frame_scheduler.h
@@ -5,13 +5,11 @@
#ifndef GARNET_LIB_UI_GFX_ENGINE_FRAME_SCHEDULER_H_
#define GARNET_LIB_UI_GFX_ENGINE_FRAME_SCHEDULER_H_
-#include <queue>
+#include <vector>
-#include <lib/async/dispatcher.h>
+#include "garnet/lib/ui/gfx/id.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 {
@@ -20,16 +18,29 @@
class Display;
class FrameTimings;
using FrameTimingsPtr = fxl::RefPtr<FrameTimings>;
+using PresentationTime = uint64_t;
-// Interface implemented by the engine to perform per-frame processing in
-// response to a frame being scheduled.
-class FrameSchedulerDelegate {
+// Interface for performing session updates.
+class SessionUpdater {
public:
- virtual ~FrameSchedulerDelegate() = default;
+ virtual ~SessionUpdater() = 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.
+ // Applies all updates scheduled before or at |presentation_time|, for each
+ // session in |sessions|. Returns true if any updates were applied, false
+ // otherwise.
+ virtual bool UpdateSessions(std::vector<SessionId> sessions,
+ uint64_t frame_number, uint64_t presentation_time,
+ uint64_t presentation_interval) = 0;
+};
+
+// Interface for rendering frames.
+class FrameRenderer {
+ public:
+ virtual ~FrameRenderer() = default;
+
+ // Called when it's time to 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
@@ -37,18 +48,14 @@
// 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;
+ uint64_t presentation_interval) = 0;
+};
+
+struct FrameSchedulerDelegate {
+ fxl::WeakPtr<FrameRenderer> frame_renderer;
+ fxl::WeakPtr<SessionUpdater> session_updater;
};
// The FrameScheduler is responsible for scheduling frames to be drawn in
@@ -61,73 +68,33 @@
// 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);
+ virtual ~FrameScheduler() = default;
// 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;
+ virtual std::pair<zx_time_t, zx_time_t>
+ ComputeTargetPresentationAndWakeupTimes(
+ zx_time_t requested_presentation_time) const = 0;
- 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);
+ virtual void SetDelegate(FrameSchedulerDelegate delegate) = 0;
- // 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();
+ // If |render_continuously|, we keep scheduling new frames immediately after
+ // each presented frame, regardless of whether they're explicitly requested
+ // using RequestFrame().
+ virtual void SetRenderContinuously(bool render_continuously) = 0;
- // 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();
+ // 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.
+ virtual void ScheduleUpdateForSession(uint64_t presentation_time,
+ scenic_impl::SessionId session) = 0;
- // 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
+ protected:
+ // Called 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);
+ virtual void OnFramePresented(FrameTimings* timings) = 0;
};
} // namespace gfx
diff --git a/garnet/lib/ui/gfx/engine/gfx_command_applier.cc b/garnet/lib/ui/gfx/engine/gfx_command_applier.cc
index edba2bc..fc49e9f 100644
--- a/garnet/lib/ui/gfx/engine/gfx_command_applier.cc
+++ b/garnet/lib/ui/gfx/engine/gfx_command_applier.cc
@@ -1105,7 +1105,7 @@
Session* session, ResourceId id, fuchsia::ui::gfx::ImagePipeArgs args) {
auto image_pipe = fxl::MakeRefCounted<ImagePipe>(
session, id, std::move(args.image_pipe_request),
- session->session_context().update_scheduler);
+ session->session_context().frame_scheduler);
return session->resources()->AddResource(id, image_pipe);
}
diff --git a/garnet/lib/ui/gfx/engine/session.cc b/garnet/lib/ui/gfx/engine/session.cc
index 8423656..313ddab 100644
--- a/garnet/lib/ui/gfx/engine/session.cc
+++ b/garnet/lib/ui/gfx/engine/session.cc
@@ -39,7 +39,7 @@
wrapped_hits.resize(hits.size());
for (size_t i = 0; i < hits.size(); ++i) {
const Hit& hit = hits[i];
- ::fuchsia::ui::gfx::Hit wrapped_hit;
+ fuchsia::ui::gfx::Hit wrapped_hit;
wrapped_hit.tag_value = hit.tag_value;
wrapped_hit.ray_origin = Wrap(hit.ray.origin);
wrapped_hit.ray_direction = Wrap(hit.ray.direction);
@@ -103,8 +103,8 @@
bool Session::ScheduleUpdate(
uint64_t requested_presentation_time,
std::vector<::fuchsia::ui::gfx::Command> commands,
- ::std::vector<zx::event> acquire_fences,
- ::std::vector<zx::event> release_events,
+ std::vector<zx::event> acquire_fences,
+ std::vector<zx::event> release_events,
fuchsia::ui::scenic::Session::PresentCallback callback) {
uint64_t last_scheduled_presentation_time =
last_applied_update_presentation_time_;
@@ -157,9 +157,8 @@
acquire_fence_set->WaitReadyAsync(
[weak = GetWeakPtr(), requested_presentation_time] {
if (weak) {
- weak->session_context_.session_manager->ScheduleUpdateForSession(
- weak->session_context_.update_scheduler,
- requested_presentation_time, std::move(weak));
+ weak->session_context_.frame_scheduler->ScheduleUpdateForSession(
+ requested_presentation_time, weak->id());
}
});
@@ -177,8 +176,8 @@
scheduled_image_pipe_updates_.push(
{presentation_time, std::move(image_pipe)});
- session_context_.session_manager->ScheduleUpdateForSession(
- session_context_.update_scheduler, presentation_time, GetWeakPtr());
+ session_context_.frame_scheduler->ScheduleUpdateForSession(presentation_time,
+ id_);
}
Session::ApplyUpdateResult Session::ApplyScheduledUpdates(
@@ -309,8 +308,8 @@
// consumed by the FrameScheduler.
}
-void Session::HitTest(uint32_t node_id, ::fuchsia::ui::gfx::vec3 ray_origin,
- ::fuchsia::ui::gfx::vec3 ray_direction,
+void Session::HitTest(uint32_t node_id, fuchsia::ui::gfx::vec3 ray_origin,
+ fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestCallback callback) {
if (auto node = resources_.FindResource<Node>(node_id)) {
SessionHitTester hit_tester(node->session());
@@ -330,7 +329,7 @@
}
void Session::HitTestDeviceRay(
- ::fuchsia::ui::gfx::vec3 ray_origin, ::fuchsia::ui::gfx::vec3 ray_direction,
+ fuchsia::ui::gfx::vec3 ray_origin, fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestCallback callback) {
escher::ray4 ray =
escher::ray4{{Unwrap(ray_origin), 1.f}, {Unwrap(ray_direction), 0.f}};
diff --git a/garnet/lib/ui/gfx/engine/session_context.h b/garnet/lib/ui/gfx/engine/session_context.h
index c047334..e32202d2 100644
--- a/garnet/lib/ui/gfx/engine/session_context.h
+++ b/garnet/lib/ui/gfx/engine/session_context.h
@@ -45,7 +45,6 @@
EventTimestamper* event_timestamper;
SessionManager* session_manager;
FrameScheduler* frame_scheduler;
- UpdateScheduler* update_scheduler;
DisplayManager* display_manager;
SceneGraphWeakPtr scene_graph;
ResourceLinker* resource_linker;
diff --git a/garnet/lib/ui/gfx/engine/session_handler.cc b/garnet/lib/ui/gfx/engine/session_handler.cc
index e9a5953..ab38dc0 100644
--- a/garnet/lib/ui/gfx/engine/session_handler.cc
+++ b/garnet/lib/ui/gfx/engine/session_handler.cc
@@ -37,8 +37,8 @@
}
void SessionHandler::Present(
- uint64_t presentation_time, ::std::vector<zx::event> acquire_fences,
- ::std::vector<zx::event> release_fences,
+ uint64_t presentation_time, std::vector<zx::event> acquire_fences,
+ std::vector<zx::event> release_fences,
fuchsia::ui::scenic::Session::PresentCallback callback) {
if (!session_->ScheduleUpdate(
presentation_time, std::move(buffered_commands_),
@@ -51,15 +51,15 @@
}
void SessionHandler::HitTest(
- uint32_t node_id, ::fuchsia::ui::gfx::vec3 ray_origin,
- ::fuchsia::ui::gfx::vec3 ray_direction,
+ uint32_t node_id, fuchsia::ui::gfx::vec3 ray_origin,
+ fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestCallback callback) {
session_->HitTest(node_id, std::move(ray_origin), std::move(ray_direction),
std::move(callback));
}
void SessionHandler::HitTestDeviceRay(
- ::fuchsia::ui::gfx::vec3 ray_origin, ::fuchsia::ui::gfx::vec3 ray_direction,
+ fuchsia::ui::gfx::vec3 ray_origin, fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestDeviceRayCallback callback) {
session_->HitTestDeviceRay(std::move(ray_origin), std::move(ray_direction),
std::move(callback));
diff --git a/garnet/lib/ui/gfx/engine/session_manager.cc b/garnet/lib/ui/gfx/engine/session_manager.cc
index ccb4125..d5b7eca 100644
--- a/garnet/lib/ui/gfx/engine/session_manager.cc
+++ b/garnet/lib/ui/gfx/engine/session_manager.cc
@@ -8,27 +8,12 @@
#include <lib/async/default.h>
#include <trace/event.h>
-#include "garnet/lib/ui/gfx/engine/session.h"
#include "garnet/lib/ui/gfx/engine/session_handler.h"
-#include "garnet/lib/ui/gfx/engine/update_scheduler.h"
#include "garnet/lib/ui/scenic/session.h"
namespace scenic_impl {
namespace gfx {
-CommandContext::CommandContext(
- std::unique_ptr<escher::BatchGpuUploader> uploader)
- : batch_gpu_uploader_(std::move(uploader)) {}
-
-void CommandContext::Flush() {
- if (batch_gpu_uploader_) {
- // Submit regardless of whether or not there are updates to release the
- // underlying CommandBuffer so the pool and sequencer don't stall out.
- // TODO(ES-115) to remove this restriction.
- batch_gpu_uploader_->Submit();
- }
-}
-
SessionHandler* SessionManager::FindSessionHandler(SessionId id) {
auto it = session_handlers_.find(id);
if (it != session_handlers_.end()) {
@@ -61,52 +46,6 @@
++session_count_;
}
-void SessionManager::ScheduleUpdateForSession(
- UpdateScheduler* update_scheduler, uint64_t presentation_time,
- fxl::WeakPtr<scenic_impl::gfx::Session> session) {
- FXL_DCHECK(update_scheduler);
- if (session) {
- updatable_sessions_.push({presentation_time, std::move(session)});
- update_scheduler->ScheduleUpdate(presentation_time);
- }
-}
-
-bool SessionManager::ApplyScheduledSessionUpdates(
- CommandContext* command_context, uint64_t presentation_time,
- uint64_t presentation_interval) {
- // NOTE: this name is important for benchmarking. Do not remove or modify it
- // without also updating the "process_scenic_trace.go" script.
- TRACE_DURATION("gfx", "ApplyScheduledSessionUpdates", "time",
- presentation_time, "interval", presentation_interval);
-
- bool needs_render = false;
- while (!updatable_sessions_.empty()) {
- auto& top = updatable_sessions_.top();
- if (top.first > presentation_time)
- break;
- auto session = std::move(top.second);
- updatable_sessions_.pop();
- if (session.get() != nullptr) {
- auto update_results = session->ApplyScheduledUpdates(
- command_context, presentation_time, presentation_interval);
-
- needs_render |= update_results.needs_render;
-
- // If update fails, kill the entire client session
- if (!update_results.success) {
- auto session_handler = FindSessionHandler(session->id());
- FXL_DCHECK(session_handler);
- session_handler->BeginTearDown();
- }
- } else {
- // Corresponds to a call to ScheduleUpdate(), which always triggers a
- // render.
- needs_render = true;
- }
- }
- return needs_render;
-}
-
void SessionManager::RemoveSessionHandler(SessionId id) {
auto it = session_handlers_.find(id);
if (it != session_handlers_.end()) {
@@ -116,5 +55,11 @@
}
}
+void SessionManager::KillSession(SessionId session_id) {
+ auto session_handler = FindSessionHandler(session_id);
+ FXL_DCHECK(session_handler);
+ session_handler->BeginTearDown();
+}
+
} // namespace gfx
} // namespace scenic_impl
diff --git a/garnet/lib/ui/gfx/engine/session_manager.h b/garnet/lib/ui/gfx/engine/session_manager.h
index 9fa5de6..54fd8e8 100644
--- a/garnet/lib/ui/gfx/engine/session_manager.h
+++ b/garnet/lib/ui/gfx/engine/session_manager.h
@@ -5,12 +5,9 @@
#ifndef GARNET_LIB_UI_GFX_ENGINE_SESSION_MANAGER_H_
#define GARNET_LIB_UI_GFX_ENGINE_SESSION_MANAGER_H_
-#include <set>
#include <unordered_map>
-#include "garnet/lib/ui/gfx/engine/session.h"
#include "garnet/lib/ui/scenic/command_dispatcher.h"
-#include "lib/escher/renderer/batch_gpu_uploader.h"
namespace scenic_impl {
class EventReporter;
@@ -21,29 +18,9 @@
namespace gfx {
using SessionId = ::scenic_impl::SessionId;
-using PresentationTime = uint64_t;
class SessionHandler;
class Engine;
-class UpdateScheduler;
-
-// Graphical context for a set of session updates.
-// The CommandContext is only valid during RenderFrame() and should not be
-// accessed outside of that.
-class CommandContext {
- public:
- CommandContext(std::unique_ptr<escher::BatchGpuUploader> uploader);
-
- escher::BatchGpuUploader* batch_gpu_uploader() const {
- return batch_gpu_uploader_.get();
- }
-
- // Flush any work accumulated during command processing.
- void Flush();
-
- private:
- std::unique_ptr<escher::BatchGpuUploader> batch_gpu_uploader_;
-};
// Manages a collection of SessionHandlers.
// Tracks future updates requested by Sessions, and executes updates for a
@@ -64,56 +41,28 @@
std::unique_ptr<CommandDispatcher> CreateCommandDispatcher(
CommandDispatcherContext context, Engine* engine);
- // Tell the UpdateScheduler to schedule a frame, and remember the Session so
- // that we can tell it to apply updates in ApplyScheduledSessionUpdates().
- void ScheduleUpdateForSession(
- UpdateScheduler* update_scheduler, uint64_t presentation_time,
- fxl::WeakPtr<scenic_impl::gfx::Session> session);
-
- // Executes updates that are schedule up to and including a given presentation
- // time. Returns true if rendering is needed.
- bool ApplyScheduledSessionUpdates(CommandContext* command_context,
- uint64_t presentation_time,
- uint64_t presentation_interval);
+ // Called by engine on a failed session update
+ void KillSession(SessionId session_id);
protected:
// Protected for testing subclass
void InsertSessionHandler(SessionId session_id,
SessionHandler* session_handler);
+ virtual std::unique_ptr<SessionHandler> CreateSessionHandler(
+ CommandDispatcherContext context, Engine* engine,
+ EventReporter* event_reporter, ErrorReporter* error_reporter) const;
+
private:
friend class SessionHandler;
- // Used to compare presentation times so that the priority_queue acts as a min
- // heap, placing the earliest PresentationTime at the top
- class UpdatableSessionsComparator {
- public:
- bool operator()(
- std::pair<PresentationTime, fxl::WeakPtr<Session>> updatable_session1,
- std::pair<PresentationTime, fxl::WeakPtr<Session>> updatable_session2) {
- return updatable_session1.first > updatable_session2.first;
- }
- };
-
// Removes the SessionHandler from the session_handlers_ map. We assume that
// the SessionHandler has already taken care of itself and its Session.
void RemoveSessionHandler(SessionId id);
- virtual std::unique_ptr<SessionHandler> CreateSessionHandler(
- CommandDispatcherContext context, Engine* engine,
- EventReporter* event_reporter, ErrorReporter* error_reporter) const;
-
// Map of all the sessions.
std::unordered_map<SessionId, SessionHandler*> session_handlers_;
size_t session_count_ = 0;
-
- // Lists all Session that have updates to apply, sorted by the earliest
- // requested presentation time of each update.
- std::priority_queue<
- std::pair<PresentationTime, fxl::WeakPtr<Session>>,
- std::vector<std::pair<PresentationTime, fxl::WeakPtr<Session>>>,
- UpdatableSessionsComparator>
- updatable_sessions_;
};
} // namespace gfx
diff --git a/garnet/lib/ui/gfx/engine/update_scheduler.h b/garnet/lib/ui/gfx/engine/update_scheduler.h
deleted file mode 100644
index 6380ccd..0000000
--- a/garnet/lib/ui/gfx/engine/update_scheduler.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 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_UPDATE_SCHEDULER_H_
-#define GARNET_LIB_UI_GFX_ENGINE_UPDATE_SCHEDULER_H_
-
-namespace scenic_impl {
-namespace gfx {
-
-class UpdateScheduler {
- public:
- virtual void ScheduleUpdate(uint64_t presentation_time) = 0;
- virtual ~UpdateScheduler() = default;
-};
-
-} // namespace gfx
-} // namespace scenic_impl
-
-#endif // GARNET_LIB_UI_GFX_ENGINE_UPDATE_SCHEDULER_H_
diff --git a/garnet/lib/ui/gfx/gfx_system.cc b/garnet/lib/ui/gfx/gfx_system.cc
index ecea93a..3f80f20 100644
--- a/garnet/lib/ui/gfx/gfx_system.cc
+++ b/garnet/lib/ui/gfx/gfx_system.cc
@@ -6,6 +6,7 @@
#include <fs/pseudo-file.h>
+#include "garnet/lib/ui/gfx/engine/default_frame_scheduler.h"
#include "garnet/lib/ui/gfx/engine/session_handler.h"
#include "garnet/lib/ui/gfx/screenshotter.h"
#include "garnet/lib/ui/gfx/util/vulkan_utils.h"
@@ -54,9 +55,10 @@
}
std::unique_ptr<Engine> GfxSystem::InitializeEngine() {
- return std::make_unique<Engine>(
- std::make_unique<FrameScheduler>(display_manager_->default_display()),
- display_manager_.get(), escher_->GetWeakPtr());
+ return std::make_unique<Engine>(std::make_unique<DefaultFrameScheduler>(
+ display_manager_->default_display()),
+ display_manager_.get(),
+ escher_->GetWeakPtr());
}
std::unique_ptr<escher::Escher> GfxSystem::InitializeEscher() {
diff --git a/garnet/lib/ui/gfx/resources/image_pipe.cc b/garnet/lib/ui/gfx/resources/image_pipe.cc
index 50a8161..7883cd9 100644
--- a/garnet/lib/ui/gfx/resources/image_pipe.cc
+++ b/garnet/lib/ui/gfx/resources/image_pipe.cc
@@ -6,8 +6,8 @@
#include <trace/event.h>
+#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
#include "garnet/lib/ui/gfx/engine/session.h"
-#include "garnet/lib/ui/gfx/engine/update_scheduler.h"
#include "garnet/lib/ui/gfx/resources/memory.h"
#include "lib/escher/flib/fence.h"
@@ -18,22 +18,22 @@
ResourceType::kImagePipe | ResourceType::kImageBase, "ImagePipe"};
ImagePipe::ImagePipe(Session* session, ResourceId id,
- UpdateScheduler* update_scheduler)
+ FrameScheduler* frame_scheduler)
: ImageBase(session, id, ImagePipe::kTypeInfo),
- update_scheduler_(update_scheduler),
+ frame_scheduler_(frame_scheduler),
weak_ptr_factory_(this) {
- FXL_DCHECK(update_scheduler);
+ FXL_DCHECK(frame_scheduler);
}
ImagePipe::ImagePipe(
Session* session, ResourceId id,
::fidl::InterfaceRequest<fuchsia::images::ImagePipe> request,
- UpdateScheduler* update_scheduler)
+ FrameScheduler* frame_scheduler)
: ImageBase(session, id, ImagePipe::kTypeInfo),
handler_(std::make_unique<ImagePipeHandler>(std::move(request), this)),
- update_scheduler_(update_scheduler),
+ frame_scheduler_(frame_scheduler),
weak_ptr_factory_(this) {
- FXL_DCHECK(update_scheduler);
+ FXL_DCHECK(frame_scheduler);
}
void ImagePipe::AddImage(uint32_t image_id,
@@ -87,7 +87,7 @@
images_.clear();
// Schedule a new frame.
- update_scheduler_->ScheduleUpdate(0);
+ frame_scheduler_->ScheduleUpdateForSession(0, session()->id());
}
void ImagePipe::OnConnectionError() { CloseConnectionAndCleanUp(); }
diff --git a/garnet/lib/ui/gfx/resources/image_pipe.h b/garnet/lib/ui/gfx/resources/image_pipe.h
index 43603bd..75a2825 100644
--- a/garnet/lib/ui/gfx/resources/image_pipe.h
+++ b/garnet/lib/ui/gfx/resources/image_pipe.h
@@ -32,10 +32,10 @@
public:
static const ResourceTypeInfo kTypeInfo;
- ImagePipe(Session* session, ResourceId id, UpdateScheduler* update_scheduler);
+ ImagePipe(Session* session, ResourceId id, FrameScheduler* frame_scheduler);
ImagePipe(Session* session, ResourceId id,
::fidl::InterfaceRequest<fuchsia::images::ImagePipe> request,
- UpdateScheduler* update_scheduler);
+ FrameScheduler* frame_scheduler);
// Called by |ImagePipeHandler|, part of |ImagePipe| interface.
void AddImage(uint32_t image_id, fuchsia::images::ImageInfo image_info,
@@ -112,7 +112,7 @@
std::unordered_map<ResourceId, ImagePtr> images_;
bool is_valid_ = true;
- UpdateScheduler* update_scheduler_;
+ FrameScheduler* frame_scheduler_;
fxl::WeakPtrFactory<ImagePipe> weak_ptr_factory_; // must be last
diff --git a/garnet/lib/ui/gfx/tests/gfx_apptest.cc b/garnet/lib/ui/gfx/tests/gfx_apptest.cc
index b46e6762..9f7fed3 100644
--- a/garnet/lib/ui/gfx/tests/gfx_apptest.cc
+++ b/garnet/lib/ui/gfx/tests/gfx_apptest.cc
@@ -85,14 +85,14 @@
// Call Present with release fences.
session->Present(0u, std::vector<zx::event>(), std::move(release_fences),
[](fuchsia::images::PresentationInfo info) {});
- RunLoopUntilIdle();
+ RunLoopFor(zx::sec(1));
EXPECT_EQ(1u, handler->present_count());
EXPECT_FALSE(IsFenceSignalled(release_fence1));
EXPECT_FALSE(IsFenceSignalled(release_fence2));
// Call Present again with no release fences.
session->Present(0u, std::vector<zx::event>(), std::vector<zx::event>(),
[](fuchsia::images::PresentationInfo info) {});
- RunLoopUntilIdle();
+ RunLoopFor(zx::sec(1));
EXPECT_EQ(2u, handler->present_count());
EXPECT_TRUE(IsFenceSignalled(release_fence2));
}
@@ -129,20 +129,20 @@
// Call Present with both the acquire and release fences.
session->Present(0u, std::move(acquire_fences), std::move(release_fences),
[](fuchsia::images::PresentationInfo info) {});
- RunLoopUntilIdle();
+ RunLoopFor(zx::sec(1));
EXPECT_EQ(1u, handler->present_count());
EXPECT_FALSE(IsFenceSignalled(release_fence));
// Call Present again with no fences.
session->Present(0u, std::vector<zx::event>(), std::vector<zx::event>(),
[](fuchsia::images::PresentationInfo info) {});
- RunLoopUntilIdle();
+ RunLoopFor(zx::sec(1));
EXPECT_EQ(2u, handler->present_count());
EXPECT_FALSE(IsFenceSignalled(release_fence));
// Now signal the acquire fence.
acquire_fence.signal(0u, escher::kFenceSignalled);
// Now expect that the first frame was presented, and its release fence was
// signalled.
- RunLoopUntilIdle();
+ RunLoopFor(zx::sec(1));
EXPECT_TRUE(IsFenceSignalled(release_fence));
}
diff --git a/garnet/lib/ui/gfx/tests/image_pipe_unittest.cc b/garnet/lib/ui/gfx/tests/image_pipe_unittest.cc
index c9aa858..17f54f6 100644
--- a/garnet/lib/ui/gfx/tests/image_pipe_unittest.cc
+++ b/garnet/lib/ui/gfx/tests/image_pipe_unittest.cc
@@ -4,7 +4,7 @@
#include "garnet/lib/ui/gfx/resources/image_pipe.h"
#include "garnet/lib/ui/gfx/tests/mocks.h"
-#include "garnet/lib/ui/gfx/tests/session_test.h"
+#include "garnet/lib/ui/gfx/tests/session_handler_test.h"
#include "garnet/lib/ui/gfx/tests/util.h"
#include "gtest/gtest.h"
#include "lib/escher/flib/fence.h"
@@ -27,7 +27,7 @@
uint32_t update_count_ = 0;
protected:
- bool UpdatePixels(escher::BatchGpuUploader* uploader) override {
+ bool UpdatePixels(escher::BatchGpuUploader* gpu_uploader) override {
++update_count_;
// Update pixels returns the new dirty state. False will stop additional
// calls to UpdatePixels() until the image is marked dirty.
@@ -35,31 +35,12 @@
}
}; // namespace test
-class ImagePipeTest : public SessionTest, public escher::ResourceManager {
+class ImagePipeTest : public SessionHandlerTest,
+ public escher::ResourceManager {
public:
ImagePipeTest() : escher::ResourceManager(escher::EscherWeakPtr()) {}
- std::unique_ptr<SessionForTest> CreateSession() override {
- SessionContext session_context = CreateBarebonesSessionContext();
-
- command_buffer_sequencer_ =
- std::make_unique<escher::impl::CommandBufferSequencer>();
-
- mock_release_fence_signaller_ =
- std::make_unique<ReleaseFenceSignallerForTest>(
- command_buffer_sequencer_.get());
- session_context.release_fence_signaller =
- mock_release_fence_signaller_.get();
-
- return std::make_unique<SessionForTest>(1, std::move(session_context), this,
- error_reporter());
- }
-
void OnReceiveOwnable(std::unique_ptr<escher::Resource> resource) override {}
-
- std::unique_ptr<escher::impl::CommandBufferSequencer>
- command_buffer_sequencer_;
- std::unique_ptr<ReleaseFenceSignallerForTest> mock_release_fence_signaller_;
};
fxl::RefPtr<fsl::SharedVmo> CreateVmoWithBuffer(
@@ -96,11 +77,10 @@
class ImagePipeThatCreatesDummyImages : public ImagePipe {
public:
ImagePipeThatCreatesDummyImages(
- Session* session, escher::ResourceManager* dummy_resource_manager,
- UpdateScheduler* update_scheduler)
- : ImagePipe(session, 0u, update_scheduler),
+ Session* session, escher::ResourceManager* dummy_resource_manager)
+ : ImagePipe(session, 0u, session->session_context().frame_scheduler),
dummy_resource_manager_(dummy_resource_manager) {
- FXL_CHECK(update_scheduler);
+ FXL_CHECK(session->session_context().frame_scheduler);
}
std::vector<fxl::RefPtr<DummyImage>> dummy_images_;
@@ -130,8 +110,7 @@
TEST_F(ImagePipeTest, ImagePipeImageIdMustNotBeZero) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
-
+ session_handler_->session(), this);
uint32_t image1_id = 0;
// Create a checkerboard image and copy it into a vmo.
{
@@ -154,7 +133,7 @@
TEST_F(ImagePipeTest, PresentImagesOutOfOrder) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
+ session_handler_->session(), this);
uint32_t image1_id = 1;
// Create a checkerboard image and copy it into a vmo.
@@ -187,7 +166,7 @@
TEST_F(ImagePipeTest, PresentImagesInOrder) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
+ session_handler_->session(), this);
uint32_t image1_id = 1;
// Create a checkerboard image and copy it into a vmo.
@@ -219,7 +198,7 @@
TEST_F(ImagePipeTest, PresentImagesWithOffset) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
+ session_handler_->session(), this);
uint32_t image1_id = 1;
// Create a checkerboard image and copy it into a vmo.
@@ -259,7 +238,7 @@
TEST_F(ImagePipeTest, ImagePipePresentTwoFrames) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
+ session_handler_->session(), this);
uint32_t image1_id = 1;
@@ -285,18 +264,18 @@
// Current presented image should be null, since we haven't signalled
// acquire fence yet.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopFor(zx::sec(1)));
ASSERT_FALSE(image_pipe->GetEscherImage());
// Signal on the acquire fence.
acquire_fence1.signal(0u, escher::kFenceSignalled);
// Run until image1 is presented.
- RunLoopUntilIdle();
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
ASSERT_TRUE(image_pipe->GetEscherImage());
- escher::ImagePtr image1 = image_pipe->GetEscherImage();
// Image should now be presented.
+ escher::ImagePtr image1 = image_pipe->GetEscherImage();
ASSERT_TRUE(image1);
uint32_t image2_id = 2;
@@ -313,7 +292,7 @@
}
// The first image should not have been released.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopFor(zx::sec(1)));
ASSERT_FALSE(IsEventSignalled(release_fence1, escher::kFenceSignalled));
// Make gradient the currently displayed image.
@@ -325,21 +304,19 @@
// Verify that the currently display image hasn't changed yet, since we
// haven't signalled the acquire fence.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopUntilIdle());
ASSERT_EQ(image_pipe->GetEscherImage(), image1);
// Signal on the acquire fence.
acquire_fence2.signal(0u, escher::kFenceSignalled);
// There should be a new image presented.
- RunLoopUntilIdle();
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
escher::ImagePtr image2 = image_pipe->GetEscherImage();
ASSERT_TRUE(image2);
ASSERT_NE(image1, image2);
// The first image should have been released.
- ASSERT_EQ(mock_release_fence_signaller_->num_calls_to_add_cpu_release_fence(),
- 1u);
ASSERT_TRUE(IsEventSignalled(release_fence1, escher::kFenceSignalled));
ASSERT_FALSE(IsEventSignalled(release_fence2, escher::kFenceSignalled));
}
@@ -348,10 +325,7 @@
// called on images that are acquired and used.
TEST_F(ImagePipeTest, ImagePipeUpdateTwoFrames) {
auto image_pipe = fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
- zx::time now = Now();
- zx::time now_ish = now + zx::sec(2);
- zx::time later = now + zx::sec(5);
+ session_handler_->session(), this);
// Image A is a 2x2 image with id=2.
// Image B is a 4x4 image with id=4.
@@ -368,12 +342,13 @@
imageIdB, std::move(image_info_b), CopyVmo(gradient_b->vmo()), 0,
GetVmoSize(gradient_b->vmo()), fuchsia::images::MemoryType::HOST_MEMORY);
- image_pipe->PresentImage(imageIdA, now_ish.get(), std::vector<zx::event>(),
+ image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(),
std::vector<zx::event>(), nullptr);
- image_pipe->PresentImage(imageIdB, now_ish.get(), std::vector<zx::event>(),
+ image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(),
std::vector<zx::event>(), nullptr);
- RunLoopUntil(later);
+ // Let all updates get scheduled and finished
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
auto image_out = image_pipe->GetEscherImage();
// We should get the second image in the queue, since both should have been
@@ -384,20 +359,18 @@
ASSERT_EQ(image_pipe->dummy_images_[0]->update_count_, 0u);
ASSERT_EQ(image_pipe->dummy_images_[1]->update_count_, 1u);
- zx::time even_later = later + zx::sec(1);
- zx::time much_later = even_later + zx::sec(2);
// Do it again, to make sure that update is called a second time (since
// released images could be edited by the client before presentation).
//
// In this case, we need to run to idle after presenting image A, so that
// image B is returned by the pool, marked dirty, and is free to be acquired
// again.
- image_pipe->PresentImage(imageIdA, even_later.get(), std::vector<zx::event>(),
+ image_pipe->PresentImage(imageIdA, 0, std::vector<zx::event>(),
std::vector<zx::event>(), nullptr);
- RunLoopUntil(even_later);
- image_pipe->PresentImage(imageIdB, even_later.get(), std::vector<zx::event>(),
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
+ image_pipe->PresentImage(imageIdB, 0, std::vector<zx::event>(),
std::vector<zx::event>(), nullptr);
- RunLoopUntil(much_later);
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
image_out = image_pipe->GetEscherImage();
ASSERT_EQ(image_pipe->dummy_images_.size(), 2u);
@@ -413,7 +386,7 @@
TEST_F(ImagePipeTest, ImagePipeRemoveImageThatIsPendingPresent) {
ImagePipePtr image_pipe =
fxl::MakeRefCounted<ImagePipeThatCreatesDummyImages>(
- session_.get(), this, update_scheduler_.get());
+ session_handler_->session(), this);
uint32_t image1_id = 1;
@@ -439,7 +412,7 @@
// Current presented image should be null, since we haven't signalled
// acquire fence yet.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopFor(zx::sec(1)));
ASSERT_FALSE(image_pipe->GetEscherImage());
// Remove the image; by the ImagePipe semantics, the consumer will
@@ -450,7 +423,7 @@
acquire_fence1.signal(0u, escher::kFenceSignalled);
// Run until image1 is presented.
- RunLoopUntilIdle();
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
ASSERT_TRUE(image_pipe->GetEscherImage());
escher::ImagePtr image1 = image_pipe->GetEscherImage();
@@ -471,7 +444,7 @@
}
// The first image should not have been released.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopFor(zx::sec(1)));
ASSERT_FALSE(IsEventSignalled(release_fence1, escher::kFenceSignalled));
// Make gradient the currently displayed image.
@@ -483,21 +456,19 @@
// Verify that the currently display image hasn't changed yet, since we
// haven't signalled the acquire fence.
- RunLoopUntilIdle();
+ ASSERT_FALSE(RunLoopFor(zx::sec(1)));
ASSERT_EQ(image_pipe->GetEscherImage(), image1);
// Signal on the acquire fence.
acquire_fence2.signal(0u, escher::kFenceSignalled);
// There should be a new image presented.
- RunLoopUntilIdle();
+ ASSERT_TRUE(RunLoopFor(zx::sec(1)));
escher::ImagePtr image2 = image_pipe->GetEscherImage();
ASSERT_TRUE(image2);
ASSERT_NE(image1, image2);
// The first image should have been released.
- ASSERT_EQ(mock_release_fence_signaller_->num_calls_to_add_cpu_release_fence(),
- 1u);
ASSERT_TRUE(IsEventSignalled(release_fence1, escher::kFenceSignalled));
ASSERT_FALSE(IsEventSignalled(release_fence2, escher::kFenceSignalled));
EXPECT_ERROR_COUNT(0);
diff --git a/garnet/lib/ui/gfx/tests/mocks.cc b/garnet/lib/ui/gfx/tests/mocks.cc
index ba2dcd2..4e8b6dc 100644
--- a/garnet/lib/ui/gfx/tests/mocks.cc
+++ b/garnet/lib/ui/gfx/tests/mocks.cc
@@ -4,6 +4,7 @@
#include "garnet/lib/ui/gfx/tests/mocks.h"
+#include "garnet/lib/ui/gfx/engine/default_frame_scheduler.h"
#include "garnet/lib/ui/gfx/tests/session_test.h"
#include "garnet/lib/ui/scenic/command_dispatcher.h"
@@ -16,14 +17,24 @@
ErrorReporter* error_reporter)
: Session(id, std::move(context), event_reporter, error_reporter) {}
-SessionHandlerForTest::SessionHandlerForTest(CommandDispatcherContext context,
- SessionManager* session_manager,
+SessionHandlerForTest::SessionHandlerForTest(SessionManager* session_manager,
SessionContext session_context,
+ SessionId session_id,
+ Scenic* scenic,
EventReporter* event_reporter,
ErrorReporter* error_reporter)
- : SessionHandler(std::move(context), session_manager,
- std::move(session_context), event_reporter,
- error_reporter),
+ : SessionHandler(
+ CommandDispatcherContext(scenic, /* session = */ nullptr, session_id),
+ session_manager, std::move(session_context), event_reporter, error_reporter),
+ command_count_(0),
+ present_count_(0) {}
+
+SessionHandlerForTest::SessionHandlerForTest(
+ CommandDispatcherContext command_dispatcher_context,
+ SessionManager* session_manager, SessionContext session_context,
+ EventReporter* event_reporter, ErrorReporter* error_reporter)
+ : SessionHandler(std::move(command_dispatcher_context), session_manager,
+ std::move(session_context), event_reporter, error_reporter),
command_count_(0),
present_count_(0) {}
@@ -52,8 +63,6 @@
fence.signal(0u, escher::kFenceSignalled);
}
-SessionManagerForTest::SessionManagerForTest() : SessionManager() {}
-
void SessionManagerForTest::InsertSessionHandler(
SessionId session_id, SessionHandler* session_handler) {
SessionManager::InsertSessionHandler(session_id, session_handler);
@@ -65,12 +74,14 @@
return std::make_unique<SessionHandlerForTest>(
std::move(context), engine->session_manager(), engine->session_context(),
event_reporter, error_reporter);
-};
+}
EngineForTest::EngineForTest(DisplayManager* display_manager,
std::unique_ptr<escher::ReleaseFenceSignaller> r,
escher::EscherWeakPtr escher)
- : Engine(std::unique_ptr<FrameScheduler>(), display_manager, std::move(r),
+ : Engine(std::make_unique<DefaultFrameScheduler>(
+ display_manager->default_display()),
+ display_manager, std::move(r),
std::make_unique<SessionManagerForTest>(), std::move(escher)) {}
} // namespace test
diff --git a/garnet/lib/ui/gfx/tests/mocks.h b/garnet/lib/ui/gfx/tests/mocks.h
index 3bcf9ec..b8fc06a 100644
--- a/garnet/lib/ui/gfx/tests/mocks.h
+++ b/garnet/lib/ui/gfx/tests/mocks.h
@@ -26,11 +26,16 @@
class SessionHandlerForTest : public SessionHandler {
public:
SessionHandlerForTest(
- CommandDispatcherContext context, SessionManager* session_manager,
- SessionContext session_context,
+ SessionManager* session_manager, SessionContext session_context,
+ SessionId session_id, Scenic* scenic,
EventReporter* event_reporter = EventReporter::Default(),
ErrorReporter* error_reporter = ErrorReporter::Default());
+ SessionHandlerForTest(
+ CommandDispatcherContext command_dispatcher_context,
+ SessionManager* session_manager, SessionContext session_context,
+ EventReporter* event_reporter, ErrorReporter* error_reporter);
+
// |scenic::CommandDispatcher|
void DispatchCommand(fuchsia::ui::scenic::Command command) override;
@@ -68,12 +73,14 @@
class SessionManagerForTest : public SessionManager {
public:
- SessionManagerForTest();
+ SessionManagerForTest() = default;
+ ~SessionManagerForTest() override = default;
// Publicly accessible for tests.
void InsertSessionHandler(SessionId session_id,
SessionHandler* session_handler);
+ protected:
std::unique_ptr<SessionHandler> CreateSessionHandler(
CommandDispatcherContext context, Engine* engine,
EventReporter* event_reporter,
diff --git a/garnet/lib/ui/gfx/tests/session_handler_test.cc b/garnet/lib/ui/gfx/tests/session_handler_test.cc
index e68f7ed..6b991c6 100644
--- a/garnet/lib/ui/gfx/tests/session_handler_test.cc
+++ b/garnet/lib/ui/gfx/tests/session_handler_test.cc
@@ -8,13 +8,22 @@
namespace gfx {
namespace test {
-void SessionHandlerTest::SetUp() { SessionTest::SetUp(); }
+void SessionHandlerTest::SetUp() {
+ InitializeScenic();
+ InitializeDisplayManager();
+ InitializeEngine();
+
+ InitializeSessionHandler();
+}
void SessionHandlerTest::TearDown() {
- SessionTest::TearDown();
session_handler_.reset();
+ engine_.reset();
+ command_buffer_sequencer_.reset();
+ display_manager_.reset();
scenic_.reset();
app_context_.reset();
+ events_.clear();
}
void SessionHandlerTest::InitializeScenic() {
@@ -25,16 +34,51 @@
}
void SessionHandlerTest::InitializeSessionHandler() {
- if (!scenic_) {
- InitializeScenic();
- }
+ auto session_context = engine_->session_context();
+ auto session_manager = session_context.session_manager;
+ auto session_id = SessionId(1);
- auto session_context = CreateBarebonesSessionContext();
session_handler_ = std::make_unique<SessionHandlerForTest>(
- CommandDispatcherContext(scenic_.get(), nullptr, session_->id()),
- session_manager_.get(), std::move(session_context));
- session_manager_->InsertSessionHandler(session_->id(),
- session_handler_.get());
+ session_manager, std::move(session_context), session_id, scenic_.get(),
+ this, error_reporter());
+ static_cast<SessionManagerForTest*>(session_manager)
+ ->InsertSessionHandler(session_id, session_handler_.get());
+}
+
+void SessionHandlerTest::InitializeDisplayManager() {
+ display_manager_ = std::make_unique<DisplayManager>();
+ display_manager_->SetDefaultDisplayForTests(std::make_unique<Display>(
+ /*id*/ 0, /*px-width*/ 0, /*px-height*/ 0));
+}
+
+void SessionHandlerTest::InitializeEngine() {
+ command_buffer_sequencer_ =
+ std::make_unique<escher::impl::CommandBufferSequencer>();
+
+ auto mock_release_fence_signaller =
+ std::make_unique<ReleaseFenceSignallerForTest>(
+ command_buffer_sequencer_.get());
+
+ engine_ = std::make_unique<EngineForTest>(
+ display_manager_.get(), std::move(mock_release_fence_signaller));
+}
+
+void SessionHandlerTest::EnqueueEvent(fuchsia::ui::gfx::Event event) {
+ fuchsia::ui::scenic::Event scenic_event;
+ scenic_event.set_gfx(std::move(event));
+ events_.push_back(std::move(scenic_event));
+}
+
+void SessionHandlerTest::EnqueueEvent(fuchsia::ui::input::InputEvent event) {
+ fuchsia::ui::scenic::Event scenic_event;
+ scenic_event.set_input(std::move(event));
+ events_.push_back(std::move(scenic_event));
+}
+
+void SessionHandlerTest::EnqueueEvent(fuchsia::ui::scenic::Command unhandled) {
+ fuchsia::ui::scenic::Event scenic_event;
+ scenic_event.set_unhandled(std::move(unhandled));
+ events_.push_back(std::move(scenic_event));
}
} // namespace test
diff --git a/garnet/lib/ui/gfx/tests/session_handler_test.h b/garnet/lib/ui/gfx/tests/session_handler_test.h
index be07096..80a7048 100644
--- a/garnet/lib/ui/gfx/tests/session_handler_test.h
+++ b/garnet/lib/ui/gfx/tests/session_handler_test.h
@@ -5,7 +5,7 @@
#ifndef GARNET_LIB_UI_GFX_TESTS_SESSION_HANDLER_TEST_H_
#define GARNET_LIB_UI_GFX_TESTS_SESSION_HANDLER_TEST_H_
-#include "garnet/lib/ui/gfx/tests/session_test.h"
+#include "garnet/lib/ui/gfx/tests/error_reporting_test.h"
#include <lib/fit/function.h>
@@ -23,19 +23,31 @@
// For testing SessionHandler without having to manually provide all the state
// necessary for SessionHandler to run
-class SessionHandlerTest : public SessionTest {
- public:
- void ResetSessionHandler() { session_handler_.reset(); }
- void InitializeScenic();
- void InitializeSessionHandler();
-
+class SessionHandlerTest : public ErrorReportingTest, public EventReporter {
+ protected:
+ // | ::testing::Test |
void SetUp() override;
void TearDown() override;
- protected:
+ // |EventReporter|
+ void EnqueueEvent(fuchsia::ui::gfx::Event event) override;
+ void EnqueueEvent(fuchsia::ui::input::InputEvent event) override;
+ void EnqueueEvent(fuchsia::ui::scenic::Command unhandled) override;
+
+ void InitializeScenic();
+ void InitializeDisplayManager();
+ void InitializeEngine();
+ void InitializeSessionHandler();
+
std::unique_ptr<component::StartupContext> app_context_;
std::unique_ptr<Scenic> scenic_;
+ std::unique_ptr<escher::impl::CommandBufferSequencer>
+ command_buffer_sequencer_;
+ std::unique_ptr<EngineForTest> engine_;
std::unique_ptr<SessionHandlerForTest> session_handler_;
+ std::unique_ptr<DisplayManager> display_manager_;
+
+ std::vector<fuchsia::ui::scenic::Event> events_;
};
} // namespace test
diff --git a/garnet/lib/ui/gfx/tests/session_handler_unittest.cc b/garnet/lib/ui/gfx/tests/session_handler_unittest.cc
index 3ba37e7..71a08c5 100644
--- a/garnet/lib/ui/gfx/tests/session_handler_unittest.cc
+++ b/garnet/lib/ui/gfx/tests/session_handler_unittest.cc
@@ -14,15 +14,18 @@
SessionHandlerTest,
WhenSessionHandlerDestroyed_ShouldRemoveSessionHandlerPtrFromSessionManager) {
InitializeSessionHandler();
- auto id = session_->id();
+ auto id = session_handler_->session()->id();
+
+ auto session_manager = engine_->session_context().session_manager;
+ ASSERT_NE(session_manager, nullptr);
EXPECT_NE(session_handler_.get(), nullptr);
- EXPECT_EQ(session_manager_->FindSessionHandler(id), session_handler_.get());
+ EXPECT_EQ(session_manager->FindSessionHandler(id), session_handler_.get());
- ResetSessionHandler();
+ session_handler_.reset();
EXPECT_EQ(session_handler_.get(), nullptr);
- EXPECT_EQ(session_manager_->FindSessionHandler(id), nullptr);
+ EXPECT_EQ(session_manager->FindSessionHandler(id), nullptr);
}
} // namespace test
diff --git a/garnet/lib/ui/gfx/tests/session_test.cc b/garnet/lib/ui/gfx/tests/session_test.cc
index 50105b2..785679a 100644
--- a/garnet/lib/ui/gfx/tests/session_test.cc
+++ b/garnet/lib/ui/gfx/tests/session_test.cc
@@ -4,6 +4,7 @@
#include "garnet/lib/ui/gfx/tests/session_test.h"
+#include "garnet/lib/ui/gfx/engine/default_frame_scheduler.h"
#include "garnet/lib/ui/gfx/tests/mocks.h"
#include "lib/fxl/logging.h"
@@ -12,45 +13,39 @@
namespace gfx {
namespace test {
-FakeUpdateScheduler::FakeUpdateScheduler(SessionManager* session_manager)
- : session_manager_(session_manager) {}
-
-void FakeUpdateScheduler::ScheduleUpdate(uint64_t presentation_time) {
- CommandContext empty_command_context(nullptr);
- session_manager_->ApplyScheduledSessionUpdates(&empty_command_context,
- presentation_time, 0);
-}
-
void SessionTest::SetUp() { session_ = CreateSession(); }
void SessionTest::TearDown() {
+ session_.reset();
session_manager_.reset();
- if (session_) {
- session_.reset();
- }
+ frame_scheduler_.reset();
+ display_manager_.reset();
events_.clear();
}
SessionContext SessionTest::CreateBarebonesSessionContext() {
session_manager_ = std::make_unique<SessionManagerForTest>();
- update_scheduler_ =
- std::make_unique<FakeUpdateScheduler>(session_manager_.get());
+
+ display_manager_ = std::make_unique<DisplayManager>();
+ display_manager_->SetDefaultDisplayForTests(std::make_unique<Display>(
+ /*id*/ 0, /*px-width*/ 0, /*px-height*/ 0));
+ frame_scheduler_ = std::make_unique<DefaultFrameScheduler>(
+ display_manager_->default_display());
SessionContext session_context{
vk::Device(),
- nullptr, // escher::Escher*
- 0, // imported_memory_type_index;
- nullptr, // escher::ResourceRecycler
- nullptr, // escher::ImageFactory*
- nullptr, // escher::RoundedRectFactory*
- nullptr, // escher::ReleaseFenceSignaller*
- nullptr, // EventTimestamper*
- session_manager_.get(), // SessionManager*
- nullptr, // FrameScheduler*
- update_scheduler_.get(), // UpdateScheduler*
- nullptr, // DisplayManager*
- SceneGraphWeakPtr(), // SceneGraphWeakPtr
- nullptr, // ResourceLinker*
- nullptr // ViewLinker*
+ nullptr, // escher::Escher*
+ 0, // imported_memory_type_index;
+ nullptr, // escher::ResourceRecycler
+ nullptr, // escher::ImageFactory*
+ nullptr, // escher::RoundedRectFactory*
+ nullptr, // escher::ReleaseFenceSignaller*
+ nullptr, // EventTimestamper*
+ session_manager_.get(), // SessionManager*
+ frame_scheduler_.get(), // FrameScheduler*
+ display_manager_.get(), // DisplayManager*
+ SceneGraphWeakPtr(), // SceneGraphWeakPtr
+ nullptr, // ResourceLinker*
+ nullptr // ViewLinker*
};
return session_context;
}
diff --git a/garnet/lib/ui/gfx/tests/session_test.h b/garnet/lib/ui/gfx/tests/session_test.h
index 3916eaf..adabf02 100644
--- a/garnet/lib/ui/gfx/tests/session_test.h
+++ b/garnet/lib/ui/gfx/tests/session_test.h
@@ -20,16 +20,6 @@
namespace gfx {
namespace test {
-class FakeUpdateScheduler : public UpdateScheduler {
- public:
- FakeUpdateScheduler(SessionManager* session_manager);
-
- void ScheduleUpdate(uint64_t presentation_time) override;
-
- private:
- SessionManager* session_manager_ = nullptr;
-};
-
class SessionTest : public ErrorReportingTest, public EventReporter {
protected:
// | ::testing::Test |
@@ -45,7 +35,7 @@
virtual std::unique_ptr<SessionForTest> CreateSession();
// Creates a SessionContext with only a SessionManager and a
- // FakeUpdateScheduler.
+ // FrameScheduler.
SessionContext CreateBarebonesSessionContext();
// Apply the specified Command. Return true if it was applied successfully,
@@ -60,7 +50,8 @@
return session_->resources()->FindResource<ResourceT>(id);
}
- std::unique_ptr<UpdateScheduler> update_scheduler_;
+ std::unique_ptr<DisplayManager> display_manager_;
+ std::unique_ptr<FrameScheduler> frame_scheduler_;
std::unique_ptr<SessionForTest> session_;
std::unique_ptr<SessionManagerForTest> session_manager_;
std::vector<fuchsia::ui::scenic::Event> events_;
diff --git a/garnet/lib/ui/scenic/tests/mocks.cc b/garnet/lib/ui/scenic/tests/mocks.cc
index 4b5f2de..a513ed4 100644
--- a/garnet/lib/ui/scenic/tests/mocks.cc
+++ b/garnet/lib/ui/scenic/tests/mocks.cc
@@ -4,6 +4,8 @@
#include "garnet/lib/ui/scenic/tests/mocks.h"
+#include "garnet/lib/ui/gfx/engine/default_frame_scheduler.h"
+
namespace scenic_impl {
namespace test {
@@ -21,7 +23,7 @@
gfx::DisplayManager* display_manager,
std::unique_ptr<escher::ReleaseFenceSignaller> release_signaler,
escher::EscherWeakPtr escher)
- : gfx::Engine(std::make_unique<gfx::FrameScheduler>(
+ : gfx::Engine(std::make_unique<gfx::DefaultFrameScheduler>(
display_manager->default_display()),
display_manager, std::move(release_signaler),
std::make_unique<gfx::SessionManager>(), std::move(escher)) {}