28#include <drm_fourcc.h>
30#include <sys/utsname.h>
34static std::unique_ptr<MockGpu> findPrimaryDevice(
int crtcCount)
36 const int deviceCount = drmGetDevices2(0,
nullptr, 0);
37 if (deviceCount <= 0) {
41 QList<drmDevice *> devices(deviceCount);
42 if (drmGetDevices2(0, devices.data(), devices.size()) < 0) {
45 auto deviceCleanup = qScopeGuard([&devices]() {
46 drmFreeDevices(devices.data(), devices.size());
49 for (drmDevice *device : std::as_const(devices)) {
50 if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
51 int fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
53 return std::make_unique<MockGpu>(fd, device->nodes[DRM_NODE_PRIMARY], crtcCount);
65 void testAmsDetection();
66 void testOutputDetection();
67 void testZeroModesHandling();
68 void testModeGeneration_data();
69 void testModeGeneration();
70 void testConnectorLifetime();
71 void testModeset_data();
75static void verifyCleanup(
MockGpu *mockGpu)
79 QVERIFY(mockGpu->
drmCrtcs.isEmpty());
82 QVERIFY(mockGpu->
fbs.isEmpty());
83 QVERIFY(mockGpu->
drmProps.isEmpty());
88void DrmTest::testAmsDetection()
91 const auto backend = std::make_unique<DrmBackend>(session.get());
95 const auto mockGpu = findPrimaryDevice(0);
96 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
97 QVERIFY(!gpu->atomicModeSetting());
102 const auto mockGpu = findPrimaryDevice(0);
104 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
105 gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
106 QVERIFY(gpu->atomicModeSetting());
111 const auto mockGpu = findPrimaryDevice(0);
113 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
114 QVERIFY(!gpu->atomicModeSetting());
116 verifyCleanup(mockGpu.get());
120void DrmTest::testOutputDetection()
122 const auto mockGpu = findPrimaryDevice(5);
124 const auto one = std::make_shared<MockConnector>(mockGpu.get());
125 const auto two = std::make_shared<MockConnector>(mockGpu.get());
126 const auto vr = std::make_shared<MockConnector>(mockGpu.get(),
true);
127 mockGpu->connectors.push_back(one);
128 mockGpu->connectors.push_back(two);
129 mockGpu->connectors.push_back(vr);
132 const auto backend = std::make_unique<DrmBackend>(session.get());
133 const auto renderBackend = backend->createQPainterBackend();
134 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
135 QVERIFY(gpu->updateOutputs());
138 const auto outputs = gpu->drmOutputs();
140 const auto vrOutput = std::find_if(
outputs.begin(),
outputs.end(), [](
const auto &output) {
141 return output->isNonDesktop();
143 QVERIFY(vrOutput !=
outputs.end());
147 mockGpu->connectors.removeOne(one);
148 QVERIFY(gpu->updateOutputs());
149 QCOMPARE(gpu->drmOutputs().size(), 2);
152 mockGpu->connectors.push_back(one);
153 QVERIFY(gpu->updateOutputs());
154 QCOMPARE(gpu->drmOutputs().size(), 3);
157 one->connection = DRM_MODE_DISCONNECTED;
158 QVERIFY(gpu->updateOutputs());
159 QCOMPARE(gpu->drmOutputs().size(), 2);
162 two->connection = DRM_MODE_DISCONNECTED;
163 vr->connection = DRM_MODE_DISCONNECTED;
164 QVERIFY(gpu->updateOutputs());
165 QVERIFY(gpu->drmOutputs().empty());
168 verifyCleanup(mockGpu.get());
171void DrmTest::testZeroModesHandling()
173 const auto mockGpu = findPrimaryDevice(5);
175 const auto conn = std::make_shared<MockConnector>(mockGpu.get());
176 mockGpu->connectors.push_back(conn);
179 const auto backend = std::make_unique<DrmBackend>(session.get());
180 const auto renderBackend = backend->createQPainterBackend();
181 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
185 QVERIFY(gpu->updateOutputs());
186 QVERIFY(gpu->drmOutputs().empty());
189 conn->addMode(1920, 1080, 60);
190 QVERIFY(gpu->updateOutputs());
191 QCOMPARE(gpu->drmOutputs().size(), 1);
195 QVERIFY(gpu->updateOutputs());
196 QCOMPARE(gpu->drmOutputs().size(), 1);
197 QVERIFY(!gpu->drmOutputs().constFirst()->modes().empty());
200 verifyCleanup(mockGpu.get());
203void DrmTest::testModeGeneration_data()
205 QTest::addColumn<QSize>(
"nativeMode");
206 QTest::addColumn<QList<QSize>>(
"expectedModes");
208 QTest::newRow(
"2160p") << QSize(3840, 2160) << QList<QSize>{
224 QTest::newRow(
"1440p") << QSize(2560, 1440) << QList<QSize>{
236 QTest::newRow(
"1080p") << QSize(1920, 1080) << QList<QSize>{
246 QTest::newRow(
"2160p 21:9") << QSize(5120, 2160) << QList<QSize>{
263 QTest::newRow(
"1440p 21:9") << QSize(3440, 1440) << QList<QSize>{
276 QTest::newRow(
"1080p 21:9") << QSize(2560, 1080) << QList<QSize>{
288void DrmTest::testModeGeneration()
290 const auto mockGpu = findPrimaryDevice(5);
292 const auto conn = std::make_shared<MockConnector>(mockGpu.get());
293 mockGpu->connectors.push_back(conn);
296 const auto backend = std::make_unique<DrmBackend>(session.get());
297 const auto renderBackend = backend->createQPainterBackend();
298 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
300 QFETCH(QSize, nativeMode);
301 QFETCH(QList<QSize>, expectedModes);
304 conn->addMode(nativeMode.width(), nativeMode.height(), 60);
305 QVERIFY(gpu->updateOutputs());
306 QCOMPARE(gpu->drmOutputs().size(), 1);
308 QCOMPARE(gpu->drmOutputs().front()->modes().size(), 1);
310 mockGpu->connectors.removeAll(conn);
311 QVERIFY(gpu->updateOutputs());
313 conn->props.emplace_back(conn.get(), QStringLiteral(
"scaling mode"), 0, DRM_MODE_PROP_ENUM, QList<QByteArray>{
"None",
"Full",
"Center",
"Full aspect"});
314 mockGpu->connectors.push_back(conn);
315 QVERIFY(gpu->updateOutputs());
317 DrmOutput *
const output = gpu->drmOutputs().front();
318 QCOMPARE(output->
modes().size(), expectedModes.size());
319 for (
const auto &mode : output->modes()) {
320 QVERIFY(expectedModes.contains(mode->size()));
321 QVERIFY(mode->size().width() <= nativeMode.width());
322 QVERIFY(mode->size().height() <= nativeMode.height());
323 QVERIFY(mode->refreshRate() <= 60000);
327 verifyCleanup(mockGpu.get());
330void DrmTest::testConnectorLifetime()
333 const auto mockGpu = findPrimaryDevice(5);
335 const auto conn = std::make_shared<MockConnector>(mockGpu.get());
336 mockGpu->connectors.push_back(conn);
339 const auto backend = std::make_unique<DrmBackend>(session.get());
340 const auto renderBackend = backend->createQPainterBackend();
341 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
343 QVERIFY(gpu->updateOutputs());
344 QCOMPARE(gpu->drmOutputs().size(), 1);
346 DrmOutput *
const output = gpu->drmOutputs().front();
349 mockGpu->connectors.clear();
350 QVERIFY(gpu->updateOutputs());
354 verifyCleanup(mockGpu.get());
357void DrmTest::testModeset_data()
359 QTest::addColumn<int>(
"AMS");
362 QTest::newRow(
"enabled") << 1;
365void DrmTest::testModeset()
369 const auto mockGpu = findPrimaryDevice(5);
372 const auto conn = std::make_shared<MockConnector>(mockGpu.get());
373 mockGpu->connectors.push_back(conn);
376 const auto backend = std::make_unique<DrmBackend>(session.get());
377 const auto renderBackend = backend->createQPainterBackend();
378 auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
380 QVERIFY(gpu->updateOutputs());
381 QCOMPARE(gpu->drmOutputs().size(), 1);
382 const auto output = gpu->drmOutputs().front();
391 verifyCleanup(mockGpu.get());
395#include "drmTest.moc"
RenderLoop * renderLoop() const override
DrmOutputLayer * primaryLayer() const override
bool present(const std::shared_ptr< OutputFrame > &frame) override
DrmConnector * connector() const
QList< std::shared_ptr< OutputMode > > modes() const
virtual std::optional< OutputLayerBeginFrameInfo > beginFrame()=0
static std::unique_ptr< Session > create()
QList< drmModePlanePtr > drmPlanes
QList< drmModeConnectorPtr > drmConnectors
QList< drmModePropertyPtr > drmProps
QList< drmModeObjectPropertiesPtr > drmObjectProperties
QList< drmModePropertyBlobPtr > drmPropertyBlobs
QList< drmModePlaneResPtr > drmPlaneRes
QList< drmModeCrtcPtr > drmCrtcs
QList< drmModeEncoderPtr > drmEncoders
#define MOCKDRM_DEVICE_CAP_ATOMIC
QList< KWayland::Client::Output * > outputs
KWIN_EXPORT QRect infiniteRegion()