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 287 Lines • ▼ Show 20 Line(s) | |||||
323 | 324 | | |||
324 | void Compositor::startupWithWorkspace() | 325 | void Compositor::startupWithWorkspace() | ||
325 | { | 326 | { | ||
326 | connect(kwinApp(), &Application::x11ConnectionChanged, | 327 | connect(kwinApp(), &Application::x11ConnectionChanged, | ||
327 | this, &Compositor::setupX11Support, Qt::UniqueConnection); | 328 | this, &Compositor::setupX11Support, Qt::UniqueConnection); | ||
328 | Workspace::self()->markXStackingOrderAsDirty(); | 329 | Workspace::self()->markXStackingOrderAsDirty(); | ||
329 | Q_ASSERT(m_scene); | 330 | Q_ASSERT(m_scene); | ||
330 | 331 | | |||
331 | connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); | 332 | connect(workspace(), &Workspace::destroyed, this, [this] { m_fallbackTimer.stop(); }); | ||
332 | setupX11Support(); | 333 | setupX11Support(); | ||
333 | fpsInterval = options->maxFpsInterval(); | 334 | fpsInterval = options->maxFpsInterval(); | ||
334 | 335 | | |||
335 | if (m_scene->syncsToVBlank()) { | 336 | if (m_scene->syncsToVBlank()) { | ||
336 | // If we do vsync, set the fps to the next multiple of the vblank rate. | 337 | // If we do vsync, set the fps to the next multiple of the vblank rate. | ||
337 | vBlankInterval = milliToNano(1000) / currentRefreshRate(); | 338 | vBlankInterval = milliToNano(1000) / currentRefreshRate(); | ||
338 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | 339 | fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); | ||
339 | } else { | 340 | } else { | ||
340 | // No vsync - DO NOT set "0", would cause div-by-zero segfaults. | 341 | // No vsync - DO NOT set "0", would cause div-by-zero segfaults. | ||
341 | vBlankInterval = milliToNano(1); | 342 | vBlankInterval = milliToNano(1); | ||
342 | } | 343 | } | ||
343 | 344 | | |||
344 | // This means "start now" - we don't have even a slight idea when the first vsync will occur. | 345 | // 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 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); | ||
346 | scheduleRepaint(); | 347 | // scheduleRepaint(); | ||
347 | 348 | | |||
348 | // Sets also the 'effects' pointer. | 349 | // Sets also the 'effects' pointer. | ||
349 | kwinApp()->platform()->createEffectsHandler(this, m_scene); | 350 | kwinApp()->platform()->createEffectsHandler(this, m_scene); | ||
350 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | 351 | connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::removeToplevel); | ||
351 | connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); | 352 | connect(effects, &EffectsHandler::screenGeometryChanged, this, &Compositor::addRepaintFull); | ||
352 | addRepaintFull(); | 353 | addRepaintFull(); | ||
353 | 354 | | |||
354 | for (Client *c : Workspace::self()->clientList()) { | 355 | for (Client *c : Workspace::self()->clientList()) { | ||
Show All 29 Lines | |||||
384 | } | 385 | } | ||
385 | 386 | | |||
386 | // Render at least once. | 387 | // Render at least once. | ||
387 | performCompositing(); | 388 | performCompositing(); | ||
388 | } | 389 | } | ||
389 | 390 | | |||
390 | void Compositor::scheduleRepaint() | 391 | void Compositor::scheduleRepaint() | ||
391 | { | 392 | { | ||
392 | if (!compositeTimer.isActive()) | 393 | if (m_state != State::On) { | ||
393 | setCompositeTimer(); | 394 | return; | ||
395 | } | ||||
396 | if (!kwinApp()->platform()->areOutputsEnabled()) { | ||||
397 | return; | ||||
398 | } | ||||
399 | | ||||
400 | // TODO: Make this distinction not on the question if there is a swap event but if per screen | ||||
401 | // rendering? On X we get swap events but they are aligned with the "wrong" screen if | ||||
402 | // it the primary/first one is not the one with the highest refresh rate. | ||||
403 | // But on the other side Present extension does not allow to sync with another screen | ||||
404 | // anyway. | ||||
405 | | ||||
406 | if (m_scene->hasSwapEvent()) { | ||||
407 | QTimer::singleShot(0, this, [this]() { performCompositing(); }); | ||||
408 | } else { | ||||
409 | setFallbackTimer(); | ||||
410 | } | ||||
394 | } | 411 | } | ||
395 | 412 | | |||
396 | void Compositor::stop() | 413 | void Compositor::stop() | ||
397 | { | 414 | { | ||
398 | if (m_state == State::Off || m_state == State::Stopping) { | 415 | if (m_state == State::Off || m_state == State::Stopping) { | ||
399 | return; | 416 | return; | ||
400 | } | 417 | } | ||
401 | m_state = State::Stopping; | 418 | m_state = State::Stopping; | ||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | 457 | if (waylandServer()) { | |||
449 | } | 466 | } | ||
450 | for (ShellClient *c : waylandServer()->internalClients()) { | 467 | for (ShellClient *c : waylandServer()->internalClients()) { | ||
451 | c->finishCompositing(); | 468 | c->finishCompositing(); | ||
452 | } | 469 | } | ||
453 | } | 470 | } | ||
454 | 471 | | |||
455 | delete m_scene; | 472 | delete m_scene; | ||
456 | m_scene = NULL; | 473 | m_scene = NULL; | ||
457 | compositeTimer.stop(); | 474 | m_fallbackTimer.stop(); | ||
458 | repaints_region = QRegion(); | 475 | repaints_region = QRegion(); | ||
459 | 476 | | |||
460 | m_state = State::Off; | 477 | m_state = State::Off; | ||
461 | emit compositingToggled(false); | 478 | emit compositingToggled(false); | ||
462 | } | 479 | } | ||
463 | 480 | | |||
464 | void Compositor::destroyCompositorSelection() | 481 | void Compositor::destroyCompositorSelection() | ||
465 | { | 482 | { | ||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Line(s) | 585 | if (!hasScene()) | |||
569 | return; | 586 | return; | ||
570 | const QSize &s = screens()->size(); | 587 | const QSize &s = screens()->size(); | ||
571 | repaints_region = QRegion(0, 0, s.width(), s.height()); | 588 | repaints_region = QRegion(0, 0, s.width(), s.height()); | ||
572 | scheduleRepaint(); | 589 | scheduleRepaint(); | ||
573 | } | 590 | } | ||
574 | 591 | | |||
575 | void Compositor::timerEvent(QTimerEvent *te) | 592 | void Compositor::timerEvent(QTimerEvent *te) | ||
576 | { | 593 | { | ||
577 | if (te->timerId() == compositeTimer.timerId()) { | 594 | if (te->timerId() == m_delayTimer.timerId()){ | ||
595 | // qDebug() << "Compositor::delay timer Event"; | ||||
596 | m_delayTimer.stop(); | ||||
597 | performCompositing(true); | ||||
598 | } else if (te->timerId() == m_fallbackTimer.timerId()) { | ||||
599 | // qDebug() << "Compositor::fallback timer Event"; | ||||
578 | performCompositing(); | 600 | performCompositing(); | ||
579 | } else | 601 | } else { | ||
580 | QObject::timerEvent(te); | 602 | QObject::timerEvent(te); | ||
581 | } | 603 | } | ||
604 | } | ||||
582 | 605 | | |||
583 | void Compositor::aboutToSwapBuffers() | 606 | void Compositor::aboutToSwapBuffers() | ||
584 | { | 607 | { | ||
585 | assert(!m_bufferSwapPending); | 608 | assert(!m_bufferSwapPending); | ||
586 | 609 | | |||
587 | m_bufferSwapPending = true; | 610 | m_bufferSwapPending = true; | ||
588 | } | 611 | } | ||
589 | 612 | | |||
590 | void Compositor::bufferSwapComplete() | 613 | void Compositor::bufferSwapComplete() | ||
591 | { | 614 | { | ||
592 | assert(m_bufferSwapPending); | 615 | assert(m_bufferSwapPending); | ||
593 | m_bufferSwapPending = false; | 616 | m_bufferSwapPending = false; | ||
594 | 617 | | |||
595 | emit bufferSwapCompleted(); | 618 | emit bufferSwapCompleted(); | ||
619 | Perf::ftrace(QStringLiteral("Swaped")); | ||||
596 | 620 | | |||
597 | if (m_composeAtSwapCompletion) { | 621 | if (m_composeAtSwapCompletion) { | ||
598 | m_composeAtSwapCompletion = false; | 622 | m_composeAtSwapCompletion = false; | ||
599 | performCompositing(); | 623 | performCompositing(); | ||
600 | } | 624 | } | ||
601 | } | 625 | } | ||
602 | 626 | | |||
603 | void Compositor::performCompositing() | 627 | void Compositor::updatePaintTimes(qint64 time) | ||
628 | { | ||||
629 | // const size_t prevIndex = m_paintTimesIndex; | ||||
630 | const size_t nextIndex = (m_paintTimesIndex + 1) % 4; | ||||
631 | | ||||
632 | m_paintTimes[nextIndex] = time; | ||||
633 | m_paintTimesIndex = nextIndex; | ||||
634 | } | ||||
635 | | ||||
636 | bool Compositor::delayPerform() | ||||
637 | { | ||||
638 | // Here we calculate in nano seconds. | ||||
639 | // const quint64 frameDuration = milliToNano(1000) / (double)currentRefreshRate(); | ||||
640 | const quint64 frameDuration = vBlankInterval; | ||||
641 | | ||||
642 | quint64 avgPaintTime = 0; | ||||
643 | for (const auto time : m_paintTimes) { | ||||
644 | avgPaintTime += time; | ||||
645 | } | ||||
646 | avgPaintTime = avgPaintTime / 4; | ||||
647 | | ||||
648 | // We give at least 10% of frame duration or 1 milli second time to paint. | ||||
649 | const qint64 minGap = qMax(nanoToMilli(frameDuration * 0.1), qint64(1)); | ||||
650 | const qint64 delay = nanoToMilli(frameDuration - avgPaintTime) - minGap; | ||||
651 | | ||||
652 | if (delay <= 0) { | ||||
653 | return false; | ||||
654 | } | ||||
655 | | ||||
656 | m_delayTimer.start(delay, this); | ||||
657 | return true; | ||||
658 | } | ||||
659 | | ||||
660 | static ulong s_msc = 0; | ||||
661 | | ||||
662 | void Compositor::performCompositing(bool force) | ||||
604 | { | 663 | { | ||
605 | // If a buffer swap is still pending, we return to the event loop and | 664 | // If a buffer swap is still pending, we return to the event loop and | ||
606 | // continue processing events until the swap has completed. | 665 | // continue processing events until the swap has completed. | ||
607 | if (m_bufferSwapPending) { | 666 | if (m_bufferSwapPending) { | ||
608 | m_composeAtSwapCompletion = true; | 667 | m_composeAtSwapCompletion = true; | ||
609 | compositeTimer.stop(); | 668 | m_fallbackTimer.stop(); | ||
610 | return; | 669 | return; | ||
611 | } | 670 | } | ||
612 | 671 | | |||
613 | // If outputs are disabled, we return to the event loop and | 672 | // If outputs are disabled, we return to the event loop and | ||
614 | // continue processing events until the outputs are enabled again | 673 | // continue processing events until the outputs are enabled again | ||
615 | if (!kwinApp()->platform()->areOutputsEnabled()) { | 674 | if (!kwinApp()->platform()->areOutputsEnabled()) { | ||
616 | compositeTimer.stop(); | 675 | m_fallbackTimer.stop(); | ||
617 | return; | 676 | return; | ||
618 | } | 677 | } | ||
619 | 678 | | |||
679 | if (m_delayTimer.isActive()) { | ||||
680 | return; | ||||
681 | } | ||||
682 | | ||||
683 | if (!force && m_scene->hasSwapEvent()) { | ||||
684 | // TODO: instead only check if vsynced? Currently not working without swap event | ||||
685 | if (delayPerform()) { | ||||
686 | return; | ||||
687 | } | ||||
688 | } | ||||
689 | | ||||
620 | // Create a list of all windows in the stacking order | 690 | // Create a list of all windows in the stacking order | ||
621 | ToplevelList windows = Workspace::self()->xStackingOrder(); | 691 | ToplevelList windows = Workspace::self()->xStackingOrder(); | ||
622 | ToplevelList damaged; | 692 | ToplevelList damaged; | ||
623 | 693 | | |||
624 | // Reset the damage state of each window and fetch the damage region | 694 | // Reset the damage state of each window and fetch the damage region | ||
625 | // without waiting for a reply | 695 | // without waiting for a reply | ||
626 | for (Toplevel *win : windows) { | 696 | for (Toplevel *win : windows) { | ||
627 | if (win->resetAndFetchDamage()) { | 697 | if (win->resetAndFetchDamage()) { | ||
Show All 31 Lines | |||||
659 | 729 | | |||
660 | if (repaints_region.isEmpty() && !windowRepaintsPending()) { | 730 | if (repaints_region.isEmpty() && !windowRepaintsPending()) { | ||
661 | m_scene->idle(); | 731 | m_scene->idle(); | ||
662 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" | 732 | m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" | ||
663 | // Note: It would seem here we should undo suspended unredirect, but when scenes need | 733 | // Note: It would seem here we should undo suspended unredirect, but when scenes need | ||
664 | // it for some reason, e.g. transformations or translucency, the next pass that does not | 734 | // it for some reason, e.g. transformations or translucency, the next pass that does not | ||
665 | // need this anymore and paints normally will also reset the suspended unredirect. | 735 | // need this anymore and paints normally will also reset the suspended unredirect. | ||
666 | // Otherwise the window would not be painted normally anyway. | 736 | // Otherwise the window would not be painted normally anyway. | ||
667 | compositeTimer.stop(); | 737 | m_fallbackTimer.stop(); | ||
668 | return; | 738 | return; | ||
669 | } | 739 | } | ||
670 | 740 | | |||
741 | Perf::ftrace(s_msc, QStringLiteral("Paint")); | ||||
742 | | ||||
671 | // Skip windows that are not yet ready for being painted and if screen is locked skip windows | 743 | // 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. | 744 | // that are neither lockscreen nor inputmethod windows. | ||
673 | // | 745 | // | ||
674 | // TODO? This cannot be used so carelessly - needs protections against broken clients, the | 746 | // 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 | 747 | // window should not get focus before it's displayed, handle unredirected windows properly and | ||
676 | // so on. | 748 | // so on. | ||
677 | for (Toplevel *win : windows) { | 749 | for (Toplevel *win : windows) { | ||
678 | if (!win->readyForPainting()) { | 750 | if (!win->readyForPainting()) { | ||
679 | windows.removeAll(win); | 751 | windows.removeAll(win); | ||
680 | } | 752 | } | ||
681 | if (waylandServer() && waylandServer()->isScreenLocked()) { | 753 | if (waylandServer() && waylandServer()->isScreenLocked()) { | ||
682 | if(!win->isLockScreen() && !win->isInputMethod()) { | 754 | if(!win->isLockScreen() && !win->isInputMethod()) { | ||
683 | windows.removeAll(win); | 755 | windows.removeAll(win); | ||
684 | } | 756 | } | ||
685 | } | 757 | } | ||
686 | } | 758 | } | ||
687 | 759 | | |||
688 | QRegion repaints = repaints_region; | 760 | QRegion repaints = repaints_region; | ||
689 | // clear all repaints, so that post-pass can add repaints for the next repaint | 761 | // clear all repaints, so that post-pass can add repaints for the next repaint | ||
690 | repaints_region = QRegion(); | 762 | repaints_region = QRegion(); | ||
691 | 763 | | |||
692 | if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { | 764 | if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { | ||
693 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); | 765 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); | ||
694 | } | 766 | } | ||
767 | | ||||
695 | m_timeSinceLastVBlank = m_scene->paint(repaints, windows); | 768 | m_timeSinceLastVBlank = m_scene->paint(repaints, windows); | ||
769 | updatePaintTimes(m_timeSinceLastVBlank); | ||||
770 | | ||||
696 | if (m_framesToTestForSafety > 0) { | 771 | if (m_framesToTestForSafety > 0) { | ||
697 | if (m_scene->compositingType() & OpenGLCompositing) { | 772 | if (m_scene->compositingType() & OpenGLCompositing) { | ||
698 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); | 773 | kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); | ||
699 | } | 774 | } | ||
700 | m_framesToTestForSafety--; | 775 | m_framesToTestForSafety--; | ||
701 | if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) { | 776 | if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) { | ||
702 | kwinApp()->platform()->createOpenGLSafePoint( | 777 | kwinApp()->platform()->createOpenGLSafePoint( | ||
703 | Platform::OpenGLSafePoint::PostLastGuardedFrame); | 778 | Platform::OpenGLSafePoint::PostLastGuardedFrame); | ||
704 | } | 779 | } | ||
705 | } | 780 | } | ||
706 | 781 | | |||
707 | if (waylandServer()) { | 782 | if (waylandServer()) { | ||
708 | const auto currentTime = static_cast<quint32>(m_monotonicClock.elapsed()); | 783 | const auto currentTime = static_cast<quint32>(m_monotonicClock.elapsed()); | ||
709 | for (Toplevel *win : qAsConst(windows)) { | 784 | for (Toplevel *win : qAsConst(windows)) { | ||
710 | if (auto surface = win->surface()) { | 785 | if (auto surface = win->surface()) { | ||
711 | surface->frameRendered(currentTime); | 786 | surface->frameRendered(currentTime); | ||
712 | } | 787 | } | ||
713 | } | 788 | } | ||
714 | } | 789 | } | ||
715 | 790 | | |||
716 | // Stop here to ensure *we* cause the next repaint schedule - not some effect | 791 | // Stop here to ensure *we* cause the next repaint schedule - not some effect | ||
717 | // through m_scene->paint(). | 792 | // through m_scene->paint(). | ||
718 | compositeTimer.stop(); | 793 | m_fallbackTimer.stop(); | ||
794 | | ||||
795 | Perf::ftrace(QStringLiteral("Paint"), s_msc); | ||||
796 | s_msc++; | ||||
719 | 797 | | |||
720 | // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() | 798 | // 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 | 799 | // 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 | 800 | // scheduleRepaint() would restart it again somewhen later, called from functions that | ||
723 | // would again add something pending. | 801 | // would again add something pending. | ||
724 | if (m_bufferSwapPending && m_scene->syncsToVBlank()) { | 802 | if (m_bufferSwapPending && m_scene->syncsToVBlank()) { | ||
725 | m_composeAtSwapCompletion = true; | 803 | m_composeAtSwapCompletion = true; | ||
726 | } else { | 804 | } else { | ||
Show All 36 Lines | 830 | if (auto *server = waylandServer()) { | |||
763 | }; | 841 | }; | ||
764 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | 842 | if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { | ||
765 | return true; | 843 | return true; | ||
766 | } | 844 | } | ||
767 | } | 845 | } | ||
768 | return false; | 846 | return false; | ||
769 | } | 847 | } | ||
770 | 848 | | |||
771 | void Compositor::setCompositeTimer() | 849 | void Compositor::setFallbackTimer() | ||
772 | { | 850 | { | ||
773 | if (m_state != State::On) { | 851 | if (m_fallbackTimer.isActive()) { | ||
774 | return; | | |||
775 | } | | |||
776 | | ||||
777 | // Don't start the timer if we're waiting for a swap event | | |||
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; | 852 | return; | ||
784 | } | 853 | } | ||
785 | 854 | | |||
786 | uint waitTime = 1; | 855 | uint waitTime = 1; | ||
787 | 856 | | |||
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) { | 857 | if (fpsInterval > m_timeSinceLastVBlank) { | ||
817 | waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); | 858 | waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); | ||
818 | if (!waitTime) { | 859 | if (!waitTime) { | ||
819 | // Will ensure we don't block out the eventloop - the system's just not faster ... | 860 | // Will ensure we don't block out the eventloop - the system's just not faster ... | ||
820 | waitTime = 1; | 861 | waitTime = 1; | ||
821 | } | 862 | } | ||
822 | } | 863 | } else { | ||
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. | 864 | // "0" would be sufficient here, but the compositor isn't the WMs only task. | ||
839 | waitTime = 1; | 865 | waitTime = 1; | ||
840 | } | 866 | } | ||
841 | } | 867 | | ||
842 | // Force 4fps minimum: | 868 | // Force 4fps minimum: | ||
843 | compositeTimer.start(qMin(waitTime, 250u), this); | 869 | m_fallbackTimer.start(qMin(waitTime, 250u), this); | ||
844 | } | 870 | } | ||
845 | 871 | | |||
846 | bool Compositor::isActive() | 872 | bool Compositor::isActive() | ||
847 | { | 873 | { | ||
848 | return m_state == State::On; | 874 | return m_state == State::On; | ||
849 | } | 875 | } | ||
850 | 876 | | |||
851 | WaylandCompositor::WaylandCompositor(QObject *parent) | 877 | WaylandCompositor::WaylandCompositor(QObject *parent) | ||
▲ Show 20 Lines • Show All 133 Lines • ▼ Show 20 Line(s) | 994 | { | |||
985 | } | 1011 | } | ||
986 | if (!Compositor::setupStart()) { | 1012 | if (!Compositor::setupStart()) { | ||
987 | // Internal setup failed, abort. | 1013 | // Internal setup failed, abort. | ||
988 | return; | 1014 | return; | ||
989 | } | 1015 | } | ||
990 | m_xrrRefreshRate = KWin::currentRefreshRate(); | 1016 | m_xrrRefreshRate = KWin::currentRefreshRate(); | ||
991 | startupWithWorkspace(); | 1017 | startupWithWorkspace(); | ||
992 | } | 1018 | } | ||
993 | void X11Compositor::performCompositing() | 1019 | void X11Compositor::performCompositing(bool force) | ||
994 | { | 1020 | { | ||
995 | if (scene()->usesOverlayWindow() && !isOverlayWindowVisible()) { | 1021 | if (scene()->usesOverlayWindow() && !isOverlayWindowVisible()) { | ||
996 | // Return since nothing is visible. | 1022 | // Return since nothing is visible. | ||
997 | return; | 1023 | return; | ||
998 | } | 1024 | } | ||
999 | Compositor::performCompositing(); | 1025 | Compositor::performCompositing(force); | ||
1000 | } | 1026 | } | ||
1001 | 1027 | | |||
1002 | bool X11Compositor::checkForOverlayWindow(WId w) const | 1028 | bool X11Compositor::checkForOverlayWindow(WId w) const | ||
1003 | { | 1029 | { | ||
1004 | if (!hasScene()) { | 1030 | if (!hasScene()) { | ||
1005 | // No scene, so it cannot be the overlay window. | 1031 | // No scene, so it cannot be the overlay window. | ||
1006 | return false; | 1032 | return false; | ||
1007 | } | 1033 | } | ||
▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines |