~ubuntu-branches/ubuntu/wily/mir/wily-proposed

« back to all changes in this revision

Viewing changes to tests/unit-tests/graphics/mesa/test_display_multi_monitor.cpp

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2014-01-08 02:04:38 UTC
  • mto: This revision was merged to the branch mainline in revision 58.
  • Revision ID: package-import@ubuntu.com-20140108020438-e1npu0pm7qdv5wc4
Tags: upstream-0.1.3+14.04.20140108
ImportĀ upstreamĀ versionĀ 0.1.3+14.04.20140108

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright Ā© 2013 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
 
17
 */
 
18
 
 
19
#include "mir/graphics/display.h"
 
20
#include "mir/graphics/display_buffer.h"
 
21
#include "mir/graphics/display_configuration.h"
 
22
#include "src/platform/graphics/mesa/platform.h"
 
23
 
 
24
#include "mir_test_doubles/mock_egl.h"
 
25
#include "mir_test_doubles/mock_gl.h"
 
26
#include "mir/graphics/null_display_report.h"
 
27
#include "mir/graphics/display_configuration_policy.h"
 
28
#include "mir_test_doubles/null_virtual_terminal.h"
 
29
 
 
30
#include "mir_test_framework/udev_environment.h"
 
31
 
 
32
#include "mir_test_doubles/mock_drm.h"
 
33
#include "mir_test_doubles/mock_gbm.h"
 
34
 
 
35
#include <gtest/gtest.h>
 
36
#include <gmock/gmock.h>
 
37
 
 
38
#include <unordered_set>
 
39
 
 
40
namespace mg = mir::graphics;
 
41
namespace mgm = mir::graphics::mesa;
 
42
namespace geom = mir::geometry;
 
43
namespace mtd = mir::test::doubles;
 
44
namespace mtf = mir::mir_test_framework;
 
45
 
 
46
namespace
 
47
{
 
48
 
 
49
class ClonedDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy
 
50
{
 
51
public:
 
52
    void apply_to(mg::DisplayConfiguration& conf)
 
53
    {
 
54
        conf.for_each_output(
 
55
            [&](mg::DisplayConfigurationOutput const& conf_output)
 
56
            {
 
57
                if (conf_output.connected && conf_output.modes.size() > 0)
 
58
                {
 
59
                    conf.configure_output(conf_output.id, true, geom::Point{0, 0},
 
60
                                          conf_output.preferred_mode_index, mir_power_mode_on);
 
61
                }
 
62
                else
 
63
                {
 
64
                    conf.configure_output(conf_output.id, false, conf_output.top_left,
 
65
                                          conf_output.current_mode_index, mir_power_mode_on);
 
66
                }
 
67
            });
 
68
    }
 
69
};
 
70
 
 
71
class SideBySideDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy
 
72
{
 
73
public:
 
74
    void apply_to(mg::DisplayConfiguration& conf)
 
75
    {
 
76
        int max_x = 0;
 
77
 
 
78
        conf.for_each_output(
 
79
            [&](mg::DisplayConfigurationOutput const& conf_output)
 
80
            {
 
81
                if (conf_output.connected && conf_output.modes.size() > 0)
 
82
                {
 
83
                    conf.configure_output(conf_output.id, true, geom::Point{max_x, 0},
 
84
                                          conf_output.preferred_mode_index, mir_power_mode_on);
 
85
                    max_x += conf_output.modes[conf_output.preferred_mode_index].size.width.as_int();
 
86
                }
 
87
                else
 
88
                {
 
89
                    conf.configure_output(conf_output.id, false, conf_output.top_left,
 
90
                                          conf_output.current_mode_index, mir_power_mode_on);
 
91
                }
 
92
            });
 
93
    }
 
94
};
 
95
 
 
96
class MesaDisplayMultiMonitorTest : public ::testing::Test
 
97
{
 
98
public:
 
99
    MesaDisplayMultiMonitorTest()
 
100
    {
 
101
        using namespace testing;
 
102
 
 
103
        /* Needed for display start-up */
 
104
        ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
 
105
            .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
 
106
                                 SetArgPointee<4>(1),
 
107
                                 Return(EGL_TRUE)));
 
108
 
 
109
        const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_MESA_drm_image";
 
110
        const char* gl_exts = "GL_OES_texture_npot GL_OES_EGL_image";
 
111
 
 
112
        ON_CALL(mock_egl, eglQueryString(_,EGL_EXTENSIONS))
 
113
            .WillByDefault(Return(egl_exts));
 
114
        ON_CALL(mock_gl, glGetString(GL_EXTENSIONS))
 
115
            .WillByDefault(Return(reinterpret_cast<const GLubyte*>(gl_exts)));
 
116
 
 
117
        /*
 
118
         * Silence uninteresting calls called when cleaning up resources in
 
119
         * the MockGBM destructor, and which are not handled by NiceMock<>.
 
120
         */
 
121
        EXPECT_CALL(mock_gbm, gbm_bo_get_device(_))
 
122
            .Times(AtLeast(0));
 
123
        EXPECT_CALL(mock_gbm, gbm_device_get_fd(_))
 
124
            .Times(AtLeast(0));
 
125
 
 
126
        fake_devices.add_standard_drm_devices();
 
127
    }
 
128
 
 
129
    std::shared_ptr<mgm::Platform> create_platform()
 
130
    {
 
131
        return std::make_shared<mgm::Platform>(
 
132
            std::make_shared<mg::NullDisplayReport>(),
 
133
            std::make_shared<mtd::NullVirtualTerminal>());
 
134
    }
 
135
 
 
136
    std::shared_ptr<mg::Display> create_display_cloned(
 
137
        std::shared_ptr<mg::Platform> const& platform)
 
138
    {
 
139
        auto conf_policy = std::make_shared<ClonedDisplayConfigurationPolicy>();
 
140
        return platform->create_display(conf_policy);
 
141
    }
 
142
 
 
143
    std::shared_ptr<mg::Display> create_display_side_by_side(
 
144
        std::shared_ptr<mg::Platform> const& platform)
 
145
    {
 
146
        auto conf_policy = std::make_shared<SideBySideDisplayConfigurationPolicy>();
 
147
        return platform->create_display(conf_policy);
 
148
    }
 
149
 
 
150
    void setup_outputs(int connected, int disconnected)
 
151
    {
 
152
        using fake = mtd::FakeDRMResources;
 
153
 
 
154
        mtd::FakeDRMResources& resources(mock_drm.fake_drm);
 
155
 
 
156
        modes0.clear();
 
157
        modes0.push_back(fake::create_mode(1920, 1080, 138500, 2080, 1111, fake::NormalMode));
 
158
        modes0.push_back(fake::create_mode(1920, 1080, 148500, 2200, 1125, fake::PreferredMode));
 
159
        modes0.push_back(fake::create_mode(1680, 1050, 119000, 1840, 1080, fake::NormalMode));
 
160
        modes0.push_back(fake::create_mode(832, 624, 57284, 1152, 667, fake::NormalMode));
 
161
 
 
162
        geom::Size const connector_physical_size_mm{1597, 987};
 
163
 
 
164
        resources.reset();
 
165
 
 
166
        uint32_t const crtc_base_id{10};
 
167
        uint32_t const encoder_base_id{20};
 
168
        uint32_t const connector_base_id{30};
 
169
 
 
170
        for (int i = 0; i < connected; i++)
 
171
        {
 
172
            uint32_t const crtc_id{crtc_base_id + i};
 
173
            uint32_t const encoder_id{encoder_base_id + i};
 
174
            uint32_t const all_crtcs_mask{0xff};
 
175
 
 
176
            crtc_ids.push_back(crtc_id);
 
177
            resources.add_crtc(crtc_id, drmModeModeInfo());
 
178
 
 
179
            encoder_ids.push_back(encoder_id);
 
180
            resources.add_encoder(encoder_id, crtc_id, all_crtcs_mask);
 
181
        }
 
182
 
 
183
        for (int i = 0; i < connected; i++)
 
184
        {
 
185
            uint32_t const connector_id{connector_base_id + i};
 
186
 
 
187
            connector_ids.push_back(connector_id);
 
188
            resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA,
 
189
                                    DRM_MODE_CONNECTED, encoder_ids[i],
 
190
                                    modes0, encoder_ids, connector_physical_size_mm);
 
191
        }
 
192
 
 
193
        for (int i = 0; i < disconnected; i++)
 
194
        {
 
195
            uint32_t const connector_id{connector_base_id + connected + i};
 
196
 
 
197
            connector_ids.push_back(connector_id);
 
198
            resources.add_connector(connector_id, DRM_MODE_CONNECTOR_VGA,
 
199
                                    DRM_MODE_DISCONNECTED, 0,
 
200
                                    modes_empty, encoder_ids, geom::Size{});
 
201
        }
 
202
 
 
203
        resources.prepare();
 
204
    }
 
205
 
 
206
 
 
207
    testing::NiceMock<mtd::MockEGL> mock_egl;
 
208
    testing::NiceMock<mtd::MockGL> mock_gl;
 
209
    testing::NiceMock<mtd::MockDRM> mock_drm;
 
210
    testing::NiceMock<mtd::MockGBM> mock_gbm;
 
211
 
 
212
    std::vector<drmModeModeInfo> modes0;
 
213
    std::vector<drmModeModeInfo> modes_empty;
 
214
    std::vector<uint32_t> crtc_ids;
 
215
    std::vector<uint32_t> encoder_ids;
 
216
    std::vector<uint32_t> connector_ids;
 
217
 
 
218
    mtf::UdevEnvironment fake_devices;
 
219
};
 
220
 
 
221
}
 
222
 
 
223
TEST_F(MesaDisplayMultiMonitorTest, create_display_sets_all_connected_crtcs)
 
224
{
 
225
    using namespace testing;
 
226
 
 
227
    int const num_connected_outputs{3};
 
228
    int const num_disconnected_outputs{2};
 
229
    uint32_t const fb_id{66};
 
230
 
 
231
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
232
 
 
233
    /* Create DRM FBs */
 
234
    EXPECT_CALL(mock_drm, drmModeAddFB(mock_drm.fake_drm.fd(),
 
235
                                       _, _, _, _, _, _, _))
 
236
        .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
 
237
 
 
238
    ExpectationSet crtc_setups;
 
239
 
 
240
    /* All crtcs are set */
 
241
    for (int i = 0; i < num_connected_outputs; i++)
 
242
    {
 
243
        crtc_setups += EXPECT_CALL(mock_drm,
 
244
                                   drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
245
                                                  crtc_ids[i], fb_id,
 
246
                                                  _, _,
 
247
                                                  Pointee(connector_ids[i]),
 
248
                                                  _, _))
 
249
                           .Times(AtLeast(1));
 
250
    }
 
251
 
 
252
    /* All crtcs are restored at teardown */
 
253
    for (int i = 0; i < num_connected_outputs; i++)
 
254
    {
 
255
        EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
256
                                             crtc_ids[i], Ne(fb_id),
 
257
                                             _, _,
 
258
                                             Pointee(connector_ids[i]),
 
259
                                             _, _))
 
260
            .Times(1)
 
261
            .After(crtc_setups);
 
262
    }
 
263
 
 
264
    auto display = create_display_cloned(create_platform());
 
265
}
 
266
 
 
267
TEST_F(MesaDisplayMultiMonitorTest, create_display_creates_shared_egl_contexts)
 
268
{
 
269
    using namespace testing;
 
270
 
 
271
    int const num_connected_outputs{3};
 
272
    int const num_disconnected_outputs{2};
 
273
    EGLContext const shared_context{reinterpret_cast<EGLContext>(0x77)};
 
274
 
 
275
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
276
 
 
277
    /* Will create only one shared context */
 
278
    EXPECT_CALL(mock_egl, eglCreateContext(_, _, EGL_NO_CONTEXT, _))
 
279
        .WillOnce(Return(shared_context));
 
280
 
 
281
    /* Will use the shared context when creating other contexts */
 
282
    EXPECT_CALL(mock_egl, eglCreateContext(_, _, shared_context, _))
 
283
        .Times(AtLeast(1));
 
284
 
 
285
    {
 
286
        InSequence s;
 
287
 
 
288
        /* Contexts are made current to initialize DisplayBuffers */
 
289
        EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(shared_context)))
 
290
            .Times(AtLeast(1));
 
291
 
 
292
        /* The shared context is made current finally */
 
293
        EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,shared_context))
 
294
            .Times(1);
 
295
    }
 
296
 
 
297
    auto display = create_display_cloned(create_platform());
 
298
}
 
299
 
 
300
namespace
 
301
{
 
302
 
 
303
ACTION_P(InvokePageFlipHandler, param)
 
304
{
 
305
    int const dont_care{0};
 
306
    char dummy;
 
307
 
 
308
    arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, *param);
 
309
    ASSERT_EQ(1, read(arg0, &dummy, 1));
 
310
}
 
311
 
 
312
}
 
313
 
 
314
TEST_F(MesaDisplayMultiMonitorTest, post_update_flips_all_connected_crtcs)
 
315
{
 
316
    using namespace testing;
 
317
 
 
318
    int const num_connected_outputs{3};
 
319
    int const num_disconnected_outputs{2};
 
320
    uint32_t const fb_id{66};
 
321
    std::vector<void*> user_data(num_connected_outputs, nullptr);
 
322
 
 
323
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
324
 
 
325
    /* Create DRM FBs */
 
326
    EXPECT_CALL(mock_drm, drmModeAddFB(mock_drm.fake_drm.fd(),
 
327
                                       _, _, _, _, _, _, _))
 
328
        .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0)));
 
329
 
 
330
    /* All crtcs are flipped */
 
331
    for (int i = 0; i < num_connected_outputs; i++)
 
332
    {
 
333
        EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
 
334
                                              crtc_ids[i], fb_id,
 
335
                                              _, _))
 
336
            .Times(1)
 
337
            .WillOnce(DoAll(SaveArg<4>(&user_data[i]), Return(0)));
 
338
 
 
339
        /* Emit fake DRM page-flip events */
 
340
        EXPECT_EQ(1, write(mock_drm.fake_drm.write_fd(), "a", 1));
 
341
    }
 
342
 
 
343
    /* Handle the events properly */
 
344
    EXPECT_CALL(mock_drm, drmHandleEvent(mock_drm.fake_drm.fd(), _))
 
345
        .Times(num_connected_outputs)
 
346
        .WillOnce(DoAll(InvokePageFlipHandler(&user_data[0]), Return(0)))
 
347
        .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0)))
 
348
        .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0)));
 
349
 
 
350
    auto display = create_display_cloned(create_platform());
 
351
 
 
352
    display->for_each_display_buffer([](mg::DisplayBuffer& buffer)
 
353
    {
 
354
        buffer.post_update();
 
355
    });
 
356
}
 
357
 
 
358
namespace
 
359
{
 
360
 
 
361
struct FBIDContainer
 
362
{
 
363
    FBIDContainer(uint32_t base_fb_id) : last_fb_id{base_fb_id} {}
 
364
 
 
365
    int add_fb(int, uint32_t, uint32_t, uint8_t,
 
366
               uint8_t, uint32_t, uint32_t,
 
367
               uint32_t *buf_id)
 
368
    {
 
369
        *buf_id = last_fb_id;
 
370
        fb_ids.insert(last_fb_id);
 
371
        ++last_fb_id;
 
372
        return 0;
 
373
    }
 
374
 
 
375
    bool check_fb_id(uint32_t i)
 
376
    {
 
377
        if (fb_ids.find(i) != fb_ids.end())
 
378
        {
 
379
            fb_ids.erase(i);
 
380
            return true;
 
381
        }
 
382
 
 
383
        return false;
 
384
    }
 
385
 
 
386
    std::unordered_set<uint32_t> fb_ids;
 
387
    uint32_t last_fb_id;
 
388
};
 
389
 
 
390
MATCHER_P(IsValidFB, fb_id_container, "") { return fb_id_container->check_fb_id(arg); }
 
391
 
 
392
}
 
393
 
 
394
TEST_F(MesaDisplayMultiMonitorTest, create_display_uses_different_drm_fbs_for_side_by_side)
 
395
{
 
396
    using namespace testing;
 
397
 
 
398
    int const num_connected_outputs{3};
 
399
    int const num_disconnected_outputs{2};
 
400
    uint32_t const base_fb_id{66};
 
401
    FBIDContainer fb_id_container{base_fb_id};
 
402
 
 
403
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
404
 
 
405
    /* Create DRM FBs */
 
406
    EXPECT_CALL(mock_drm, drmModeAddFB(mock_drm.fake_drm.fd(),
 
407
                                       _, _, _, _, _, _, _))
 
408
        .Times(num_connected_outputs)
 
409
        .WillRepeatedly(Invoke(&fb_id_container, &FBIDContainer::add_fb));
 
410
 
 
411
    ExpectationSet crtc_setups;
 
412
 
 
413
    /* All crtcs are set */
 
414
    for (int i = 0; i < num_connected_outputs; i++)
 
415
    {
 
416
        crtc_setups += EXPECT_CALL(mock_drm,
 
417
                                   drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
418
                                                  crtc_ids[i],
 
419
                                                  IsValidFB(&fb_id_container),
 
420
                                                  _, _,
 
421
                                                  Pointee(connector_ids[i]),
 
422
                                                  _, _))
 
423
                           .Times(AtLeast(1));
 
424
    }
 
425
 
 
426
    /* All crtcs are restored at teardown */
 
427
    for (int i = 0; i < num_connected_outputs; i++)
 
428
    {
 
429
        EXPECT_CALL(mock_drm, drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
430
                                             crtc_ids[i], 0,
 
431
                                             _, _,
 
432
                                             Pointee(connector_ids[i]),
 
433
                                             _, _))
 
434
            .Times(1)
 
435
            .After(crtc_setups);
 
436
    }
 
437
 
 
438
    auto display = create_display_side_by_side(create_platform());
 
439
}
 
440
 
 
441
TEST_F(MesaDisplayMultiMonitorTest, configure_clears_unused_connected_outputs)
 
442
{
 
443
    using namespace testing;
 
444
 
 
445
    int const num_connected_outputs{3};
 
446
    int const num_disconnected_outputs{2};
 
447
 
 
448
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
449
 
 
450
    auto display = create_display_cloned(create_platform());
 
451
 
 
452
    Mock::VerifyAndClearExpectations(&mock_drm);
 
453
 
 
454
    /* All unused connected outputs are cleared */
 
455
    for (int i = 0; i < num_connected_outputs; i++)
 
456
    {
 
457
        EXPECT_CALL(mock_drm,
 
458
                    drmModeSetCursor(mock_drm.fake_drm.fd(),
 
459
                                     crtc_ids[i], 0, 0, 0))
 
460
                        .Times(1);
 
461
        EXPECT_CALL(mock_drm,
 
462
                    drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
463
                                   crtc_ids[i], 0, 0, 0,
 
464
                                   nullptr, 0, nullptr))
 
465
                        .Times(1);
 
466
    }
 
467
 
 
468
    /* Set all outputs to unused */
 
469
    auto conf = display->configuration();
 
470
 
 
471
    conf->for_each_output(
 
472
        [&](mg::DisplayConfigurationOutput const& conf_output)
 
473
        {
 
474
            conf->configure_output(conf_output.id, false, conf_output.top_left,
 
475
                                   conf_output.preferred_mode_index, mir_power_mode_on);
 
476
        });
 
477
 
 
478
    display->configure(*conf);
 
479
 
 
480
    Mock::VerifyAndClearExpectations(&mock_drm);
 
481
 
 
482
    /* All crtcs are restored at teardown */
 
483
    for (int i = 0; i < num_connected_outputs; i++)
 
484
    {
 
485
        EXPECT_CALL(mock_drm,
 
486
                    drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i],
 
487
                                   0, _, _, Pointee(connector_ids[i]),
 
488
                                   _, _))
 
489
                        .Times(1);
 
490
    }
 
491
}
 
492
 
 
493
TEST_F(MesaDisplayMultiMonitorTest, resume_clears_unused_connected_outputs)
 
494
{
 
495
    using namespace testing;
 
496
 
 
497
    int const num_connected_outputs{3};
 
498
    int const num_disconnected_outputs{2};
 
499
 
 
500
    setup_outputs(num_connected_outputs, num_disconnected_outputs);
 
501
 
 
502
    auto display = create_display_cloned(create_platform());
 
503
 
 
504
    /* Set all outputs to unused */
 
505
    auto conf = display->configuration();
 
506
 
 
507
    conf->for_each_output(
 
508
        [&](mg::DisplayConfigurationOutput const& conf_output)
 
509
        {
 
510
            conf->configure_output(conf_output.id, false, conf_output.top_left,
 
511
                                   conf_output.preferred_mode_index, mir_power_mode_on);
 
512
        });
 
513
 
 
514
    display->configure(*conf);
 
515
 
 
516
    display->pause();
 
517
 
 
518
    Mock::VerifyAndClearExpectations(&mock_drm);
 
519
 
 
520
    /* All unused connected outputs are cleared */
 
521
    for (int i = 0; i < num_connected_outputs; i++)
 
522
    {
 
523
        EXPECT_CALL(mock_drm,
 
524
                    drmModeSetCrtc(mock_drm.fake_drm.fd(),
 
525
                                   crtc_ids[i], 0, 0, 0,
 
526
                                   nullptr, 0, nullptr))
 
527
                        .Times(1);
 
528
    }
 
529
 
 
530
    display->resume();
 
531
 
 
532
    Mock::VerifyAndClearExpectations(&mock_drm);
 
533
 
 
534
    /* All crtcs are restored at teardown */
 
535
    for (int i = 0; i < num_connected_outputs; i++)
 
536
    {
 
537
        EXPECT_CALL(mock_drm,
 
538
                    drmModeSetCrtc(mock_drm.fake_drm.fd(), crtc_ids[i],
 
539
                                   0, _, _, Pointee(connector_ids[i]),
 
540
                                   _, _))
 
541
                        .Times(1);
 
542
    }
 
543
}