DrmDevice.cpp \
DrmDisplay.cpp \
DrmFramebuffer.cpp \
- DrmFramebufferLibDrm.cpp
+ DrmFramebufferLibDrm.cpp \
+ GraphicsThread.cpp \
+ DrmVsyncThread.cpp
LOCAL_HEADER_LIBRARIES := \
android.hardware.graphics.composer@2.1-hal
}
}
-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*/,
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);
}
}
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();
}
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 {
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;
mFramebuffers.clear();
mModes.clear();
- }
- report();
+ report();
+
+ mVsyncThread.stop();
+ }
}
void DrmDisplay::setModes(const drmModeModeInfo* begin, const drmModeModeInfo* end) {
}
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;
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";
}
}
}
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;
<< " for display " << *this;
} else {
mModeSet = true;
+ if (mVsyncEnabled)
+ mVsyncThread.enable();
}
}
}
#include <iostream>
#include <xf86drmMode.h>
#include "DrmFramebuffer.h"
+#include "DrmVsyncThread.h"
namespace android {
namespace hardware {
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; }
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();
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);
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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