Changeset View
Changeset View
Standalone View
Standalone View
autotests/integration/scene_opengl_shadow_test.cpp
Show All 24 Lines | |||||
25 | #include <QObject> | 25 | #include <QObject> | ||
26 | #include <QPair> | 26 | #include <QPair> | ||
27 | #include <QVector> | 27 | #include <QVector> | ||
28 | 28 | | |||
29 | #include <KDecoration2/Decoration> | 29 | #include <KDecoration2/Decoration> | ||
30 | #include <KDecoration2/DecorationShadow> | 30 | #include <KDecoration2/DecorationShadow> | ||
31 | 31 | | |||
32 | #include <KWayland/Client/server_decoration.h> | 32 | #include <KWayland/Client/server_decoration.h> | ||
33 | #include <KWayland/Client/shadow.h> | ||||
33 | #include <KWayland/Client/shell.h> | 34 | #include <KWayland/Client/shell.h> | ||
35 | #include <KWayland/Client/shm_pool.h> | ||||
34 | #include <KWayland/Client/surface.h> | 36 | #include <KWayland/Client/surface.h> | ||
37 | #include <KWayland/Server/shadow_interface.h> | ||||
38 | #include <KWayland/Server/surface_interface.h> | ||||
35 | 39 | | |||
36 | #include "kwin_wayland_test.h" | 40 | #include "kwin_wayland_test.h" | ||
37 | 41 | | |||
38 | #include "composite.h" | 42 | #include "composite.h" | ||
39 | #include "effect_builtins.h" | 43 | #include "effect_builtins.h" | ||
40 | #include "effectloader.h" | 44 | #include "effectloader.h" | ||
41 | #include "effects.h" | 45 | #include "effects.h" | ||
42 | #include "platform.h" | 46 | #include "platform.h" | ||
Show All 13 Lines | |||||
56 | { | 60 | { | ||
57 | Q_OBJECT | 61 | Q_OBJECT | ||
58 | 62 | | |||
59 | public: | 63 | public: | ||
60 | SceneOpenGLShadowTest() {} | 64 | SceneOpenGLShadowTest() {} | ||
61 | 65 | | |||
62 | private Q_SLOTS: | 66 | private Q_SLOTS: | ||
63 | void initTestCase(); | 67 | void initTestCase(); | ||
64 | void init(); | | |||
65 | void cleanup(); | 68 | void cleanup(); | ||
66 | 69 | | |||
67 | void testShadowTileOverlaps_data(); | 70 | void testShadowTileOverlaps_data(); | ||
68 | void testShadowTileOverlaps(); | 71 | void testShadowTileOverlaps(); | ||
72 | void testNoCornerShadowTiles(); | ||||
73 | void testDistributeHugeCornerTiles(); | ||||
69 | 74 | | |||
70 | }; | 75 | }; | ||
71 | 76 | | |||
72 | inline bool isClose(double a, double b, double eps = 1e-5) | 77 | inline bool isClose(double a, double b, double eps = 1e-5) | ||
73 | { | 78 | { | ||
74 | if (a == b) { | 79 | if (a == b) { | ||
75 | return true; | 80 | return true; | ||
76 | } | 81 | } | ||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Line(s) | 147 | QCoreApplication::addLibraryPath( | |||
143 | QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("fakes") | 148 | QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("fakes") | ||
144 | ); | 149 | ); | ||
145 | 150 | | |||
146 | // Change decoration theme. | 151 | // Change decoration theme. | ||
147 | KConfigGroup group = kwinApp()->config()->group("org.kde.kdecoration2"); | 152 | KConfigGroup group = kwinApp()->config()->group("org.kde.kdecoration2"); | ||
148 | group.writeEntry("library", "org.kde.test.fakedecowithshadows"); | 153 | group.writeEntry("library", "org.kde.test.fakedecowithshadows"); | ||
149 | group.sync(); | 154 | group.sync(); | ||
150 | Workspace::self()->slotReconfigure(); | 155 | Workspace::self()->slotReconfigure(); | ||
151 | } | | |||
152 | 156 | | |||
153 | void SceneOpenGLShadowTest::init() | | |||
154 | { | | |||
155 | QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration)); | | |||
156 | } | 157 | } | ||
157 | 158 | | |||
158 | void SceneOpenGLShadowTest::cleanup() | 159 | void SceneOpenGLShadowTest::cleanup() | ||
159 | { | 160 | { | ||
160 | Test::destroyWaylandConnection(); | 161 | Test::destroyWaylandConnection(); | ||
161 | } | 162 | } | ||
162 | 163 | | |||
163 | namespace { | 164 | namespace { | ||
▲ Show 20 Lines • Show All 453 Lines • ▼ Show 20 Line(s) | 184 | { | |||
617 | } | 618 | } | ||
618 | } | 619 | } | ||
619 | 620 | | |||
620 | void SceneOpenGLShadowTest::testShadowTileOverlaps() | 621 | void SceneOpenGLShadowTest::testShadowTileOverlaps() | ||
621 | { | 622 | { | ||
622 | QFETCH(QSize, windowSize); | 623 | QFETCH(QSize, windowSize); | ||
623 | QFETCH(WindowQuadList, expectedQuads); | 624 | QFETCH(WindowQuadList, expectedQuads); | ||
624 | 625 | | |||
626 | QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration)); | ||||
627 | | ||||
625 | // Create a decorated client. | 628 | // Create a decorated client. | ||
626 | QScopedPointer<Surface> surface(Test::createSurface()); | 629 | QScopedPointer<Surface> surface(Test::createSurface()); | ||
627 | QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data())); | 630 | QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data())); | ||
628 | QScopedPointer<ServerSideDecoration> ssd(Test::waylandServerSideDecoration()->create(surface.data())); | 631 | QScopedPointer<ServerSideDecoration> ssd(Test::waylandServerSideDecoration()->create(surface.data())); | ||
629 | 632 | | |||
630 | auto *client = Test::renderAndWaitForShown(surface.data(), windowSize, Qt::blue); | 633 | auto *client = Test::renderAndWaitForShown(surface.data(), windowSize, Qt::blue); | ||
631 | 634 | | |||
632 | QSignalSpy sizeChangedSpy(shellSurface.data(), &ShellSurface::sizeChanged); | 635 | QSignalSpy sizeChangedSpy(shellSurface.data(), &ShellSurface::sizeChanged); | ||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Line(s) | |||||
674 | 677 | | |||
675 | for (const auto &v : qAsConst(mask)) { | 678 | for (const auto &v : qAsConst(mask)) { | ||
676 | if (!v) { | 679 | if (!v) { | ||
677 | QFAIL("missed a shadow quad"); | 680 | QFAIL("missed a shadow quad"); | ||
678 | } | 681 | } | ||
679 | } | 682 | } | ||
680 | } | 683 | } | ||
681 | 684 | | |||
685 | void SceneOpenGLShadowTest::testNoCornerShadowTiles() | ||||
686 | { | ||||
687 | // this test verifies that top/right/bottom/left shadow tiles are | ||||
688 | // still drawn even when corner tiles are missing | ||||
689 | | ||||
690 | QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::ShadowManager)); | ||||
691 | | ||||
692 | // Create a surface. | ||||
693 | QScopedPointer<Surface> surface(Test::createSurface()); | ||||
694 | QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data())); | ||||
695 | auto *client = Test::renderAndWaitForShown(surface.data(), QSize(512, 512), Qt::blue); | ||||
696 | QVERIFY(client); | ||||
697 | QVERIFY(!client->isDecorated()); | ||||
698 | | ||||
699 | // Render reference shadow texture with the following params: | ||||
700 | // - shadow size: 128 | ||||
701 | // - inner rect size: 1 | ||||
702 | // - padding: 128 | ||||
703 | QImage referenceShadowTexture(256 + 1, 256 + 1, QImage::Format_ARGB32_Premultiplied); | ||||
704 | referenceShadowTexture.fill(Qt::transparent); | ||||
705 | | ||||
706 | // We don't care about content of the shadow. | ||||
707 | | ||||
708 | // Submit the shadow to KWin. | ||||
709 | QScopedPointer<KWayland::Client::Shadow> clientShadow(Test::waylandShadowManager()->createShadow(surface.data())); | ||||
710 | QVERIFY(clientShadow->isValid()); | ||||
711 | | ||||
712 | auto *shmPool = Test::waylandShmPool(); | ||||
713 | | ||||
714 | Buffer::Ptr bufferTop = shmPool->createBuffer( | ||||
715 | referenceShadowTexture.copy(QRect(128, 0, 1, 128))); | ||||
716 | clientShadow->attachTop(bufferTop); | ||||
717 | | ||||
718 | Buffer::Ptr bufferRight = shmPool->createBuffer( | ||||
719 | referenceShadowTexture.copy(QRect(128 + 1, 128, 128, 1))); | ||||
720 | clientShadow->attachRight(bufferRight); | ||||
721 | | ||||
722 | Buffer::Ptr bufferBottom = shmPool->createBuffer( | ||||
723 | referenceShadowTexture.copy(QRect(128, 128 + 1, 1, 128))); | ||||
724 | clientShadow->attachBottom(bufferBottom); | ||||
725 | | ||||
726 | Buffer::Ptr bufferLeft = shmPool->createBuffer( | ||||
727 | referenceShadowTexture.copy(QRect(0, 128, 128, 1))); | ||||
728 | clientShadow->attachLeft(bufferLeft); | ||||
729 | | ||||
730 | clientShadow->setOffsets(QMarginsF(128, 128, 128, 128)); | ||||
731 | | ||||
732 | QSignalSpy shadowChangedSpy(client->surface(), &KWayland::Server::SurfaceInterface::shadowChanged); | ||||
733 | QVERIFY(shadowChangedSpy.isValid()); | ||||
734 | clientShadow->commit(); | ||||
735 | surface->commit(Surface::CommitFlag::None); | ||||
736 | QVERIFY(shadowChangedSpy.wait()); | ||||
737 | | ||||
738 | // Check that we got right shadow from the client. | ||||
739 | QPointer<KWayland::Server::ShadowInterface> shadowIface = client->surface()->shadow(); | ||||
740 | QVERIFY(!shadowIface.isNull()); | ||||
741 | QCOMPARE(shadowIface->offset().left(), 128.0); | ||||
742 | QCOMPARE(shadowIface->offset().top(), 128.0); | ||||
743 | QCOMPARE(shadowIface->offset().right(), 128.0); | ||||
744 | QCOMPARE(shadowIface->offset().bottom(), 128.0); | ||||
745 | | ||||
746 | QVERIFY(client->effectWindow()); | ||||
747 | QVERIFY(client->effectWindow()->sceneWindow()); | ||||
748 | KWin::Shadow *shadow = client->effectWindow()->sceneWindow()->shadow(); | ||||
749 | QVERIFY(shadow != nullptr); | ||||
750 | | ||||
751 | const WindowQuadList &quads = shadow->shadowQuads(); | ||||
752 | QCOMPARE(quads.count(), 4); | ||||
753 | | ||||
754 | // Shadow size: 128 | ||||
755 | // Padding: QMargins(128, 128, 128, 128) | ||||
756 | // Inner rect: QRect(128, 128, 1, 1) | ||||
757 | // Texture size: QSize(257, 257) | ||||
758 | // Window size: QSize(512, 512) | ||||
759 | WindowQuadList expectedQuads; | ||||
760 | expectedQuads << makeShadowQuad(QRectF(0, -128, 512, 128), 0.0, 0.0, 1.0 / 257.0, 128.0 / 257.0); // top | ||||
761 | expectedQuads << makeShadowQuad(QRectF(512, 0, 128, 512), 1.0 - 128.0 / 257.0, 0.0, 1.0, 1.0 / 257.0); // right | ||||
762 | expectedQuads << makeShadowQuad(QRectF(0, 512, 512, 128), 0.0, 1.0 - 128.0 / 257, 1.0 / 257, 1.0); // bottom | ||||
763 | expectedQuads << makeShadowQuad(QRectF(-128, 0, 128, 512), 0.0, 0.0, 128.0 / 257.0, 1.0 / 257.0); // left | ||||
764 | | ||||
765 | for (const WindowQuad &expectedQuad : expectedQuads) { | ||||
766 | auto it = std::find_if(quads.constBegin(), quads.constEnd(), | ||||
767 | [&expectedQuad](const WindowQuad &quad) { | ||||
768 | return compareQuads(quad, expectedQuad); | ||||
769 | }); | ||||
770 | if (it == quads.constEnd()) { | ||||
771 | const QString message = QStringLiteral("Missing shadow quad (left: %1, top: %2, right: %3, bottom: %4)") | ||||
772 | .arg(expectedQuad.left()) | ||||
773 | .arg(expectedQuad.top()) | ||||
774 | .arg(expectedQuad.right()) | ||||
775 | .arg(expectedQuad.bottom()); | ||||
776 | const QByteArray rawMessage = message.toLocal8Bit().data(); | ||||
777 | QFAIL(rawMessage.data()); | ||||
778 | } | ||||
779 | } | ||||
780 | } | ||||
781 | | ||||
782 | void SceneOpenGLShadowTest::testDistributeHugeCornerTiles() | ||||
783 | { | ||||
784 | // this test verifies that huge corner tiles are distributed correctly | ||||
785 | | ||||
786 | QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::ShadowManager)); | ||||
787 | | ||||
788 | // Create a surface. | ||||
789 | QScopedPointer<Surface> surface(Test::createSurface()); | ||||
790 | QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data())); | ||||
791 | auto *client = Test::renderAndWaitForShown(surface.data(), QSize(64, 64), Qt::blue); | ||||
792 | QVERIFY(client); | ||||
793 | QVERIFY(!client->isDecorated()); | ||||
794 | | ||||
795 | // Submit the shadow to KWin. | ||||
796 | QScopedPointer<KWayland::Client::Shadow> clientShadow(Test::waylandShadowManager()->createShadow(surface.data())); | ||||
797 | QVERIFY(clientShadow->isValid()); | ||||
798 | | ||||
799 | QImage referenceTileTexture(512, 512, QImage::Format_ARGB32_Premultiplied); | ||||
800 | referenceTileTexture.fill(Qt::transparent); | ||||
801 | | ||||
802 | auto *shmPool = Test::waylandShmPool(); | ||||
803 | | ||||
804 | Buffer::Ptr bufferTopLeft = shmPool->createBuffer(referenceTileTexture); | ||||
805 | clientShadow->attachTopLeft(bufferTopLeft); | ||||
806 | | ||||
807 | Buffer::Ptr bufferTopRight = shmPool->createBuffer(referenceTileTexture); | ||||
808 | clientShadow->attachTopRight(bufferTopRight); | ||||
809 | | ||||
810 | clientShadow->setOffsets(QMarginsF(256, 256, 256, 0)); | ||||
811 | | ||||
812 | QSignalSpy shadowChangedSpy(client->surface(), &KWayland::Server::SurfaceInterface::shadowChanged); | ||||
813 | QVERIFY(shadowChangedSpy.isValid()); | ||||
814 | clientShadow->commit(); | ||||
815 | surface->commit(Surface::CommitFlag::None); | ||||
816 | QVERIFY(shadowChangedSpy.wait()); | ||||
817 | | ||||
818 | // Check that we got right shadow from the client. | ||||
819 | QPointer<KWayland::Server::ShadowInterface> shadowIface = client->surface()->shadow(); | ||||
820 | QVERIFY(!shadowIface.isNull()); | ||||
821 | QCOMPARE(shadowIface->offset().left(), 256.0); | ||||
822 | QCOMPARE(shadowIface->offset().top(), 256.0); | ||||
823 | QCOMPARE(shadowIface->offset().right(), 256.0); | ||||
824 | QCOMPARE(shadowIface->offset().bottom(), 0.0); | ||||
825 | | ||||
826 | QVERIFY(client->effectWindow()); | ||||
827 | QVERIFY(client->effectWindow()->sceneWindow()); | ||||
828 | KWin::Shadow *shadow = client->effectWindow()->sceneWindow()->shadow(); | ||||
829 | QVERIFY(shadow != nullptr); | ||||
830 | | ||||
831 | WindowQuadList expectedQuads; | ||||
832 | | ||||
833 | // Top-left quad | ||||
834 | expectedQuads << makeShadowQuad( | ||||
835 | QRectF(-256, -256, 256 + 32, 256 + 64), | ||||
836 | 0.0, 0.0, (256.0 + 32.0) / 1024.0, (256.0 + 64.0) / 512.0); | ||||
837 | | ||||
838 | // Top-right quad | ||||
839 | expectedQuads << makeShadowQuad( | ||||
840 | QRectF(32, -256, 256 + 32, 256 + 64), | ||||
841 | 1.0 - (256.0 + 32.0) / 1024.0, 0.0, 1.0, (256.0 + 64.0) / 512.0); | ||||
842 | | ||||
843 | const WindowQuadList &quads = shadow->shadowQuads(); | ||||
844 | QCOMPARE(quads.count(), expectedQuads.count()); | ||||
845 | | ||||
846 | for (const WindowQuad &expectedQuad : expectedQuads) { | ||||
847 | auto it = std::find_if(quads.constBegin(), quads.constEnd(), | ||||
848 | [&expectedQuad](const WindowQuad &quad) { | ||||
849 | return compareQuads(quad, expectedQuad); | ||||
850 | }); | ||||
851 | if (it == quads.constEnd()) { | ||||
852 | const QString message = QStringLiteral("Missing shadow quad (left: %1, top: %2, right: %3, bottom: %4)") | ||||
853 | .arg(expectedQuad.left()) | ||||
854 | .arg(expectedQuad.top()) | ||||
855 | .arg(expectedQuad.right()) | ||||
856 | .arg(expectedQuad.bottom()); | ||||
857 | const QByteArray rawMessage = message.toLocal8Bit().data(); | ||||
858 | QFAIL(rawMessage.data()); | ||||
859 | } | ||||
860 | } | ||||
861 | } | ||||
862 | | ||||
682 | WAYLANDTEST_MAIN(SceneOpenGLShadowTest) | 863 | WAYLANDTEST_MAIN(SceneOpenGLShadowTest) | ||
683 | #include "scene_opengl_shadow_test.moc" | 864 | #include "scene_opengl_shadow_test.moc" |