blob: 49cffbad61aaad02a5dd27b4c11068b224338a9d [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/lib/ui/gfx/engine/frame_timings.h"
#include "garnet/lib/ui/gfx/engine/frame_scheduler.h"
namespace scenic_impl {
namespace gfx {
FrameTimings::FrameTimings(FrameScheduler* frame_scheduler,
uint64_t frame_number,
zx_time_t target_presentation_time,
zx_time_t rendering_started_time)
: frame_scheduler_(frame_scheduler),
frame_number_(frame_number),
target_presentation_time_(target_presentation_time),
rendering_started_time_(rendering_started_time) {}
size_t FrameTimings::AddSwapchain(Swapchain* swapchain) {
// All swapchains that we are timing must be added before any of them finish.
// The purpose of this is to verify that we cannot notify the FrameScheduler
// that the frame has finished before all swapchains have been added.
FXL_DCHECK(frame_rendered_count_ == 0);
FXL_DCHECK(frame_presented_count_ == 0);
swapchain_records_.push_back({});
return swapchain_records_.size() - 1;
}
void FrameTimings::OnFrameRendered(size_t swapchain_index, zx_time_t time) {
if (finalized()) {
// Frame was dropped, but we received a delayed OnFrameRendered event.
FXL_LOG(INFO)
<< "Frame was dropped, but received a delayed OnFrameRendered event.";
return;
}
FXL_DCHECK(swapchain_index < swapchain_records_.size());
FXL_DCHECK(frame_rendered_count_ < swapchain_records_.size())
<< "Frame rendered count: " << frame_rendered_count_
<< " Swapchain records size: " << swapchain_records_.size();
FXL_DCHECK(time > 0);
auto& record = swapchain_records_[swapchain_index];
FXL_DCHECK(swapchain_records_[swapchain_index].frame_rendered_time == 0);
if (record.frame_presented_time > 0 && record.frame_presented_time < time) {
// NOTE: Because there is a delay between when rendering is actually
// completed and when EventTimestamper generates the timestamp, it's
// possible that this timestamp is later than the present timestamp. Since
// we know that's actually impossible, adjust the render timestamp to
// make it a bit more accurate.
time = record.frame_presented_time;
}
swapchain_records_[swapchain_index].frame_rendered_time = time;
++frame_rendered_count_;
// TODO(SCN-1324): We currently only return the time of the last received
// callback. This is not a problem right now, since we only have cases with a
// single swapchain/display, but need to figure out how to handle the general
// case.
rendering_finished_time_ = time;
if (received_all_frame_rendered_callbacks() && frame_scheduler_) {
frame_scheduler_->OnFrameRendered(*this);
}
if (received_all_callbacks()) {
Finalize();
}
}
void FrameTimings::OnFramePresented(size_t swapchain_index, zx_time_t time) {
FXL_DCHECK(swapchain_index < swapchain_records_.size());
FXL_DCHECK(frame_presented_count_ < swapchain_records_.size());
FXL_DCHECK(swapchain_records_[swapchain_index].frame_presented_time == 0);
FXL_DCHECK(time > 0);
swapchain_records_[swapchain_index].frame_presented_time = time;
if (time > actual_presentation_time_) {
actual_presentation_time_ = time;
}
++frame_presented_count_;
if (received_all_callbacks()) {
Finalize();
}
}
void FrameTimings::OnFrameDropped(size_t swapchain_index) {
// Indicates that "frame was dropped".
actual_presentation_time_ = ZX_TIME_INFINITE;
// The record should also reflect that "frame was dropped". Additionally,
// update counts to simulate calls to OnFrameRendered/OnFramePresented; this
// maintains count-related invariants.
Record& record = swapchain_records_[swapchain_index];
if (record.frame_presented_time == 0) {
record.frame_presented_time = ZX_TIME_INFINITE;
actual_presentation_time_ = ZX_TIME_INFINITE;
++frame_presented_count_;
}
if (record.frame_rendered_time == 0) {
record.frame_rendered_time = ZX_TIME_INFINITE;
rendering_finished_time_ = ZX_TIME_INFINITE;
++frame_rendered_count_;
}
FXL_DCHECK(received_all_callbacks())
<< "Callback counts for render/present are incorrect.";
// Do scheduler-related cleanup.
Finalize();
}
void FrameTimings::Finalize() {
FXL_DCHECK(!finalized());
finalized_ = true;
if (frame_scheduler_) {
frame_scheduler_->OnFramePresented(*this);
}
}
} // namespace gfx
} // namespace scenic_impl