Add libvrflinger for use in SurfaceFlinger
A separate CL uses this code from SurfaceFlinger.
Bug: None
Test: Manually ran modified SurfaceFlinger
Change-Id: I34588df1365588c0a0265e1e2325e3dd5516206a
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
new file mode 100644
index 0000000..48fa2c2
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -0,0 +1,208 @@
+#include "vsync_service.h"
+
+#include <log/log.h>
+#include <hardware/hwcomposer.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/display_types.h>
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VSyncService::VSyncService()
+ : BASE("VSyncService", Endpoint::Create(DisplayVSyncRPC::kClientPath)),
+ last_vsync_(0),
+ current_vsync_(0),
+ compositor_time_ns_(0),
+ current_vsync_count_(0) {}
+
+VSyncService::~VSyncService() {}
+
+void VSyncService::VSyncEvent(int display, int64_t timestamp_ns,
+ int64_t compositor_time_ns,
+ uint32_t vsync_count) {
+ ATRACE_NAME("VSyncService::VSyncEvent");
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ if (display == HWC_DISPLAY_PRIMARY) {
+ last_vsync_ = current_vsync_;
+ current_vsync_ = timestamp_ns;
+ compositor_time_ns_ = compositor_time_ns;
+ current_vsync_count_ = vsync_count;
+
+ NotifyWaiters();
+ UpdateClients();
+ }
+}
+
+std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
+ AddClient(client);
+
+ return client;
+}
+
+void VSyncService::OnChannelClose(pdx::Message& /*message*/,
+ const std::shared_ptr<Channel>& channel) {
+ auto client = std::static_pointer_cast<VSyncChannel>(channel);
+ if (!client) {
+ ALOGW("WARNING: VSyncChannel was NULL!!!\n");
+ return;
+ }
+
+ RemoveClient(client);
+}
+
+void VSyncService::AddWaiter(pdx::Message& message) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
+ waiters_.push_back(std::move(waiter));
+}
+
+void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ clients_.push_back(client);
+}
+
+void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ clients_.remove(client);
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::NotifyWaiters() {
+ ATRACE_NAME("VSyncService::NotifyWaiters");
+ auto first = waiters_.begin();
+ auto last = waiters_.end();
+
+ while (first != last) {
+ (*first)->Notify(current_vsync_);
+ waiters_.erase(first++);
+ }
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::UpdateClients() {
+ ATRACE_NAME("VSyncService::UpdateClients");
+ auto first = clients_.begin();
+ auto last = clients_.end();
+
+ while (first != last) {
+ (*first)->Signal();
+ first++;
+ }
+}
+
+int VSyncService::HandleMessage(pdx::Message& message) {
+ switch (message.GetOp()) {
+ case DisplayVSyncRPC::Wait::Opcode:
+ AddWaiter(message);
+ return 0;
+
+ case DisplayVSyncRPC::GetLastTimestamp::Opcode:
+ DispatchRemoteMethod<DisplayVSyncRPC::GetLastTimestamp>(
+ *this, &VSyncService::OnGetLastTimestamp, message);
+ return 0;
+
+ case DisplayVSyncRPC::GetSchedInfo::Opcode:
+ DispatchRemoteMethod<DisplayVSyncRPC::GetSchedInfo>(
+ *this, &VSyncService::OnGetSchedInfo, message);
+ return 0;
+
+ case DisplayVSyncRPC::Acknowledge::Opcode:
+ DispatchRemoteMethod<DisplayVSyncRPC::Acknowledge>(
+ *this, &VSyncService::OnAcknowledge, message);
+ return 0;
+
+ default:
+ return Service::HandleMessage(message);
+ }
+}
+
+int64_t VSyncService::OnGetLastTimestamp(pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // Getting the timestamp has the side effect of ACKing.
+ client->Ack();
+ return current_vsync_;
+}
+
+VSyncSchedInfo VSyncService::OnGetSchedInfo(pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // Getting the timestamp has the side effect of ACKing.
+ client->Ack();
+
+ uint32_t next_vsync_count = current_vsync_count_ + 1;
+ int64_t current_time = GetSystemClockNs();
+ int64_t vsync_period_ns = 0;
+ int64_t next_warp;
+ if (current_vsync_ == 0 || last_vsync_ == 0) {
+ // Handle startup when current_vsync_ or last_vsync_ are 0.
+ // Normally should not happen because vsync_service is running before
+ // applications, but in case it does a sane time prevents applications
+ // from malfunctioning.
+ vsync_period_ns = 20000000;
+ next_warp = current_time;
+ } else {
+ // TODO(jbates) When we have an accurate reading of the true vsync
+ // period, use that instead of this estimated value.
+ vsync_period_ns = current_vsync_ - last_vsync_;
+ // Clamp the period, because when there are no surfaces the last_vsync_
+ // value will get stale. Note this is temporary and goes away as soon
+ // as we have an accurate vsync period reported by the system.
+ vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
+ next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
+ // If the request missed the present window, move up to the next vsync.
+ if (current_time > next_warp) {
+ next_warp += vsync_period_ns;
+ ++next_vsync_count;
+ }
+ }
+
+ return {vsync_period_ns, next_warp, next_vsync_count};
+}
+
+int VSyncService::OnAcknowledge(pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+ client->Ack();
+ return 0;
+}
+
+void VSyncWaiter::Notify(int64_t timestamp) {
+ timestamp_ = timestamp;
+ DispatchRemoteMethod<DisplayVSyncRPC::Wait>(*this, &VSyncWaiter::OnWait,
+ message_);
+}
+
+int64_t VSyncWaiter::OnWait(pdx::Message& /*message*/) { return timestamp_; }
+
+void VSyncChannel::Ack() {
+ ALOGD_IF(TRACE, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
+ service_.ModifyChannelEvents(cid_, POLLPRI, 0);
+}
+
+void VSyncChannel::Signal() {
+ ALOGD_IF(TRACE, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
+ service_.ModifyChannelEvents(cid_, 0, POLLPRI);
+}
+
+} // namespace dvr
+} // namespace android