15using namespace std::chrono_literals;
23static constexpr auto s_pageflipTimeout = 5s;
30 m_thread.reset(QThread::create([
this]() {
31 const auto thread = QThread::currentThread();
34 if (thread->isInterruptionRequested()) {
37 std::unique_lock lock(m_mutex);
40 timeout = m_commitPending.wait_for(lock, s_pageflipTimeout) == std::cv_status::timeout;
41 }
else if (m_commits.empty()) {
42 m_commitPending.wait(lock);
47 qCCritical(KWIN_DRM,
"Pageflip timed out! This is a kernel bug");
48 std::unique_ptr<DrmAtomicCommit> committed(
static_cast<DrmAtomicCommit *
>(m_committed.release()));
49 const bool cursorOnly = committed->isCursorOnly();
50 m_droppedCommits.push_back(std::move(committed));
57 if (!m_commits.empty()) {
58 const auto now = std::chrono::steady_clock::now();
59 if (m_targetPageflipTime > now + m_safetyMargin) {
61 std::this_thread::sleep_until(m_targetPageflipTime - m_safetyMargin);
65 auto &commit = m_commits.front();
66 if (!commit->areBuffersReadable()) {
69 m_targetPageflipTime += 50us;
71 m_targetPageflipTime += m_minVblankInterval;
75 const auto vrr = commit->isVrr();
76 const bool success = commit->commit();
78 m_vrr = vrr.value_or(m_vrr);
79 m_committed = std::move(commit);
80 m_commits.erase(m_commits.begin());
82 if (m_commits.size() > 1) {
85 while (m_commits.size() > 1) {
86 auto toMerge = std::move(m_commits[1]);
87 m_commits.erase(m_commits.begin() + 1);
88 m_commits.front()->merge(toMerge.get());
89 m_droppedCommits.push_back(std::move(toMerge));
96 const bool cursorOnly = std::all_of(m_commits.begin(), m_commits.end(), [](
const auto &commit) {
97 return commit->isCursorOnly();
99 for (
auto &commit : m_commits) {
100 m_droppedCommits.push_back(std::move(commit));
103 qCWarning(KWIN_DRM) <<
"atomic commit failed:" << strerror(errno);
108 QMetaObject::invokeMethod(
this, &DrmCommitThread::clearDroppedCommits, Qt::ConnectionType::QueuedConnection);
112 m_thread->setObjectName(name);
116void DrmCommitThread::optimizeCommits()
118 if (m_commits.size() <= 1) {
122 if (m_commits.front()->areBuffersReadable()) {
123 auto it = m_commits.begin() + 1;
124 while (it != m_commits.end() && (*it)->areBuffersReadable()) {
125 m_commits.front()->merge(it->get());
126 m_droppedCommits.push_back(std::move(*it));
127 it = m_commits.erase(it);
131 for (
auto it = m_commits.begin(); it != m_commits.end();) {
132 DrmAtomicCommit *
const commit = it->get();
134 while (it != m_commits.end() && commit->modifiedPlanes() == (*it)->modifiedPlanes() && (*it)->areBuffersReadable()) {
135 commit->merge(it->get());
136 m_droppedCommits.push_back(std::move(*it));
137 it = m_commits.erase(it);
140 if (m_commits.size() == 1) {
144 std::unique_ptr<DrmAtomicCommit> front;
145 if (m_commits.front()->areBuffersReadable()) {
146 front = std::move(m_commits.front());
147 m_commits.erase(m_commits.begin());
150 for (
auto it = m_commits.begin() + 1; it != m_commits.end();) {
153 const auto &planes = commit->modifiedPlanes();
154 const bool skipping = std::any_of(m_commits.begin(), it, [&planes](
const auto &other) {
155 return std::any_of(planes.begin(), planes.end(), [&other](DrmPlane *plane) {
156 return other->modifiedPlanes().contains(plane);
159 if (skipping || !commit->areBuffersReadable()) {
164 std::unique_ptr<DrmAtomicCommit> duplicate;
166 duplicate = std::make_unique<DrmAtomicCommit>(*front);
167 duplicate->merge(commit.get());
168 if (!duplicate->test()) {
169 m_droppedCommits.push_back(std::move(duplicate));
174 if (!commit->test()) {
178 duplicate = std::make_unique<DrmAtomicCommit>(*commit);
181 for (
const auto &otherCommit : m_commits) {
182 if (otherCommit != commit) {
183 duplicate->merge(otherCommit.get());
184 if (!duplicate->test()) {
190 m_droppedCommits.push_back(std::move(duplicate));
193 front->merge(commit.get());
194 m_droppedCommits.push_back(std::move(commit));
196 front = std::move(commit);
198 it = m_commits.erase(it);
204 m_commits.insert(m_commits.begin(), std::move(front));
208DrmCommitThread::~DrmCommitThread()
211 m_thread->requestInterruption();
212 m_commitPending.notify_all();
217void DrmCommitThread::addCommit(std::unique_ptr<DrmAtomicCommit> &&commit)
219 std::unique_lock lock(m_mutex);
220 m_commits.push_back(std::move(commit));
221 const auto now = std::chrono::steady_clock::now();
222 if (m_vrr && now >= m_lastPageflip + m_minVblankInterval) {
223 m_targetPageflipTime = now;
225 m_targetPageflipTime = estimateNextVblank(now);
227 m_commits.back()->setDeadline(m_targetPageflipTime - m_safetyMargin);
228 m_commitPending.notify_all();
231void DrmCommitThread::setPendingCommit(std::unique_ptr<DrmLegacyCommit> &&commit)
233 m_committed = std::move(commit);
236void DrmCommitThread::clearDroppedCommits()
238 std::unique_lock lock(m_mutex);
239 m_droppedCommits.clear();
242void DrmCommitThread::setModeInfo(uint32_t maximum, std::chrono::nanoseconds vblankTime)
244 std::unique_lock lock(m_mutex);
245 m_minVblankInterval = std::chrono::nanoseconds(1'000'000'000'000ull / maximum);
248 m_safetyMargin = vblankTime + 1500us;
251void DrmCommitThread::pageFlipped(std::chrono::nanoseconds timestamp)
253 std::unique_lock lock(m_mutex);
256 if (!m_commits.empty()) {
257 m_targetPageflipTime = estimateNextVblank(std::chrono::steady_clock::now());
258 m_commitPending.notify_all();
262bool DrmCommitThread::pageflipsPending()
264 std::unique_lock lock(m_mutex);
265 return !m_commits.empty() || m_committed;
271 const uint64_t pageflipsSince = now >= m_lastPageflip ? (now - m_lastPageflip) / m_minVblankInterval : 0;
272 return m_lastPageflip + m_minVblankInterval * (pageflipsSince + 1);
275std::chrono::nanoseconds DrmCommitThread::safetyMargin()
const
277 return m_safetyMargin;
DrmCommitThread(DrmGpu *gpu, const QString &name)
bool atomicModeSetting() const
std::chrono::steady_clock::time_point TimePoint