1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "hwc-vsync-worker" 18 19 #include "drmresources.h" 20 #include "vsyncworker.h" 21 #include "worker.h" 22 23 #include <map> 24 #include <stdlib.h> 25 #include <time.h> 26 #include <xf86drm.h> 27 #include <xf86drmMode.h> 28 29 #include <cutils/log.h> 30 #include <hardware/hardware.h> 31 32 namespace android { 33 34 VSyncWorker::VSyncWorker() 35 : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY), 36 drm_(NULL), 37 procs_(NULL), 38 display_(-1), 39 last_timestamp_(-1) { 40 } 41 42 VSyncWorker::~VSyncWorker() { 43 } 44 45 int VSyncWorker::Init(DrmResources *drm, int display) { 46 drm_ = drm; 47 display_ = display; 48 49 return InitWorker(); 50 } 51 52 int VSyncWorker::SetProcs(hwc_procs_t const *procs) { 53 int ret = Lock(); 54 if (ret) { 55 ALOGE("Failed to lock vsync worker lock %d\n", ret); 56 return ret; 57 } 58 59 procs_ = procs; 60 61 ret = Unlock(); 62 if (ret) { 63 ALOGE("Failed to unlock vsync worker lock %d\n", ret); 64 return ret; 65 } 66 return 0; 67 } 68 69 int VSyncWorker::VSyncControl(bool enabled) { 70 int ret = Lock(); 71 if (ret) { 72 ALOGE("Failed to lock vsync worker lock %d\n", ret); 73 return ret; 74 } 75 76 enabled_ = enabled; 77 last_timestamp_ = -1; 78 int signal_ret = SignalLocked(); 79 80 ret = Unlock(); 81 if (ret) { 82 ALOGE("Failed to unlock vsync worker lock %d\n", ret); 83 return ret; 84 } 85 86 return signal_ret; 87 } 88 89 /* 90 * Returns the timestamp of the next vsync in phase with last_timestamp_. 91 * For example: 92 * last_timestamp_ = 137 93 * frame_ns = 50 94 * current = 683 95 * 96 * ret = (50 * ((683 - 137)/50 + 1)) + 137 97 * ret = 687 98 * 99 * Thus, we must sleep until timestamp 687 to maintain phase with the last 100 * timestamp. 101 */ 102 int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) { 103 if (last_timestamp_ < 0) 104 return current + frame_ns; 105 106 return frame_ns * ((current - last_timestamp_) / frame_ns + 1) + 107 last_timestamp_; 108 } 109 110 static const int64_t kOneSecondNs = 1 * 1000 * 1000 * 1000; 111 112 int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) { 113 struct timespec vsync; 114 int ret = clock_gettime(CLOCK_MONOTONIC, &vsync); 115 116 float refresh = 60.0f; // Default to 60Hz refresh rate 117 DrmConnector *conn = drm_->GetConnectorForDisplay(display_); 118 if (conn && conn->active_mode().v_refresh() != 0.0f) 119 refresh = conn->active_mode().v_refresh(); 120 else 121 ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn, 122 conn ? conn->active_mode().v_refresh() : 0.0f); 123 124 int64_t phased_timestamp = GetPhasedVSync( 125 kOneSecondNs / refresh, vsync.tv_sec * kOneSecondNs + vsync.tv_nsec); 126 vsync.tv_sec = phased_timestamp / kOneSecondNs; 127 vsync.tv_nsec = phased_timestamp - (vsync.tv_sec * kOneSecondNs); 128 do { 129 ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, NULL); 130 } while (ret == -1 && errno == EINTR); 131 if (ret) 132 return ret; 133 134 *timestamp = (int64_t)vsync.tv_sec * kOneSecondNs + (int64_t)vsync.tv_nsec; 135 return 0; 136 } 137 138 void VSyncWorker::Routine() { 139 int ret = Lock(); 140 if (ret) { 141 ALOGE("Failed to lock worker %d", ret); 142 return; 143 } 144 145 if (!enabled_) { 146 ret = WaitForSignalOrExitLocked(); 147 if (ret == -EINTR) { 148 return; 149 } 150 } 151 152 bool enabled = enabled_; 153 int display = display_; 154 hwc_procs_t const *procs = procs_; 155 156 ret = Unlock(); 157 if (ret) { 158 ALOGE("Failed to unlock worker %d", ret); 159 } 160 161 if (!enabled) 162 return; 163 164 DrmCrtc *crtc = drm_->GetCrtcForDisplay(display); 165 if (!crtc) { 166 ALOGE("Failed to get crtc for display"); 167 return; 168 } 169 uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); 170 171 drmVBlank vblank; 172 memset(&vblank, 0, sizeof(vblank)); 173 vblank.request.type = (drmVBlankSeqType)( 174 DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); 175 vblank.request.sequence = 1; 176 177 int64_t timestamp; 178 ret = drmWaitVBlank(drm_->fd(), &vblank); 179 if (ret == -EINTR) { 180 return; 181 } else if (ret) { 182 ret = SyntheticWaitVBlank(×tamp); 183 if (ret) 184 return; 185 } else { 186 timestamp = (int64_t)vblank.reply.tval_sec * kOneSecondNs + 187 (int64_t)vblank.reply.tval_usec * 1000; 188 } 189 190 /* 191 * There's a race here where a change in procs_ will not take effect until 192 * the next subsequent requested vsync. This is unavoidable since we can't 193 * call the vsync hook while holding the thread lock. 194 * 195 * We could shorten the race window by caching procs_ right before calling 196 * the hook. However, in practice, procs_ is only updated once, so it's not 197 * worth the overhead. 198 */ 199 if (procs && procs->vsync) 200 procs->vsync(procs, display, timestamp); 201 last_timestamp_ = timestamp; 202 } 203 } 204