Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flatland] Handle fence overflow in flatland_connection.cc #53366

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 155 additions & 10 deletions shell/platform/fuchsia/flutter/flatland_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

#include "flatland_connection.h"

#include <lib/async/cpp/task.h>
#include <lib/async/default.h>

#include <zircon/rights.h>
#include <zircon/status.h>
#include <zircon/types.h>

#include <utility>

#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"

namespace flutter_runner {

Expand All @@ -22,12 +28,14 @@ double DeltaFromNowInNanoseconds(const fml::TimePoint& now,
} // namespace

FlatlandConnection::FlatlandConnection(
std::string debug_label,
const std::string& debug_label,
fuchsia::ui::composition::FlatlandHandle flatland,
fml::closure error_callback,
on_frame_presented_event on_frame_presented_callback)
: flatland_(flatland.Bind()),
error_callback_(error_callback),
on_frame_presented_event on_frame_presented_callback,
async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher),
flatland_(flatland.Bind()),
error_callback_(std::move(error_callback)),
on_frame_presented_callback_(std::move(on_frame_presented_callback)) {
flatland_.set_error_handler([callback = error_callback_](zx_status_t status) {
FML_LOG(ERROR) << "Flatland disconnected: " << zx_status_get_string(status);
Expand Down Expand Up @@ -71,6 +79,42 @@ void FlatlandConnection::DoPresent() {
fuchsia::ui::composition::PresentArgs present_args;
present_args.set_requested_presentation_time(0);
present_args.set_acquire_fences(std::move(acquire_fences_));

// Schedule acquire fence overflow signaling if there is one.
if (acquire_overflow_ != nullptr) {
FML_CHECK(acquire_overflow_->event_.is_valid());
async::PostTask(dispatcher_, [dispatcher = dispatcher_,
overflow = acquire_overflow_]() {
const size_t fences_size = overflow->fences_.size();
std::shared_ptr<size_t> fences_completed = std::make_shared<size_t>(0);
std::shared_ptr<std::vector<async::WaitOnce>> closures;

for (auto i = 0u; i < fences_size; i++) {
auto wait = std::make_unique<async::WaitOnce>(
overflow->fences_[i].get(), ZX_EVENT_SIGNALED, 0u);
auto wait_ptr = wait.get();
wait_ptr->Begin(
dispatcher,
[wait = std::move(wait), overflow, fences_size, fences_completed,
closures](async_dispatcher_t*, async::WaitOnce*,
zx_status_t status, const zx_packet_signal_t*) {
(*fences_completed)++;
FML_CHECK(status == ZX_OK)
<< "status: " << zx_status_get_string(status);
if (*fences_completed == fences_size) {
// Signal the acquire fence passed on to Flatland.
const zx_status_t status =
overflow->event_.signal(0, ZX_EVENT_SIGNALED);
FML_CHECK(status == ZX_OK)
<< "status: " << zx_status_get_string(status);
}
});
}
});
acquire_overflow_.reset();
}
FML_CHECK(acquire_overflow_ == nullptr);

present_args.set_release_fences(std::move(previous_present_release_fences_));
// Frame rate over latency.
present_args.set_unsquashable(true);
Expand All @@ -81,6 +125,44 @@ void FlatlandConnection::DoPresent() {
// the correct ones for VulkanSurface's interpretation.
previous_present_release_fences_.clear();
previous_present_release_fences_.swap(current_present_release_fences_);
previous_release_overflow_ = current_release_overflow_;
current_release_overflow_ = nullptr;

// Similar to the treatment of acquire_fences_overflow_ above. Except in
// the other direction.
if (previous_release_overflow_ != nullptr) {
FML_CHECK(previous_release_overflow_->event_.is_valid());

std::shared_ptr<Overflow> fences = previous_release_overflow_;

async::PostTask(dispatcher_, [dispatcher = dispatcher_,
fences = previous_release_overflow_]() {
FML_CHECK(fences != nullptr);
FML_CHECK(fences->event_.is_valid());

auto wait = std::make_unique<async::WaitOnce>(fences->event_.get(),
ZX_EVENT_SIGNALED, 0u);
auto wait_ptr = wait.get();

wait_ptr->Begin(
dispatcher, [_wait = std::move(wait), fences](
async_dispatcher_t*, async::WaitOnce*,
zx_status_t status, const zx_packet_signal_t*) {
FML_CHECK(status == ZX_OK)
<< "status: " << zx_status_get_string(status);

// Multiplex signaling all events.
for (auto& event : fences->fences_) {
const zx_status_t status = event.signal(0, ZX_EVENT_SIGNALED);
FML_CHECK(status == ZX_OK)
<< "status: " << zx_status_get_string(status);
}
});
});
previous_release_overflow_ = nullptr;
}
FML_CHECK(previous_release_overflow_ == nullptr); // Moved.

acquire_fences_.clear();
}

Expand All @@ -93,12 +175,13 @@ void FlatlandConnection::AwaitVsync(FireCallbackCallback callback) {
const auto now = fml::TimePoint::Now();

// Initial case.
if (MaybeRunInitialVsyncCallback(now, callback))
if (MaybeRunInitialVsyncCallback(now, callback)) {
return;
}

// Throttle case.
if (threadsafe_state_.present_credits_ == 0) {
threadsafe_state_.pending_fire_callback_ = callback;
threadsafe_state_.pending_fire_callback_ = std::move(callback);
return;
}

Expand All @@ -116,8 +199,9 @@ void FlatlandConnection::AwaitVsyncForSecondaryCallback(
const auto now = fml::TimePoint::Now();

// Initial case.
if (MaybeRunInitialVsyncCallback(now, callback))
if (MaybeRunInitialVsyncCallback(now, callback)) {
return;
}

// Regular case.
RunVsyncCallback(now, callback);
Expand Down Expand Up @@ -260,14 +344,75 @@ void FlatlandConnection::RunVsyncCallback(const fml::TimePoint& now,
callback(frame_start, frame_end);
}

// Enqueue a single fence into either the "base" vector of fences, or a
// "special" overflow multiplexer.
//
// Args:
// - fence: the fence to add
// - fences: the "regular" fences vector to add to.
// - overflow: the overflow fences vector. Fences added here if there are
// more than can fit in `fences`.
static void Enqueue(zx::event fence,
std::vector<zx::event>* fences,
std::shared_ptr<Overflow>* overflow) {
constexpr size_t kMaxFences =
fuchsia::ui::composition::MAX_ACQUIRE_RELEASE_FENCE_COUNT;

// Number of all previously added fences, plus this one.
const auto num_all_fences =
fences->size() + 1 +
((*overflow == nullptr) ? 0 : (*overflow)->fences_.size());

// If more than max number of fences come in, schedule any further fences into
// an overflow. The overflow fences are scheduled for processing here, but are
// processed in DoPresent().
if (num_all_fences <= kMaxFences) {
fences->push_back(std::move(fence));
} else if (num_all_fences == kMaxFences + 1) {
// The ownership of the overflow will be handed over to the signaling
// closure on DoPresent call. So we always expect that we enter here with
// overflow not set.
FML_CHECK((*overflow) == nullptr) << "overflow is still active";
*overflow = std::make_shared<Overflow>();

// Set up the overflow fences. Creates an overflow handle, places it
// into `fences` instead of the previous fence, and puts the prior fence
// and this one into overflow.
zx::event overflow_handle = std::move(fences->back());
fences->pop_back();

zx::event overflow_fence;
zx_status_t status = zx::event::create(0, &overflow_fence);
FML_CHECK(status == ZX_OK) << "status: " << zx_status_get_string(status);

// Every DoPresent should invalidate this handle. Holler if not.
FML_CHECK(!(*overflow)->event_.is_valid()) << "overflow valid";
status =
overflow_fence.duplicate(ZX_RIGHT_SAME_RIGHTS, &(*overflow)->event_);
FML_CHECK(status == ZX_OK) << "status: " << zx_status_get_string(status);
fences->push_back(std::move(overflow_fence));

// Prepare for wait_many call.
(*overflow)->fences_.push_back(std::move(overflow_handle));
(*overflow)->fences_.push_back(std::move(fence));

FML_LOG(INFO) << "Enqueue using fence overflow, expect a performance hit.";
} else {
FML_CHECK((*overflow) != nullptr);
// Just add to the overflow fences.
(*overflow)->fences_.push_back(std::move(fence));
}
}

// This method is called from the raster thread.
void FlatlandConnection::EnqueueAcquireFence(zx::event fence) {
acquire_fences_.push_back(std::move(fence));
Enqueue(std::move(fence), &acquire_fences_, &acquire_overflow_);
}

// This method is called from the raster thread.
void FlatlandConnection::EnqueueReleaseFence(zx::event fence) {
current_present_release_fences_.push_back(std::move(fence));
Enqueue(std::move(fence), &current_present_release_fences_,
&current_release_overflow_);
}

} // namespace flutter_runner
58 changes: 54 additions & 4 deletions shell/platform/fuchsia/flutter/flatland_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Maintains a connection to Fuchsia's Flatland protocol used for rendering
// 2D graphics scenes.

#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FLATLAND_CONNECTION_H_
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FLATLAND_CONNECTION_H_

#include <fuchsia/ui/composition/cpp/fidl.h>
#include <lib/async/default.h>

#include "flutter/fml/closure.h"
#include "flutter/fml/macros.h"
Expand All @@ -21,6 +25,19 @@

namespace flutter_runner {

// The maximum number of fences that can be signaled at a time.
static constexpr size_t kMaxFences =
fuchsia::ui::composition::MAX_ACQUIRE_RELEASE_FENCE_COUNT;

// A helper to ferry around multiplexed events for signaling. Helps move
// non-copyable events into closures.
class Overflow {
public:
std::vector<zx::event> fences_;
zx::event event_;
Overflow() { fences_.reserve(kMaxFences); };
};

using on_frame_presented_event =
std::function<void(fuchsia::scenic::scheduling::FramePresentedInfo)>;

Expand All @@ -33,10 +50,12 @@ static constexpr fml::TimeDelta kInitialFlatlandVsyncOffset =
// maintaining the Flatland instance connection and presenting updates.
class FlatlandConnection final {
public:
FlatlandConnection(std::string debug_label,
fuchsia::ui::composition::FlatlandHandle flatland,
fml::closure error_callback,
on_frame_presented_event on_frame_presented_callback);
FlatlandConnection(
const std::string& debug_label,
fuchsia::ui::composition::FlatlandHandle flatland,
fml::closure error_callback,
on_frame_presented_event on_frame_presented_callback,
async_dispatcher_t* dispatcher = async_get_default_dispatcher());

~FlatlandConnection();

Expand All @@ -58,7 +77,24 @@ class FlatlandConnection final {
return {++next_content_id_};
}

// Adds a new acquire fence to be sent out to the next Present() call.
//
// Acquire fences must all be signaled by the user.
//
// PERFORMANCE NOTES:
//
// Enqueuing more than 16 fences per frame incurs a performance penalty, so
// use them sparingly.
//
// Skipped frames may cause the number of fences to increase, leading to
// more performance issues. Ideally, the flow of the frames should be smooth.
void EnqueueAcquireFence(zx::event fence);

// Adds a new release fence to be sent out to the next Present() call.
//
// Release fences are all signaled by Scenic (Flatland server).
//
// See the performance notes on EnqueueAcquireFence for performance details.
void EnqueueReleaseFence(zx::event fence);

private:
Expand All @@ -75,6 +111,8 @@ class FlatlandConnection final {
void RunVsyncCallback(const fml::TimePoint& now,
FireCallbackCallback& callback);

async_dispatcher_t* dispatcher_;

fuchsia::ui::composition::FlatlandPtr flatland_;

fml::closure error_callback_;
Expand Down Expand Up @@ -102,9 +140,21 @@ class FlatlandConnection final {
bool first_feedback_received_ = false;
} threadsafe_state_;

// Acquire fences sent to Flatland.
std::vector<zx::event> acquire_fences_;

// Multiplexed acquire fences over the critical number of ~16.
std::shared_ptr<Overflow> acquire_overflow_;
filmil marked this conversation as resolved.
Show resolved Hide resolved

// Release fences sent to Flatland. Similar to acquire fences above.
std::vector<zx::event> current_present_release_fences_;
std::shared_ptr<Overflow> current_release_overflow_;

// Release fences from the prior call to DoPresent().
// Similar to acquire fences above.
std::vector<zx::event> previous_present_release_fences_;
std::shared_ptr<Overflow> previous_release_overflow_;

std::string debug_label_;

FML_DISALLOW_COPY_AND_ASSIGN(FlatlandConnection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fuchsia::ui::composition::FlatlandHandle FakeFlatland::ConnectFlatland(
}

void FakeFlatland::Disconnect(fuchsia::ui::composition::FlatlandError error) {
flatland_binding_.events().OnError(std::move(error));
flatland_binding_.events().OnError(error);
flatland_binding_.Unbind();
allocator_binding_
.Unbind(); // TODO(fxb/85619): Does the real Scenic unbind this when
Expand Down
Loading