OSDN Git Service

Implement vsync events
authorlambdadroid <lambdadroid@gmail.com>
Sun, 6 Jan 2019 18:24:33 +0000 (19:24 +0100)
committerlambdadroid <lambdadroid@gmail.com>
Sun, 6 Jan 2019 18:25:15 +0000 (19:25 +0100)
Android.mk
DrmComposer.cpp
DrmDevice.cpp
DrmDevice.h
DrmDisplay.cpp
DrmDisplay.h
DrmVsyncThread.cpp [new file with mode: 0644]
DrmVsyncThread.h [new file with mode: 0644]
GraphicsThread.cpp [new file with mode: 0644]
GraphicsThread.h [new file with mode: 0644]

index 179f4ff..9331e47 100644 (file)
@@ -17,7 +17,9 @@ LOCAL_SRC_FILES := \
     DrmDevice.cpp \
     DrmDisplay.cpp \
     DrmFramebuffer.cpp \
-    DrmFramebufferLibDrm.cpp
+    DrmFramebufferLibDrm.cpp \
+    GraphicsThread.cpp \
+    DrmVsyncThread.cpp
 
 LOCAL_HEADER_LIBRARIES := \
     android.hardware.graphics.composer@2.1-hal
index 57e5a70..255439b 100644 (file)
@@ -241,13 +241,21 @@ Error DrmComposerHal::setPowerMode(Display displayId, IComposerClient::PowerMode
     }
 }
 
-Error DrmComposerHal::setVsyncEnabled(Display displayId, IComposerClient::Vsync /*enabled*/) {
+Error DrmComposerHal::setVsyncEnabled(Display displayId, IComposerClient::Vsync enabled) {
     auto display = mDevice->getConnectedDisplay(displayId);
     if (!display)
         return Error::BAD_DISPLAY;
 
-    // TODO
-    return Error::NONE;
+    switch (enabled) {
+    case IComposerClient::Vsync::ENABLE:
+        display->enableVsync();
+        return Error::NONE;
+    case IComposerClient::Vsync::DISABLE:
+        display->disableVsync();
+        return Error::NONE;
+    default:
+        return Error::BAD_PARAMETER;
+    }
 }
 
 Error DrmComposerHal::setColorTransform(Display /*displayId*/,
index 3c8b2ab..689ad4e 100644 (file)
@@ -34,19 +34,18 @@ DrmDisplay* DrmDevice::getConnectedDisplay(uint32_t connector) {
     return display->connected() ? display : nullptr;
 }
 
-uint32_t DrmDevice::reserveCrtc(unsigned index) {
-    auto mask = 1 << index;
-    if (index < mCrtcs.size() && !(mUsedCrtcs & mask)) {
+uint32_t DrmDevice::reserveCrtc(unsigned pipe) {
+    auto mask = 1 << pipe;
+    if (pipe < mCrtcs.size() && !(mUsedCrtcs & mask)) {
         mUsedCrtcs |= mask;
-        return mCrtcs[index];
+        return mCrtcs[pipe];
     }
     return 0;
 }
 
-void DrmDevice::freeCrtc(uint32_t crtc) {
-    auto i = std::find(mCrtcs.begin(), mCrtcs.end(), crtc);
-    if (i != mCrtcs.end()) {
-        mUsedCrtcs &= ~(1 << std::distance(mCrtcs.begin(), i));
+void DrmDevice::freeCrtc(unsigned pipe) {
+    if (pipe < mCrtcs.size()) {
+        mUsedCrtcs &= ~(1 << pipe);
     }
 }
 
index 33800fc..62321ae 100644 (file)
@@ -27,8 +27,8 @@ struct DrmDevice {
     DrmDisplay* getConnectedDisplay(uint32_t connector);
 
     inline const std::vector<uint32_t>& crtcs() { return mCrtcs; }
-    uint32_t reserveCrtc(unsigned index);
-    void freeCrtc(uint32_t crtc);
+    uint32_t reserveCrtc(unsigned pipe);
+    void freeCrtc(unsigned pipe);
 
     bool initialize();
     void update();
index 8fce88f..f2c756c 100644 (file)
@@ -39,13 +39,13 @@ constexpr int32_t KINCH_MILLIMETER = 25400;
 }
 
 DrmDisplay::DrmDisplay(DrmDevice& device, uint32_t connectorId)
-    : mDevice(device), mConnector(connectorId) {
+    : mDevice(device), mConnector(connectorId), mVsyncThread(*this) {
     update();
 }
 
 DrmDisplay::~DrmDisplay() {
     if (mCrtc)
-        mDevice.freeCrtc(mCrtc);
+        mDevice.freeCrtc(mPipe);
 }
 
 int32_t DrmDisplay::width(unsigned mode) const {
@@ -101,11 +101,14 @@ void DrmDisplay::update() {
         LOG(INFO) << "Display " << *this << " connected, "
             << mModes.size() << " mode(s), "
             << "default: " << mModes[mCurrentMode];
+        report();
     } else {
         LOG(INFO) << "Display " << *this << " disconnected";
 
+        disableVsync();
+
         if (mCrtc)
-            mDevice.freeCrtc(mCrtc);
+            mDevice.freeCrtc(mPipe);
 
         mFlipPending = false;
         mModeSet = false;
@@ -113,9 +116,11 @@ void DrmDisplay::update() {
 
         mFramebuffers.clear();
         mModes.clear();
-    }
 
-    report();
+        report();
+
+        mVsyncThread.stop();
+    }
 }
 
 void DrmDisplay::setModes(const drmModeModeInfo* begin, const drmModeModeInfo* end) {
@@ -154,12 +159,17 @@ void DrmDisplay::setModes(const drmModeModeInfo* begin, const drmModeModeInfo* e
 }
 
 void DrmDisplay::report() {
-    auto callback = mDevice.callback();
-    if (callback) {
+    if (auto callback = mDevice.callback(); callback) {
         callback->onHotplug(*this, mConnected);
     }
 }
 
+void DrmDisplay::vsync(int64_t timestamp) {
+    if (auto callback = mDevice.callback(); callback) {
+        callback->onVsync(*this, timestamp);
+    }
+}
+
 bool DrmDisplay::setMode(unsigned mode) {
     if (mCurrentMode == mode)
         return true;
@@ -231,17 +241,18 @@ bool DrmDisplay::enable() {
             continue;
         }
 
-        for (unsigned j = 0; j < mDevice.crtcs().size(); ++j) {
-            if (!(encoder->possible_crtcs & (1 << j)))
+        for (mPipe = 0; mPipe < mDevice.crtcs().size(); ++mPipe) {
+            if (!(encoder->possible_crtcs & (1 << mPipe)))
                 continue;
 
-            mCrtc = mDevice.reserveCrtc(j);
+            mCrtc = mDevice.reserveCrtc(mPipe);
             if (mCrtc) {
                 LOG(INFO) << "Using CRTC " << mCrtc << " for display " << *this;
                 return true;
             } else {
-                LOG(WARNING) << "CRTC " << mDevice.crtcs()[j] << " for display "
-                    << *this << " is already in use by another display";
+                LOG(WARNING) << "CRTC " << mDevice.crtcs()[mPipe]
+                    << " for display " << *this
+                    << " is already in use by another display";
             }
         }
     }
@@ -257,16 +268,29 @@ void DrmDisplay::disable() {
     LOG(INFO) << "Disabling display " << *this;
 
     if (mModeSet) {
+        mVsyncThread.disable();
         awaitPageFlip();
         if (drmModeSetCrtc(mDevice.fd(), mCrtc, 0, 0, 0, nullptr, 0, nullptr)) {
             PLOG(ERROR) << "Failed to disable display " << *this;
         }
         mModeSet = false;
     }
-    mDevice.freeCrtc(mCrtc);
+    mDevice.freeCrtc(mPipe);
     mCrtc = 0;
 }
 
+void DrmDisplay::enableVsync() {
+    mVsyncEnabled = true;
+    if (mModeSet) {
+        mVsyncThread.enable();
+    }
+}
+
+void DrmDisplay::disableVsync() {
+    mVsyncEnabled = false;
+    mVsyncThread.disable();
+}
+
 void DrmDisplay::present(buffer_handle_t buffer) {
     if (!enabled())
         return;
@@ -292,6 +316,8 @@ void DrmDisplay::present(buffer_handle_t buffer) {
                 << " for display " << *this;
         } else {
             mModeSet = true;
+            if (mVsyncEnabled)
+                mVsyncThread.enable();
         }
     }
 }
index a758c0b..1b94964 100644 (file)
@@ -9,6 +9,7 @@
 #include <iostream>
 #include <xf86drmMode.h>
 #include "DrmFramebuffer.h"
+#include "DrmVsyncThread.h"
 
 namespace android {
 namespace hardware {
@@ -23,7 +24,9 @@ struct DrmDisplay {
     DrmDisplay(DrmDevice &device, uint32_t connectorId);
     ~DrmDisplay();
 
+    inline DrmDevice& device() const { return mDevice; }
     inline uint32_t id() const { return mConnector; }
+    inline unsigned pipe() const { return mPipe; }
     inline const std::string& name() const { return mName; }
     inline unsigned modeCount() const { return mModes.size(); }
     inline unsigned currentMode() const { return mCurrentMode; }
@@ -44,10 +47,14 @@ struct DrmDisplay {
 
     void update();
     void report();
+    void vsync(int64_t timestamp);
 
     bool enable();
     void disable();
 
+    void enableVsync();
+    void disableVsync();
+
     void present(buffer_handle_t buffer);
     void handlePageFlip();
 
@@ -69,13 +76,17 @@ private:
     unsigned mCurrentMode;
 
     uint32_t mCrtc = 0; // Selected when display is powered on
+    unsigned mPipe;
 
     bool mConnected = false;
     bool mModeSet = false;
     bool mFlipPending = false;
+    bool mVsyncEnabled = false;
 
     // TODO: Clean up framebuffers
     std::unordered_map<buffer_handle_t, DrmFramebuffer> mFramebuffers;
+
+    DrmVsyncThread mVsyncThread;
 };
 
 std::ostream& operator<<(std::ostream& os, const DrmDisplay& display);
diff --git a/DrmVsyncThread.cpp b/DrmVsyncThread.cpp
new file mode 100644 (file)
index 0000000..e2d63f0
--- /dev/null
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2019 Stephan Gerhold
+
+#define LOG_TAG "drmfb-vsync"
+
+#include <time.h>
+#include <android-base/logging.h>
+#include <xf86drm.h>
+#include "DrmVsyncThread.h"
+#include "DrmDevice.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace drmfb {
+
+namespace {
+constexpr int64_t NANO = 1'000'000'000;
+constexpr int64_t DEFAULT_PERIOD = NANO / 60; // 60 Hz
+}
+
+DrmVsyncThread::DrmVsyncThread(DrmDisplay& display)
+    : GraphicsThread("drm-vsync-" + std::to_string(display.id())),
+      mDisplay(display) {}
+
+void DrmVsyncThread::run() {
+    auto highCrtc = mDisplay.pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT;
+    drmVBlank vBlank{ .request = {
+        .type = static_cast<drmVBlankSeqType>(
+            DRM_VBLANK_RELATIVE | (highCrtc & DRM_VBLANK_HIGH_CRTC_MASK)),
+        .sequence = 1,
+    }};
+
+    auto ret = drmWaitVBlank(mDisplay.device().fd(), &vBlank);
+    if (ret) {
+        PLOG(ERROR) << "drmWaitBlank failed";
+        if (errno == EBUSY || waitFallback())
+            return;
+    } else {
+        mTimestamp = vBlank.reply.tval_sec * NANO + vBlank.reply.tval_usec * 1000;
+    }
+
+    mDisplay.vsync(mTimestamp);
+}
+
+int DrmVsyncThread::waitFallback() {
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+
+    int64_t period = mDisplay.vsyncPeriod(mDisplay.currentMode());
+    if (period <= 0)
+        period = DEFAULT_PERIOD;
+
+    int64_t now = ts.tv_sec * NANO + ts.tv_nsec;
+    mTimestamp = mTimestamp ? mTimestamp + period : now + period;
+    while (mTimestamp < now) {
+        mTimestamp += period;
+    }
+
+    ts.tv_sec = mTimestamp / NANO;
+    ts.tv_nsec = mTimestamp % NANO;
+
+    int ret;
+    do {
+        ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
+    } while (ret == EINTR);
+    return ret;
+}
+
+}  // namespace drmfb
+}  // namespace V2_1
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/DrmVsyncThread.h b/DrmVsyncThread.h
new file mode 100644 (file)
index 0000000..59b534e
--- /dev/null
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2019 Stephan Gerhold
+
+#pragma once
+
+#include "GraphicsThread.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace drmfb {
+
+struct DrmDisplay;
+
+struct DrmVsyncThread : public GraphicsThread {
+    DrmVsyncThread(DrmDisplay& display);
+    void run() override;
+
+private:
+    int waitFallback();
+
+    DrmDisplay& mDisplay;
+    int64_t mTimestamp = 0;
+};
+
+}  // namespace drmfb
+}  // namespace V2_1
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/GraphicsThread.cpp b/GraphicsThread.cpp
new file mode 100644 (file)
index 0000000..6b2696f
--- /dev/null
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2019 Stephan Gerhold
+
+#define LOG_TAG "drmfb-thread"
+
+#include <android-base/logging.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <system/graphics.h>
+#include "GraphicsThread.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace drmfb {
+
+GraphicsThread::GraphicsThread(std::string name)
+    : mName(std::move(name)) {}
+
+GraphicsThread::~GraphicsThread() {
+    stop();
+}
+
+void GraphicsThread::enable() {
+    {
+        std::scoped_lock lock{mMutex};
+        mEnabled = true;
+
+        if (!mStarted) {
+            mStarted = true;
+            mThread = std::thread(&GraphicsThread::loop, this);
+            return;
+        }
+    }
+
+    mCondition.notify_all();
+}
+
+void GraphicsThread::disable() {
+    std::scoped_lock lock{mMutex};
+    mEnabled = false;
+}
+
+void GraphicsThread::stop() {
+    {
+        std::scoped_lock lock{mMutex};
+        if (!mStarted)
+            return;
+
+        mEnabled = false;
+        mStarted = false;
+    }
+
+    mCondition.notify_all();
+    mThread.join();
+}
+
+void GraphicsThread::loop() {
+    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+    prctl(PR_SET_NAME, mName.c_str(), 0, 0, 0);
+
+    LOG(DEBUG) << "Starting thread " << mName;
+
+    std::unique_lock lock{mMutex};
+    while (true) {
+        if (!mEnabled) {
+            mCondition.wait(lock, [this] { return mEnabled || !mStarted; });
+            if (!mStarted) {
+                LOG(DEBUG) << "Stopping thread " << mName;
+                return;
+            }
+        }
+
+        lock.unlock();
+        run();
+        lock.lock();
+    }
+}
+
+}  // namespace drmfb
+}  // namespace V2_1
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
diff --git a/GraphicsThread.h b/GraphicsThread.h
new file mode 100644 (file)
index 0000000..a6e2e41
--- /dev/null
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2019 Stephan Gerhold
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_1 {
+namespace drmfb {
+
+struct GraphicsThread {
+    GraphicsThread(std::string name);
+    virtual ~GraphicsThread();
+
+    void enable();
+    void disable();
+    void stop();
+
+    virtual void run() = 0;
+    void loop();
+
+private:
+    std::mutex mMutex;
+    std::thread mThread;
+    std::string mName;
+
+    bool mStarted;
+    bool mEnabled;
+    std::condition_variable mCondition;
+};
+
+}  // namespace drmfb
+}  // namespace V2_1
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android