From a2721d5caa59079ea797bf6f6f4d3be4d2fd7387 Mon Sep 17 00:00:00 2001 From: lambdadroid Date: Sun, 6 Jan 2019 19:24:33 +0100 Subject: [PATCH] Implement vsync events --- Android.mk | 4 ++- DrmComposer.cpp | 14 +++++++-- DrmDevice.cpp | 15 +++++----- DrmDevice.h | 4 +-- DrmDisplay.cpp | 52 ++++++++++++++++++++++++-------- DrmDisplay.h | 11 +++++++ DrmVsyncThread.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++ DrmVsyncThread.h | 33 +++++++++++++++++++++ GraphicsThread.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ GraphicsThread.h | 43 +++++++++++++++++++++++++++ 10 files changed, 313 insertions(+), 27 deletions(-) create mode 100644 DrmVsyncThread.cpp create mode 100644 DrmVsyncThread.h create mode 100644 GraphicsThread.cpp create mode 100644 GraphicsThread.h diff --git a/Android.mk b/Android.mk index 179f4ff..9331e47 100644 --- a/Android.mk +++ b/Android.mk @@ -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 diff --git a/DrmComposer.cpp b/DrmComposer.cpp index 57e5a70..255439b 100644 --- a/DrmComposer.cpp +++ b/DrmComposer.cpp @@ -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*/, diff --git a/DrmDevice.cpp b/DrmDevice.cpp index 3c8b2ab..689ad4e 100644 --- a/DrmDevice.cpp +++ b/DrmDevice.cpp @@ -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); } } diff --git a/DrmDevice.h b/DrmDevice.h index 33800fc..62321ae 100644 --- a/DrmDevice.h +++ b/DrmDevice.h @@ -27,8 +27,8 @@ struct DrmDevice { DrmDisplay* getConnectedDisplay(uint32_t connector); inline const std::vector& 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(); diff --git a/DrmDisplay.cpp b/DrmDisplay.cpp index 8fce88f..f2c756c 100644 --- a/DrmDisplay.cpp +++ b/DrmDisplay.cpp @@ -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(); } } } diff --git a/DrmDisplay.h b/DrmDisplay.h index a758c0b..1b94964 100644 --- a/DrmDisplay.h +++ b/DrmDisplay.h @@ -9,6 +9,7 @@ #include #include #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 mFramebuffers; + + DrmVsyncThread mVsyncThread; }; std::ostream& operator<<(std::ostream& os, const DrmDisplay& display); diff --git a/DrmVsyncThread.cpp b/DrmVsyncThread.cpp new file mode 100644 index 0000000..e2d63f0 --- /dev/null +++ b/DrmVsyncThread.cpp @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2019 Stephan Gerhold + +#define LOG_TAG "drmfb-vsync" + +#include +#include +#include +#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( + 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 index 0000000..59b534e --- /dev/null +++ b/DrmVsyncThread.h @@ -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 index 0000000..6b2696f --- /dev/null +++ b/GraphicsThread.cpp @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2019 Stephan Gerhold + +#define LOG_TAG "drmfb-thread" + +#include +#include +#include +#include +#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 index 0000000..a6e2e41 --- /dev/null +++ b/GraphicsThread.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2019 Stephan Gerhold + +#pragma once + +#include +#include +#include + +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 -- 2.11.0