EGL: Use frameId instead of framesAgo for frame events.
Using a frameId allows an app to poll for timestamps
from a thread other than the swapping thread.
Test: adb shell /data/nativetest/libgui_test/libgui_test
--gtest_filter=*GetFrameTimestamps*
Change-Id: I3faac0513929837982a2e63f7e0d3d529bd28f10
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d1582d1..1afa86c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -20,7 +20,7 @@
#include <gui/BufferQueue.h>
#include <sync/sync.h>
#include <utils/StrongPointer.h>
-#include <utils/SortedVector.h>
+#include <utils/Vector.h>
#include "driver.h"
@@ -108,19 +108,11 @@
class TimingInfo {
public:
- TimingInfo()
- : vals_{0, 0, 0, 0, 0},
- timestamp_desired_present_time_(0),
- timestamp_actual_present_time_(0),
- timestamp_render_complete_time_(0),
- timestamp_composition_latch_time_(0) {}
- TimingInfo(const VkPresentTimeGOOGLE* qp)
+ TimingInfo() = default;
+ TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId)
: vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
- timestamp_desired_present_time_(0),
- timestamp_actual_present_time_(0),
- timestamp_render_complete_time_(0),
- timestamp_composition_latch_time_(0) {}
- bool ready() {
+ native_frame_id_(nativeFrameId) {}
+ bool ready() const {
return (timestamp_desired_present_time_ &&
timestamp_actual_present_time_ &&
timestamp_render_complete_time_ &&
@@ -148,27 +140,20 @@
vals_.earliestPresentTime = early_time;
vals_.presentMargin = margin;
}
- void get_values(VkPastPresentationTimingGOOGLE* values) { *values = vals_; }
+ void get_values(VkPastPresentationTimingGOOGLE* values) const {
+ *values = vals_;
+ }
public:
- VkPastPresentationTimingGOOGLE vals_;
+ VkPastPresentationTimingGOOGLE vals_ { 0, 0, 0, 0, 0 };
- uint64_t timestamp_desired_present_time_;
- uint64_t timestamp_actual_present_time_;
- uint64_t timestamp_render_complete_time_;
- uint64_t timestamp_composition_latch_time_;
+ uint64_t native_frame_id_ { 0 };
+ uint64_t timestamp_desired_present_time_ { 0 };
+ uint64_t timestamp_actual_present_time_ { 0 };
+ uint64_t timestamp_render_complete_time_ { 0 };
+ uint64_t timestamp_composition_latch_time_ { 0 };
};
-static inline int compare_type(const TimingInfo& lhs, const TimingInfo& rhs) {
- // TODO(ianelliott): Change this from presentID to the frame ID once
- // brianderson lands the appropriate patch:
- if (lhs.vals_.presentID < rhs.vals_.presentID)
- return -1;
- if (lhs.vals_.presentID > rhs.vals_.presentID)
- return 1;
- return 0;
-}
-
// ----------------------------------------------------------------------------
struct Surface {
@@ -195,7 +180,6 @@
: surface(surface_),
num_images(num_images_),
frame_timestamps_enabled(false) {
- timing.clear();
ANativeWindow* window = surface.window.get();
int64_t rdur;
native_window_get_refresh_cycle_duration(
@@ -221,7 +205,7 @@
bool dequeued;
} images[android::BufferQueue::NUM_BUFFER_SLOTS];
- android::SortedVector<TimingInfo> timing;
+ android::Vector<TimingInfo> timing;
};
VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -293,73 +277,64 @@
}
uint32_t get_num_ready_timings(Swapchain& swapchain) {
- uint32_t num_ready = 0;
- uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
- uint32_t frames_ago = num_timings;
- for (uint32_t i = 0; i < num_timings; i++) {
- TimingInfo* ti = &swapchain.timing.editItemAt(i);
- if (ti) {
- if (ti->ready()) {
- // This TimingInfo is ready to be reported to the user. Add it
- // to the num_ready.
- num_ready++;
- } else {
- // This TimingInfo is not yet ready to be reported to the user,
- // and so we should look for any available timestamps that
- // might make it ready.
- int64_t desired_present_time = 0;
- int64_t render_complete_time = 0;
- int64_t composition_latch_time = 0;
- int64_t actual_present_time = 0;
- for (uint32_t f = MIN_NUM_FRAMES_AGO; f < frames_ago; f++) {
- // Obtain timestamps:
- int ret = native_window_get_frame_timestamps(
- swapchain.surface.window.get(), f,
- &desired_present_time, &render_complete_time,
- &composition_latch_time,
- NULL, //&first_composition_start_time,
- NULL, //&last_composition_start_time,
- NULL, //&composition_finish_time,
- // TODO(ianelliott): Maybe ask if this one is
- // supported, at startup time (since it may not be
- // supported):
- &actual_present_time,
- NULL, //&display_retire_time,
- NULL, //&dequeue_ready_time,
- NULL /*&reads_done_time*/);
- if (ret) {
- break;
- } else if (!ret) {
- // We obtained at least one valid timestamp. See if it
- // is for the present represented by this TimingInfo:
- if (static_cast<uint64_t>(desired_present_time) ==
- ti->vals_.desiredPresentTime) {
- // Record the timestamp(s) we received, and then
- // see if this TimingInfo is ready to be reported
- // to the user:
- ti->timestamp_desired_present_time_ =
- static_cast<uint64_t>(desired_present_time);
- ti->timestamp_actual_present_time_ =
- static_cast<uint64_t>(actual_present_time);
- ti->timestamp_render_complete_time_ =
- static_cast<uint64_t>(render_complete_time);
- ti->timestamp_composition_latch_time_ =
- static_cast<uint64_t>(composition_latch_time);
+ if (swapchain.timing.size() < MIN_NUM_FRAMES_AGO) {
+ return 0;
+ }
- if (ti->ready()) {
- // The TimingInfo has received enough
- // timestamps, and should now use those
- // timestamps to calculate the info that should
- // be reported to the user:
- //
- ti->calculate(swapchain.refresh_duration);
- num_ready++;
- }
- break;
- }
- }
- }
- }
+ uint32_t num_ready = 0;
+ const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1;
+ for (uint32_t i = 0; i < num_timings; i++) {
+ TimingInfo& ti = swapchain.timing.editItemAt(i);
+ if (ti.ready()) {
+ // This TimingInfo is ready to be reported to the user. Add it
+ // to the num_ready.
+ num_ready++;
+ continue;
+ }
+ // This TimingInfo is not yet ready to be reported to the user,
+ // and so we should look for any available timestamps that
+ // might make it ready.
+ int64_t desired_present_time = 0;
+ int64_t render_complete_time = 0;
+ int64_t composition_latch_time = 0;
+ int64_t actual_present_time = 0;
+ // Obtain timestamps:
+ int ret = native_window_get_frame_timestamps(
+ swapchain.surface.window.get(), ti.native_frame_id_,
+ &desired_present_time, &render_complete_time,
+ &composition_latch_time,
+ NULL, //&first_composition_start_time,
+ NULL, //&last_composition_start_time,
+ NULL, //&composition_finish_time,
+ // TODO(ianelliott): Maybe ask if this one is
+ // supported, at startup time (since it may not be
+ // supported):
+ &actual_present_time,
+ NULL, //&display_retire_time,
+ NULL, //&dequeue_ready_time,
+ NULL /*&reads_done_time*/);
+
+ if (ret != android::NO_ERROR) {
+ continue;
+ }
+
+ // Record the timestamp(s) we received, and then see if this TimingInfo
+ // is ready to be reported to the user:
+ ti.timestamp_desired_present_time_ =
+ static_cast<uint64_t>(desired_present_time);
+ ti.timestamp_actual_present_time_ =
+ static_cast<uint64_t>(actual_present_time);
+ ti.timestamp_render_complete_time_ =
+ static_cast<uint64_t>(render_complete_time);
+ ti.timestamp_composition_latch_time_ =
+ static_cast<uint64_t>(composition_latch_time);
+
+ if (ti.ready()) {
+ // The TimingInfo has received enough timestamps, and should now
+ // use those timestamps to calculate the info that should be
+ // reported to the user:
+ ti.calculate(swapchain.refresh_duration);
+ num_ready++;
}
}
return num_ready;
@@ -369,29 +344,35 @@
void copy_ready_timings(Swapchain& swapchain,
uint32_t* count,
VkPastPresentationTimingGOOGLE* timings) {
- uint32_t num_copied = 0;
- uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
- if (*count < num_timings) {
- num_timings = *count;
+ if (swapchain.timing.empty()) {
+ *count = 0;
+ return;
}
- for (uint32_t i = 0; i < num_timings; i++) {
- TimingInfo* ti = &swapchain.timing.editItemAt(i);
- if (ti && ti->ready()) {
- ti->get_values(&timings[num_copied]);
- num_copied++;
- // We only report the values for a given present once, so remove
- // them from swapchain.timing:
- //
- // TODO(ianelliott): SEE WHAT HAPPENS TO THE LOOP WHEN THE
- // FOLLOWING IS DONE:
- swapchain.timing.removeAt(i);
- i--;
- num_timings--;
- if (*count == num_copied) {
- break;
- }
+
+ size_t last_ready = swapchain.timing.size() - 1;
+ while (!swapchain.timing[last_ready].ready()) {
+ if (last_ready == 0) {
+ *count = 0;
+ return;
}
+ last_ready--;
}
+
+ uint32_t num_copied = 0;
+ size_t num_to_remove = 0;
+ for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) {
+ const TimingInfo& ti = swapchain.timing[i];
+ if (ti.ready()) {
+ ti.get_values(&timings[num_copied]);
+ num_copied++;
+ }
+ num_to_remove++;
+ }
+
+ // Discard old frames that aren't ready if newer frames are ready.
+ // We don't expect to get the timing info for those old frames.
+ swapchain.timing.removeItemsAt(0, num_to_remove);
+
*count = num_copied;
}
@@ -1219,13 +1200,20 @@
native_window_enable_frame_timestamps(window, true);
swapchain.frame_timestamps_enabled = true;
}
- // Record this presentID and desiredPresentTime so it can
- // be later correlated to this present.
- TimingInfo timing_record(time);
- swapchain.timing.add(timing_record);
- uint32_t num_timings =
- static_cast<uint32_t>(swapchain.timing.size());
- if (num_timings > MAX_TIMING_INFOS) {
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::NO_ERROR) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.push_back(TimingInfo(time, nativeFrameId));
+ while (swapchain.timing.size() > MAX_TIMING_INFOS) {
swapchain.timing.removeAt(0);
}
if (time->desiredPresentTime) {