#include "vsync_service.h" #include <hardware/hwcomposer.h> #include <log/log.h> #include <poll.h> #include <sys/prctl.h> #include <time.h> #include <utils/Trace.h> #include <dvr/dvr_display_types.h> #include <pdx/default_transport/service_endpoint.h> #include <private/dvr/clock_ns.h> #include <private/dvr/display_protocol.h> 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<std::mutex> 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<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++; } } pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) { ATRACE_NAME("VSyncService::HandleMessage"); switch (message.GetOp()) { case VSyncProtocol::Wait::Opcode: AddWaiter(message); return {}; case VSyncProtocol::GetLastTimestamp::Opcode: DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>( *this, &VSyncService::OnGetLastTimestamp, message); return {}; case VSyncProtocol::GetSchedInfo::Opcode: DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>( *this, &VSyncService::OnGetSchedInfo, message); return {}; case VSyncProtocol::Acknowledge::Opcode: DispatchRemoteMethod<VSyncProtocol::Acknowledge>( *this, &VSyncService::OnAcknowledge, message); return {}; default: return Service::HandleMessage(message); } } pdx::Status<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_}; } pdx::Status<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}}; } pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) { auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); std::lock_guard<std::mutex> autolock(mutex_); client->Ack(); return {}; } void VSyncWaiter::Notify(int64_t timestamp) { timestamp_ = timestamp; DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait, message_); } pdx::Status<int64_t> 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