blob: 93cfb7b07117451e3af40bad776cdab120a5ba20 [file] [log] [blame]
// Copyright 2020 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 "src/ui/scenic/lib/flatland/default_flatland_presenter.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
namespace flatland {
DefaultFlatlandPresenter::DefaultFlatlandPresenter(async_dispatcher_t* main_dispatcher)
: main_dispatcher_(main_dispatcher) {}
void DefaultFlatlandPresenter::SetFrameScheduler(
const std::shared_ptr<scheduling::FrameScheduler>& frame_scheduler) {
FX_DCHECK(frame_scheduler_.expired()) << "FrameScheduler already set.";
frame_scheduler_ = frame_scheduler;
}
std::vector<zx::event> DefaultFlatlandPresenter::TakeReleaseFences(
const std::unordered_map<scheduling::SessionId, scheduling::PresentId>&
highest_present_per_session) {
std::vector<zx::event> fences;
for (const auto& [session_id, present_id] : highest_present_per_session) {
const auto begin_it = release_fences_.lower_bound({session_id, 0});
const auto end_it = release_fences_.upper_bound({session_id, present_id});
FX_DCHECK(std::distance(begin_it, end_it) >= 0);
std::for_each(
begin_it, end_it,
[&fences](
std::pair<const scheduling::SchedulingIdPair, std::vector<zx::event>>& release_fences) {
std::move(std::begin(release_fences.second), std::end(release_fences.second),
std::back_inserter(fences));
});
release_fences_.erase(begin_it, end_it);
}
return fences;
}
scheduling::PresentId DefaultFlatlandPresenter::RegisterPresent(
scheduling::SessionId session_id, std::vector<zx::event> release_fences) {
scheduling::PresentId present_id = scheduling::kInvalidPresentId;
if (auto scheduler = frame_scheduler_.lock()) {
// Since FrameScheduler::RegisterPresent() will not run immediately, generate a PresentId
// independently.
present_id = scheduling::GetNextPresentId();
// TODO(fxbug.dev/61178): The FrameScheduler is not thread-safe, but a lock is not sufficient
// since GFX sessions may access the FrameScheduler without passing through this object. Post a
// task to the main thread, which is where GFX runs, to account for thread safety.
async::PostTask(main_dispatcher_, [thiz = shared_from_this(), scheduler, present_id, session_id,
release_fences = std::move(release_fences)]() mutable {
scheduling::SchedulingIdPair id_pair{session_id, present_id};
FX_DCHECK(thiz->release_fences_.find(id_pair) == thiz->release_fences_.end());
thiz->release_fences_.emplace(id_pair, std::move(release_fences));
scheduler->RegisterPresent(session_id, {}, present_id);
});
} else {
// TODO(fxbug.dev/56290): Account for missing FrameScheduler case.
FX_LOGS(WARNING) << "Cannot register present due to missing FrameScheduler.";
}
return present_id;
}
void DefaultFlatlandPresenter::ScheduleUpdateForSession(zx::time requested_presentation_time,
scheduling::SchedulingIdPair id_pair,
bool squashable) {
if (auto scheduler = frame_scheduler_.lock()) {
// TODO(fxbug.dev/61178): The FrameScheduler is not thread-safe, but a lock is not sufficient
// since GFX sessions may access the FrameScheduler without passing through this object. Post a
// task to the main thread, which is where GFX runs, to account for thread safety.
async::PostTask(
main_dispatcher_, [scheduler, requested_presentation_time, id_pair, squashable] {
scheduler->ScheduleUpdateForSession(requested_presentation_time, id_pair, squashable);
});
} else {
// TODO(fxbug.dev/56290): Account for missing FrameScheduler case.
FX_LOGS(WARNING) << "Cannot schedule update for session due to missing FrameScheduler.";
}
}
void DefaultFlatlandPresenter::GetFuturePresentationInfos(
scheduling::FrameScheduler::GetFuturePresentationInfosCallback presentation_infos_callback) {
auto flatland_thread_dispatcher = async_get_default_dispatcher();
FX_DCHECK(flatland_thread_dispatcher);
if (auto scheduler = frame_scheduler_.lock()) {
// TODO(fxbug.dev/61178): The FrameScheduler is not thread-safe, but a lock is not sufficient
// since GFX sessions may access the FrameScheduler without passing through this object. Post a
// task to the main thread, which is where GFX runs, to account for thread safety.
async::PostTask(
main_dispatcher_,
[flatland_thread_dispatcher, requested_prediction_span = kDefaultPredictionSpan, scheduler,
presentation_infos_callback = std::move(presentation_infos_callback)]() mutable {
scheduler->GetFuturePresentationInfos(
requested_prediction_span,
[flatland_thread_dispatcher,
presentation_infos_callback =
std::move(presentation_infos_callback)](auto presentation_infos) mutable {
// Post the frame scheduler's response back on the instance thread for dispatch.
async::PostTask(
flatland_thread_dispatcher,
[presentation_infos_callback = std::move(presentation_infos_callback),
presentation_infos = std::move(presentation_infos)]() mutable {
presentation_infos_callback(std::move(presentation_infos));
});
});
});
} else {
// TODO(fxbug.dev/56290): Account for missing FrameScheduler case.
FX_LOGS(WARNING) << "Cannot get future presentation infos due to missing FrameScheduler.";
}
}
void DefaultFlatlandPresenter::RemoveSession(scheduling::SessionId session_id) {
// Remove any registered release fences for the removed session.
{
auto start = release_fences_.lower_bound({session_id, 0});
auto end = release_fences_.lower_bound({session_id + 1, 0});
release_fences_.erase(start, end);
}
if (auto scheduler = frame_scheduler_.lock()) {
scheduler->RemoveSession(session_id);
} else {
// TODO(fxbug.dev/56290): Account for missing FrameScheduler case.
FX_LOGS(WARNING) << "Cannot remove session due to missing FrameScheduler.";
}
}
} // namespace flatland