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 void VSyncWorker::SetProcs(hwc_procs_t const *procs) { 53 Lock(); 54 procs_ = procs; 55 Unlock(); 56 } 57 58 void VSyncWorker::VSyncControl(bool enabled) { 59 Lock(); 60 enabled_ = enabled; 61 last_timestamp_ = -1; 62 Unlock(); 63 64 Signal(); 65 } 66 67 /* 68 * Returns the timestamp of the next vsync in phase with last_timestamp_. 69 * For example: 70 * last_timestamp_ = 137 71 * frame_ns = 50 72 * current = 683 73 * 74 * ret = (50 * ((683 - 137)/50 + 1)) + 137 75 * ret = 687 76 * 77 * Thus, we must sleep until timestamp 687 to maintain phase with the last 78 * timestamp. 79 */ 80 int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) { 81 if (last_timestamp_ < 0) 82 return current + frame_ns; 83 84 return frame_ns * ((current - last_timestamp_) / frame_ns + 1) + 85 last_timestamp_; 86 } 87 88 static const int64_t kOneSecondNs = 1 * 1000 * 1000 * 1000; 89 90 int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) { 91 struct timespec vsync; 92 int ret = clock_gettime(CLOCK_MONOTONIC, &vsync); 93 94 float refresh = 60.0f; // Default to 60Hz refresh rate 95 DrmConnector *conn = drm_->GetConnectorForDisplay(display_); 96 if (conn && conn->active_mode().v_refresh() != 0.0f) 97 refresh = conn->active_mode().v_refresh(); 98 else 99 ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn, 100 conn ? conn->active_mode().v_refresh() : 0.0f); 101 102 int64_t phased_timestamp = GetPhasedVSync( 103 kOneSecondNs / refresh, vsync.tv_sec * kOneSecondNs + vsync.tv_nsec); 104 vsync.tv_sec = phased_timestamp / kOneSecondNs; 105 vsync.tv_nsec = phased_timestamp - (vsync.tv_sec * kOneSecondNs); 106 do { 107 ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, NULL); 108 } while (ret == -1 && errno == EINTR); 109 if (ret) 110 return ret; 111 112 *timestamp = (int64_t)vsync.tv_sec * kOneSecondNs + (int64_t)vsync.tv_nsec; 113 return 0; 114 } 115 116 void VSyncWorker::Routine() { 117 int ret; 118 119 Lock(); 120 if (!enabled_) { 121 ret = WaitForSignalOrExitLocked(); 122 if (ret == -EINTR) { 123 return; 124 } 125 } 126 127 bool enabled = enabled_; 128 int display = display_; 129 hwc_procs_t const *procs = procs_; 130 Unlock(); 131 132 if (!enabled) 133 return; 134 135 DrmCrtc *crtc = drm_->GetCrtcForDisplay(display); 136 if (!crtc) { 137 ALOGE("Failed to get crtc for display"); 138 return; 139 } 140 uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); 141 142 drmVBlank vblank; 143 memset(&vblank, 0, sizeof(vblank)); 144 vblank.request.type = (drmVBlankSeqType)( 145 DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); 146 vblank.request.sequence = 1; 147 148 int64_t timestamp; 149 ret = drmWaitVBlank(drm_->fd(), &vblank); 150 if (ret == -EINTR) { 151 return; 152 } else if (ret) { 153 ret = SyntheticWaitVBlank(×tamp); 154 if (ret) 155 return; 156 } else { 157 timestamp = (int64_t)vblank.reply.tval_sec * kOneSecondNs + 158 (int64_t)vblank.reply.tval_usec * 1000; 159 } 160 161 /* 162 * There's a race here where a change in procs_ will not take effect until 163 * the next subsequent requested vsync. This is unavoidable since we can't 164 * call the vsync hook while holding the thread lock. 165 * 166 * We could shorten the race window by caching procs_ right before calling 167 * the hook. However, in practice, procs_ is only updated once, so it's not 168 * worth the overhead. 169 */ 170 if (procs && procs->vsync) 171 procs->vsync(procs, display, timestamp); 172 last_timestamp_ = timestamp; 173 } 174 } 175