Changeset View
Changeset View
Standalone View
Standalone View
composite.cpp
Show All 19 Lines | |||||
20 | #include "composite.h" | 20 | #include "composite.h" | ||
21 | 21 | | |||
22 | #include "dbusinterface.h" | 22 | #include "dbusinterface.h" | ||
23 | #include "client.h" | 23 | #include "client.h" | ||
24 | #include "decorations/decoratedclient.h" | 24 | #include "decorations/decoratedclient.h" | ||
25 | #include "deleted.h" | 25 | #include "deleted.h" | ||
26 | #include "effects.h" | 26 | #include "effects.h" | ||
27 | #include "overlaywindow.h" | 27 | #include "overlaywindow.h" | ||
28 | #include "perf.h" | ||||
28 | #include "platform.h" | 29 | #include "platform.h" | ||
29 | #include "scene.h" | 30 | #include "scene.h" | ||
30 | #include "screens.h" | 31 | #include "screens.h" | ||
31 | #include "shadow.h" | 32 | #include "shadow.h" | ||
32 | #include "shell_client.h" | 33 | #include "shell_client.h" | ||
33 | #include "unmanaged.h" | 34 | #include "unmanaged.h" | ||
34 | #include "useractions.h" | 35 | #include "useractions.h" | ||
35 | #include "utils.h" | 36 | #include "utils.h" | ||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Line(s) | |||||
117 | 118 | | |||
118 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | 119 | static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } | ||
119 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | 120 | static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } | ||
120 | 121 | | |||
121 | Compositor::Compositor(QObject* workspace) | 122 | Compositor::Compositor(QObject* workspace) | ||
122 | : QObject(workspace) | 123 | : QObject(workspace) | ||
123 | , m_state(State::Off) | 124 | , m_state(State::Off) | ||
124 | , m_selectionOwner(NULL) | 125 | , m_selectionOwner(NULL) | ||
125 | , vBlankInterval(0) | | |||
126 | , fpsInterval(0) | | |||
127 | , m_timeSinceLastVBlank(0) | | |||
128 | , m_scene(NULL) | 126 | , m_scene(NULL) | ||
129 | , m_bufferSwapPending(false) | 127 | , m_bufferSwapPending(false) | ||
130 | , m_composeAtSwapCompletion(false) | 128 | , m_composeAtSwapCompletion(false) | ||
131 | { | 129 | { | ||
132 | connect(options, &Options::configChanged, this, &Compositor::configChanged); | 130 | connect(options, &Options::configChanged, this, &Compositor::configChanged); | ||
133 | 131 | | |||
134 | m_monotonicClock.start(); | 132 | m_monotonicClock.start(); | ||
135 | 133 | | |||
▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Line(s) | |||||
323 | 321 | | |||
324 | void Compositor::startupWithWorkspace() | 322 | void Compositor::startupWithWorkspace() | ||
325 | { | 323 | { | ||
326 | connect(kwinApp(), &Application::x11ConnectionChanged, | 324 | connect(kwinApp(), &Application::x11ConnectionChanged, | ||
327 | this, &Compositor::setupX11Support, Qt::UniqueConnection); | 325 | this, &Compositor::setupX11Support, Qt::UniqueConnection); | ||
328 | Workspace::self()->markXStackingOrderAsDirty(); | 326 | Workspace::self()->markXStackingOrderAsDirty(); | ||
329 | Q_ASSERT(m_scene); | 327 | Q_ASSERT(m_scene); | ||
330 | 328 | | |||
331 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | 329 | connect(workspace(), &Workspace::destroyed, this, [this] { m_fallbackTimer.stop(); }); | ||
332 | setupX11Support(); | 330 | setupX11Support(); | ||
333 | fpsInterval = options->maxFpsInterval(); | | |||
334 | | ||||
335 | if (m_scene->syncsToVBlank()) { | | |||
336 | // If we do vsync, set the fps to the next multiple of the vblank rate. | | |||
337 | vBlankInterval = milliToNano(1000) / currentRefreshRate(); | | |||
338 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | | |||
339 | } else { | | |||
340 | // No vsync - DO NOT set "0", would cause div-by-zero segfaults. | | |||
341 | vBlankInterval = milliToNano(1); | | |||
342 | } | | |||
343 | | ||||
344 | // This means "start now" - we don't have even a slight idea when the first vsync will occur. | | |||
345 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); | | |||
346 | scheduleRepaint(); | | |||
347 | 331 | | |||
348 | // Sets also the 'effects' pointer. | 332 | // Sets also the 'effects' pointer. | ||
349 | kwinApp()->platform()->createEffectsHandler(this, m_scene); | 333 | kwinApp()->platform()->createEffectsHandler(this, m_scene); | ||
350 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | 334 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | ||
351 | connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); | 335 | connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); | ||
352 | addRepaintFull(); | 336 | addRepaintFull(); | ||
353 | 337 | | |||
354 | for (Client *c : Workspace::self()->clientList()) { | 338 | for (Client *c : Workspace::self()->clientList()) { | ||
Show All 29 Lines | |||||
384 | } | 368 | } | ||
385 | 369 | | |||
386 | // Render at least once. | 370 | // Render at least once. | ||
387 | performCompositing(); | 371 | performCompositing(); | ||
388 | } | 372 | } | ||
389 | 373 | | |||
390 | void Compositor::scheduleRepaint() | 374 | void Compositor::scheduleRepaint() | ||
391 | { | 375 | { | ||
392 | if (!compositeTimer.isActive()) | 376 | if (m_state != State::On) { | ||
393 | setCompositeTimer(); | 377 | return; | ||
378 | } | ||||
379 | if (!kwinApp()->platform()->areOutputsEnabled()) { | ||||
380 | return; | ||||
381 | } | ||||
382 | | ||||
383 | // TODO: Make this distinction not on the question if there is a swap event but if per screen | ||||
384 | // rendering? On X we get swap events but they are aligned with the "wrong" screen if | ||||
385 | // it the primary/first one is not the one with the highest refresh rate. | ||||
386 | // But on the other side Present extension does not allow to sync with another screen | ||||
387 | // anyway. | ||||
388 | | ||||
389 | if (m_scene->hasSwapEvent()) { | ||||
390 | // TODO: If we don't call it back from the event loop we often crash on Wayland | ||||
391 | // in AnimationEffect::postPaintScreen. Why? | ||||
392 | QTimer::singleShot(0, this, [this]() { performCompositing(); }); | ||||
393 | } else { | ||||
394 | setFallbackTimer(); | ||||
395 | } | ||||
394 | } | 396 | } | ||
395 | 397 | | |||
396 | void Compositor::stop() | 398 | void Compositor::stop() | ||
397 | { | 399 | { | ||
398 | if (m_state == State::Off || m_state == State::Stopping) { | 400 | if (m_state == State::Off || m_state == State::Stopping) { | ||
399 | return; | 401 | return; | ||
400 | } | 402 | } | ||
401 | m_state = State::Stopping; | 403 | m_state = State::Stopping; | ||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | 442 | if (waylandServer()) { | |||
449 | } | 451 | } | ||
450 | for (ShellClient *c : waylandServer()->internalClients()) { | 452 | for (ShellClient *c : waylandServer()->internalClients()) { | ||
451 | c->finishCompositing(); | 453 | c->finishCompositing(); | ||
452 | } | 454 | } | ||
453 | } | 455 | } | ||
454 | 456 | | |||
455 | delete m_scene; | 457 | delete m_scene; | ||
456 | m_scene = NULL; | 458 | m_scene = NULL; | ||
457 | compositeTimer.stop(); | 459 | m_fallbackTimer.stop(); | ||
458 | repaints_region = QRegion(); | 460 | repaints_region = QRegion(); | ||
459 | 461 | | |||
460 | m_state = State::Off; | 462 | m_state = State::Off; | ||
461 | emit compositingToggled(false); | 463 | emit compositingToggled(false); | ||
462 | } | 464 | } | ||
463 | 465 | | |||
464 | void Compositor::destroyCompositorSelection() | 466 | void Compositor::destroyCompositorSelection() | ||
465 | { | 467 | { | ||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Line(s) | 570 | if (!hasScene()) | |||
569 | return; | 571 | return; | ||
570 | const QSize &s = screens()->size(); | 572 | const QSize &s = screens()->size(); | ||
571 | repaints_region = QRegion(0, 0, s.width(), s.height()); | 573 | repaints_region = QRegion(0, 0, s.width(), s.height()); | ||
572 | scheduleRepaint(); | 574 | scheduleRepaint(); | ||
573 | } | 575 | } | ||
574 | 576 | | |||
575 | void Compositor::timerEvent(QTimerEvent *te) | 577 | void Compositor::timerEvent(QTimerEvent *te) | ||
576 | { | 578 | { | ||
577 | if (te->timerId() == compositeTimer.timerId()) { | 579 | if (te->timerId() == m_delayTimer.timerId()){ | ||
580 | m_delayTimer.stop(); | ||||
581 | performCompositing(true); | ||||
582 | } else if (te->timerId() == m_fallbackTimer.timerId()) { | ||||
578 | performCompositing(); | 583 | performCompositing(); | ||
579 | } else | 584 | } else { | ||
580 | QObject::timerEvent(te); | 585 | QObject::timerEvent(te); | ||
581 | } | 586 | } | ||
587 | } | ||||
582 | 588 | | |||
583 | void Compositor::aboutToSwapBuffers() | 589 | void Compositor::aboutToSwapBuffers() | ||
584 | { | 590 | { | ||
585 | assert(!m_bufferSwapPending); | 591 | assert(!m_bufferSwapPending); | ||
586 | 592 | | |||
587 | m_bufferSwapPending = true; | 593 | m_bufferSwapPending = true; | ||
588 | } | 594 | } | ||
589 | 595 | | |||
590 | void Compositor::bufferSwapComplete() | 596 | void Compositor::bufferSwapComplete() | ||
591 | { | 597 | { | ||
592 | assert(m_bufferSwapPending); | 598 | assert(m_bufferSwapPending); | ||
593 | m_bufferSwapPending = false; | 599 | m_bufferSwapPending = false; | ||
594 | 600 | | |||
595 | emit bufferSwapCompleted(); | 601 | emit bufferSwapCompleted(); | ||
602 | Perf::ftrace(QStringLiteral("Swaped")); | ||||
596 | 603 | | |||
597 | if (m_composeAtSwapCompletion) { | 604 | if (m_composeAtSwapCompletion) { | ||
598 | m_composeAtSwapCompletion = false; | 605 | m_composeAtSwapCompletion = false; | ||
599 | performCompositing(); | 606 | performCompositing(); | ||
600 | } | 607 | } | ||
601 | } | 608 | } | ||
602 | 609 | | |||
603 | void Compositor::performCompositing() | 610 | void Compositor::updatePaintTimes(qint64 time) | ||
611 | { | ||||
612 | // const size_t prevIndex = m_paintTimesIndex; | ||||
613 | const size_t nextIndex = (m_paintTimesIndex + 1) % 4; | ||||
614 | | ||||
615 | m_paintTimes[nextIndex] = time; | ||||
616 | m_paintTimesIndex = nextIndex; | ||||
617 | } | ||||
618 | | ||||
619 | bool Compositor::delayPerform() | ||||
620 | { | ||||
621 | // Here we calculate in nano seconds. | ||||
622 | // const quint64 frameDuration = milliToNano(1000) / (double)currentRefreshRate(); | ||||
623 | const quint64 frameDuration = milliToNano(1000) / refreshRate(); | ||||
624 | | ||||
625 | quint64 avgPaintTime = 0; | ||||
626 | for (const auto time : m_paintTimes) { | ||||
627 | avgPaintTime += time; | ||||
628 | } | ||||
629 | avgPaintTime = avgPaintTime / 4; | ||||
630 | | ||||
631 | // We give at least 1 millisecond time to paint. | ||||
632 | const qint64 minGap = 1; | ||||
633 | const qint64 delay = nanoToMilli(frameDuration - avgPaintTime) - minGap; | ||||
634 | | ||||
635 | if (delay <= 0) { | ||||
636 | return false; | ||||
637 | } | ||||
638 | | ||||
639 | m_delayTimer.start(delay, this); | ||||
640 | return true; | ||||
641 | } | ||||
642 | | ||||
643 | static ulong s_msc = 0; | ||||
644 | | ||||
645 | void Compositor::performCompositing(bool force) | ||||
604 | { | 646 | { | ||
605 | // If a buffer swap is still pending, we return to the event loop and | 647 | // If a buffer swap is still pending, we return to the event loop and | ||
606 | // continue processing events until the swap has completed. | 648 | // continue processing events until the swap has completed. | ||
607 | if (m_bufferSwapPending) { | 649 | if (m_bufferSwapPending) { | ||
608 | m_composeAtSwapCompletion = true; | 650 | m_composeAtSwapCompletion = true; | ||
609 | compositeTimer.stop(); | | |||
610 | return; | 651 | return; | ||
611 | } | 652 | } | ||
612 | 653 | | |||
613 | // If outputs are disabled, we return to the event loop and | 654 | // If outputs are disabled, we return to the event loop and | ||
614 | // continue processing events until the outputs are enabled again | 655 | // continue processing events until the outputs are enabled again | ||
615 | if (!kwinApp()->platform()->areOutputsEnabled()) { | 656 | if (!kwinApp()->platform()->areOutputsEnabled()) { | ||
616 | compositeTimer.stop(); | 657 | m_fallbackTimer.stop(); | ||
658 | return; | ||||
659 | } | ||||
660 | | ||||
661 | if (m_delayTimer.isActive()) { | ||||
617 | return; | 662 | return; | ||
618 | } | 663 | } | ||
619 | 664 | | |||
665 | if (!force && m_scene->hasSwapEvent()) { | ||||
666 | // TODO: instead only check if vsynced? Currently not working without swap event | ||||
667 | if (delayPerform()) { | ||||
668 | return; | ||||
669 | } | ||||
670 | } | ||||
671 | | ||||
620 | // Create a list of all windows in the stacking order | 672 | // Create a list of all windows in the stacking order | ||
621 | ToplevelList windows = Workspace::self()->xStackingOrder(); | 673 | ToplevelList windows = Workspace::self()->xStackingOrder(); | ||
622 | ToplevelList damaged; | 674 | ToplevelList damaged; | ||
623 | 675 | | |||
624 | // Reset the damage state of each window and fetch the damage region | 676 | // Reset the damage state of each window and fetch the damage region | ||
625 | // without waiting for a reply | 677 | // without waiting for a reply | ||
626 | for (Toplevel *win : windows) { | 678 | for (Toplevel *win : windows) { | ||
627 | if (win->resetAndFetchDamage()) { | 679 | if (win->resetAndFetchDamage()) { | ||
Show All 26 Lines | 701 | if (win->effectWindow()) { | |||
654 | } | 706 | } | ||
655 | } | 707 | } | ||
656 | 708 | | |||
657 | win->getDamageRegionReply(); | 709 | win->getDamageRegionReply(); | ||
658 | } | 710 | } | ||
659 | 711 | | |||
660 | if (repaints_region.isEmpty() && !windowRepaintsPending()) { | 712 | if (repaints_region.isEmpty() && !windowRepaintsPending()) { | ||
661 | m_scene->idle(); | 713 | m_scene->idle(); | ||
662 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" | 714 | | ||
663 | // Note: It would seem here we should undo suspended unredirect, but when scenes need | 715 | // TODO: Should we start directly on next damage or can we just ignore the | ||
664 | // it for some reason, e.g. transformations or translucency, the next pass that does not | 716 | // additional latency of one frame and run the timer once more? | ||
665 | // need this anymore and paints normally will also reset the suspended unredirect. | 717 | // m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" | ||
666 | // Otherwise the window would not be painted normally anyway. | 718 | | ||
667 | compositeTimer.stop(); | 719 | m_fallbackTimer.stop(); | ||
668 | return; | 720 | return; | ||
669 | } | 721 | } | ||
670 | 722 | | |||
723 | Perf::ftrace(s_msc, QStringLiteral("Paint")); | ||||
724 | | ||||
671 | // Skip windows that are not yet ready for being painted and if screen is locked skip windows | 725 | // Skip windows that are not yet ready for being painted and if screen is locked skip windows | ||
672 | // that are neither lockscreen nor inputmethod windows. | 726 | // that are neither lockscreen nor inputmethod windows. | ||
673 | // | 727 | // | ||
674 | // TODO? This cannot be used so carelessly - needs protections against broken clients, the | 728 | // TODO? This cannot be used so carelessly - needs protections against broken clients, the | ||
675 | // window should not get focus before it's displayed, handle unredirected windows properly and | 729 | // window should not get focus before it's displayed, handle unredirected windows properly and | ||
676 | // so on. | 730 | // so on. | ||
677 | for (Toplevel *win : windows) { | 731 | for (Toplevel *win : windows) { | ||
678 | if (!win->readyForPainting()) { | 732 | if (!win->readyForPainting()) { | ||
679 | windows.removeAll(win); | 733 | windows.removeAll(win); | ||
680 | } | 734 | } | ||
681 | if (waylandServer() && waylandServer()->isScreenLocked()) { | 735 | if (waylandServer() && waylandServer()->isScreenLocked()) { | ||
682 | if(!win->isLockScreen() && !win->isInputMethod()) { | 736 | if(!win->isLockScreen() && !win->isInputMethod()) { | ||
683 | windows.removeAll(win); | 737 | windows.removeAll(win); | ||
684 | } | 738 | } | ||
685 | } | 739 | } | ||
686 | } | 740 | } | ||
687 | 741 | | |||
688 | QRegion repaints = repaints_region; | 742 | QRegion repaints = repaints_region; | ||
689 | // clear all repaints, so that post-pass can add repaints for the next repaint | 743 | // clear all repaints, so that post-pass can add repaints for the next repaint | ||
690 | repaints_region = QRegion(); | 744 | repaints_region = QRegion(); | ||
691 | 745 | | |||
692 | if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { | 746 | if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { | ||
693 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); | 747 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); | ||
694 | } | 748 | } | ||
695 | m_timeSinceLastVBlank = m_scene->paint(repaints, windows); | 749 | | ||
750 | const qint64 paintDuration = m_scene->paint(repaints, windows); | ||||
751 | updatePaintTimes(paintDuration); | ||||
752 | | ||||
696 | if (m_framesToTestForSafety > 0) { | 753 | if (m_framesToTestForSafety > 0) { | ||
697 | if (m_scene->compositingType() & OpenGLCompositing) { | 754 | if (m_scene->compositingType() & OpenGLCompositing) { | ||
698 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); | 755 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); | ||
699 | } | 756 | } | ||
700 | m_framesToTestForSafety--; | 757 | m_framesToTestForSafety--; | ||
701 | if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) { | 758 | if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) { | ||
702 | kwinApp()->platform()->createOpenGLSafePoint( | 759 | kwinApp()->platform()->createOpenGLSafePoint( | ||
703 | Platform::OpenGLSafePoint::PostLastGuardedFrame); | 760 | Platform::OpenGLSafePoint::PostLastGuardedFrame); | ||
704 | } | 761 | } | ||
705 | } | 762 | } | ||
706 | 763 | | |||
707 | if (waylandServer()) { | 764 | if (waylandServer()) { | ||
708 | const auto currentTime = static_cast<quint32>(m_monotonicClock.elapsed()); | 765 | const auto currentTime = static_cast<quint32>(m_monotonicClock.elapsed()); | ||
709 | for (Toplevel *win : qAsConst(windows)) { | 766 | for (Toplevel *win : qAsConst(windows)) { | ||
710 | if (auto surface = win->surface()) { | 767 | if (auto surface = win->surface()) { | ||
711 | surface->frameRendered(currentTime); | 768 | surface->frameRendered(currentTime); | ||
712 | } | 769 | } | ||
713 | } | 770 | } | ||
714 | } | 771 | } | ||
715 | 772 | | |||
716 | // Stop here to ensure *we* cause the next repaint schedule - not some effect | 773 | // Stop here to ensure *we* cause the next repaint schedule - not some effect | ||
717 | // through m_scene->paint(). | 774 | // through m_scene->paint(). | ||
718 | compositeTimer.stop(); | 775 | m_fallbackTimer.stop(); | ||
776 | | ||||
777 | Perf::ftrace(QStringLiteral("Paint"), s_msc); | ||||
778 | s_msc++; | ||||
719 | 779 | | |||
720 | // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() | 780 | // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() | ||
721 | // is called the next time. If there would be nothing pending, it will not restart the timer and | 781 | // is called the next time. If there would be nothing pending, it will not restart the timer and | ||
722 | // scheduleRepaint() would restart it again somewhen later, called from functions that | 782 | // scheduleRepaint() would restart it again somewhen later, called from functions that | ||
723 | // would again add something pending. | 783 | // would again add something pending. | ||
724 | if (m_bufferSwapPending && m_scene->syncsToVBlank()) { | 784 | if (m_bufferSwapPending) { | ||
725 | m_composeAtSwapCompletion = true; | 785 | m_composeAtSwapCompletion = true; | ||
726 | } else { | 786 | } else { | ||
727 | scheduleRepaint(); | 787 | scheduleRepaint(); | ||
728 | } | 788 | } | ||
729 | } | 789 | } | ||
730 | 790 | | |||
731 | template <class T> | 791 | template <class T> | ||
732 | static bool repaintsPending(const QList<T*> &windows) | 792 | static bool repaintsPending(const QList<T*> &windows) | ||
Show All 30 Lines | 812 | if (auto *server = waylandServer()) { | |||
763 | }; | 823 | }; | ||
764 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | 824 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | ||
765 | return true; | 825 | return true; | ||
766 | } | 826 | } | ||
767 | } | 827 | } | ||
768 | return false; | 828 | return false; | ||
769 | } | 829 | } | ||
770 | 830 | | |||
771 | void Compositor::setCompositeTimer() | 831 | void Compositor::setFallbackTimer() | ||
772 | { | 832 | { | ||
773 | if (m_state != State::On) { | 833 | if (m_fallbackTimer.isActive()) { | ||
774 | return; | 834 | return; | ||
775 | } | 835 | } | ||
776 | 836 | | |||
777 | // Don't start the timer if we're waiting for a swap event | 837 | uint waitTime = 1000 / refreshRate(); | ||
778 | if (m_bufferSwapPending && m_composeAtSwapCompletion) | | |||
779 | return; | | |||
780 | | ||||
781 | // Don't start the timer if all outputs are disabled | | |||
782 | if (!kwinApp()->platform()->areOutputsEnabled()) { | | |||
783 | return; | | |||
784 | } | | |||
785 | | ||||
786 | uint waitTime = 1; | | |||
787 | 838 | | |||
788 | if (m_scene->blocksForRetrace()) { | | |||
789 | | ||||
790 | // TODO: make vBlankTime dynamic?! | | |||
791 | // It's required because glXWaitVideoSync will *likely* block a full frame if one enters | | |||
792 | // a retrace pass which can last a variable amount of time, depending on the actual screen | | |||
793 | // Now, my ooold 19" CRT can do such retrace so that 2ms are entirely sufficient, | | |||
794 | // while another ooold 15" TFT requires about 6ms | | |||
795 | | ||||
796 | qint64 padding = m_timeSinceLastVBlank; | | |||
797 | if (padding > fpsInterval) { | | |||
798 | // We're at low repaints or spent more time in painting than the user wanted to wait | | |||
799 | // for that frame. Align to next vblank: | | |||
800 | padding = vBlankInterval - (padding % vBlankInterval); | | |||
801 | } else { | | |||
802 | // Align to the next maxFps tick: | | |||
803 | // "remaining time of the first vsync" + "time for the other vsyncs of the frame" | | |||
804 | padding = ((vBlankInterval - padding % vBlankInterval) + | | |||
805 | (fpsInterval / vBlankInterval - 1) * vBlankInterval); | | |||
806 | } | | |||
807 | | ||||
808 | if (padding < options->vBlankTime()) { | | |||
809 | // We'll likely miss this frame so we add one: | | |||
810 | waitTime = nanoToMilli(padding + vBlankInterval - options->vBlankTime()); | | |||
811 | } else { | | |||
812 | waitTime = nanoToMilli(padding - options->vBlankTime()); | | |||
813 | } | | |||
814 | } | | |||
815 | else { // w/o blocking vsync we just jump to the next demanded tick | | |||
816 | if (fpsInterval > m_timeSinceLastVBlank) { | | |||
817 | waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); | | |||
818 | if (!waitTime) { | | |||
819 | // Will ensure we don't block out the eventloop - the system's just not faster ... | | |||
820 | waitTime = 1; | | |||
821 | } | | |||
822 | } | | |||
823 | /* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) { | | |||
824 | // NOTICE - "for later" ------------------------------------------------------------------ | | |||
825 | // It can happen that we push two frames within one refresh cycle. | | |||
826 | // Swapping will then block even with triple buffering when the GPU does not discard but | | |||
827 | // queues frames | | |||
828 | // now here's the mean part: if we take that as "OMG, we're late - next frame ASAP", | | |||
829 | // there'll immediately be 2 frames in the pipe, swapping will block, we think we're | | |||
830 | // late ... ewww | | |||
831 | // so instead we pad to the clock again and add 2ms safety to ensure the pipe is really | | |||
832 | // free | | |||
833 | // NOTICE: obviously m_timeSinceLastVBlank can be too big because we're too slow as well | | |||
834 | // So if this code was enabled, we'd needlessly half the framerate once more (15 instead of 30) | | |||
835 | waitTime = nanoToMilli(vBlankInterval - (m_timeSinceLastVBlank - fpsInterval)%vBlankInterval) + 2; | | |||
836 | }*/ | | |||
837 | else { | | |||
838 | // "0" would be sufficient here, but the compositor isn't the WMs only task. | | |||
839 | waitTime = 1; | | |||
840 | } | | |||
841 | } | | |||
842 | // Force 4fps minimum: | 839 | // Force 4fps minimum: | ||
843 | compositeTimer.start(qMin(waitTime, 250u), this); | 840 | m_fallbackTimer.start(qMin(waitTime, 250u), this); | ||
844 | } | 841 | } | ||
845 | 842 | | |||
846 | bool Compositor::isActive() | 843 | bool Compositor::isActive() | ||
847 | { | 844 | { | ||
848 | return m_state == State::On; | 845 | return m_state == State::On; | ||
849 | } | 846 | } | ||
850 | 847 | | |||
851 | WaylandCompositor::WaylandCompositor(QObject *parent) | 848 | WaylandCompositor::WaylandCompositor(QObject *parent) | ||
Show All 30 Lines | 876 | { | |||
882 | return false; | 879 | return false; | ||
883 | } | 880 | } | ||
884 | 881 | | |||
885 | int WaylandCompositor::refreshRate() const | 882 | int WaylandCompositor::refreshRate() const | ||
886 | { | 883 | { | ||
887 | // TODO: This makes no sense on Wayland. First step would be to atleast | 884 | // TODO: This makes no sense on Wayland. First step would be to atleast | ||
888 | // set the refresh rate to the highest available one. Second step | 885 | // set the refresh rate to the highest available one. Second step | ||
889 | // would be to not use a uniform value at all but per screen. | 886 | // would be to not use a uniform value at all but per screen. | ||
890 | return KWin::currentRefreshRate(); | 887 | return 60; | ||
891 | } | 888 | } | ||
892 | 889 | | |||
893 | void WaylandCompositor::updateCompositeBlocking() | 890 | void WaylandCompositor::updateCompositeBlocking() | ||
894 | { | 891 | { | ||
895 | // Composite blocking not possible on Wayland. | 892 | // Composite blocking not possible on Wayland. | ||
896 | } | 893 | } | ||
897 | 894 | | |||
898 | void WaylandCompositor::updateClientCompositeBlocking(Client *c) | 895 | void WaylandCompositor::updateClientCompositeBlocking(Client *c) | ||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Line(s) | 965 | { | |||
985 | } | 982 | } | ||
986 | if (!Compositor::setupStart()) { | 983 | if (!Compositor::setupStart()) { | ||
987 | // Internal setup failed, abort. | 984 | // Internal setup failed, abort. | ||
988 | return; | 985 | return; | ||
989 | } | 986 | } | ||
990 | m_xrrRefreshRate = KWin::currentRefreshRate(); | 987 | m_xrrRefreshRate = KWin::currentRefreshRate(); | ||
991 | startupWithWorkspace(); | 988 | startupWithWorkspace(); | ||
992 | } | 989 | } | ||
993 | void X11Compositor::performCompositing() | 990 | void X11Compositor::performCompositing(bool force) | ||
994 | { | 991 | { | ||
995 | if (scene()->usesOverlayWindow() && !isOverlayWindowVisible()) { | 992 | if (scene()->usesOverlayWindow() && !isOverlayWindowVisible()) { | ||
996 | // Return since nothing is visible. | 993 | // Return since nothing is visible. | ||
997 | return; | 994 | return; | ||
998 | } | 995 | } | ||
999 | Compositor::performCompositing(); | 996 | Compositor::performCompositing(force); | ||
1000 | } | 997 | } | ||
1001 | 998 | | |||
1002 | bool X11Compositor::checkForOverlayWindow(WId w) const | 999 | bool X11Compositor::checkForOverlayWindow(WId w) const | ||
1003 | { | 1000 | { | ||
1004 | if (!hasScene()) { | 1001 | if (!hasScene()) { | ||
1005 | // No scene, so it cannot be the overlay window. | 1002 | // No scene, so it cannot be the overlay window. | ||
1006 | return false; | 1003 | return false; | ||
1007 | } | 1004 | } | ||
▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines |