#include "vsync_service.h" #include #include #include #include #include #include #include #include #include #include using android::dvr::display::VSyncProtocol; using android::dvr::display::VSyncSchedInfo; 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(VSyncProtocol::kClientPath)), last_vsync_(0), current_vsync_(0), compositor_time_ns_(0), current_vsync_count_(0) {} VSyncService::~VSyncService() {} void VSyncService::VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns, uint32_t vsync_count) { ATRACE_NAME("VSyncService::VSyncEvent"); std::lock_guard autolock(mutex_); last_vsync_ = current_vsync_; current_vsync_ = timestamp_ns; compositor_time_ns_ = compositor_time_ns; current_vsync_count_ = vsync_count; NotifyWaiters(); UpdateClients(); } std::shared_ptr VSyncService::OnChannelOpen(pdx::Message& message) { const MessageInfo& info = message.GetInfo(); auto client = std::make_shared(*this, info.pid, info.cid); AddClient(client); return client; } void VSyncService::OnChannelClose(pdx::Message& /*message*/, const std::shared_ptr& channel) { auto client = std::static_pointer_cast(channel); if (!client) { ALOGW("WARNING: VSyncChannel was NULL!!!\n"); return; } RemoveClient(client); } void VSyncService::AddWaiter(pdx::Message& message) { std::lock_guard autolock(mutex_); std::unique_ptr waiter(new VSyncWaiter(message)); waiters_.push_back(std::move(waiter)); } void VSyncService::AddClient(const std::shared_ptr& client) { std::lock_guard autolock(mutex_); clients_.push_back(client); } void VSyncService::RemoveClient(const std::shared_ptr& client) { std::lock_guard 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++; } } pdx::Status VSyncService::HandleMessage(pdx::Message& message) { ATRACE_NAME("VSyncService::HandleMessage"); switch (message.GetOp()) { case VSyncProtocol::Wait::Opcode: AddWaiter(message); return {}; case VSyncProtocol::GetLastTimestamp::Opcode: DispatchRemoteMethod( *this, &VSyncService::OnGetLastTimestamp, message); return {}; case VSyncProtocol::GetSchedInfo::Opcode: DispatchRemoteMethod( *this, &VSyncService::OnGetSchedInfo, message); return {}; case VSyncProtocol::Acknowledge::Opcode: DispatchRemoteMethod( *this, &VSyncService::OnAcknowledge, message); return {}; default: return Service::HandleMessage(message); } } pdx::Status VSyncService::OnGetLastTimestamp(pdx::Message& message) { auto client = std::static_pointer_cast(message.GetChannel()); std::lock_guard autolock(mutex_); // Getting the timestamp has the side effect of ACKing. client->Ack(); return {current_vsync_}; } pdx::Status VSyncService::OnGetSchedInfo( pdx::Message& message) { auto client = std::static_pointer_cast(message.GetChannel()); std::lock_guard 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}}; } pdx::Status VSyncService::OnAcknowledge(pdx::Message& message) { auto client = std::static_pointer_cast(message.GetChannel()); std::lock_guard autolock(mutex_); client->Ack(); return {}; } void VSyncWaiter::Notify(int64_t timestamp) { timestamp_ = timestamp; DispatchRemoteMethod(*this, &VSyncWaiter::OnWait, message_); } pdx::Status VSyncWaiter::OnWait(pdx::Message& /*message*/) { return {timestamp_}; } void VSyncChannel::Ack() { ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_); service_.ModifyChannelEvents(cid_, POLLPRI, 0); } void VSyncChannel::Signal() { ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_); service_.ModifyChannelEvents(cid_, 0, POLLPRI); } } // namespace dvr } // namespace android