Mir

Merge lp:~alan-griffiths/mir/only-visible-surfaces-change-scene into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2614
Proposed branch: lp:~alan-griffiths/mir/only-visible-surfaces-change-scene
Merge into: lp:mir
Diff against target: 154 lines (+26/-13)
4 files modified
src/server/scene/legacy_scene_change_notification.cpp (+11/-3)
tests/integration-tests/test_surface_stack_with_compositor.cpp (+2/-2)
tests/unit-tests/scene/test_basic_surface.cpp (+5/-0)
tests/unit-tests/scene/test_legacy_scene_change_notification.cpp (+8/-8)
To merge this branch: bzr merge lp:~alan-griffiths/mir/only-visible-surfaces-change-scene
Reviewer Review Type Date Requested Status
Daniel van Vugt Approve
Alexandros Frantzis (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+260746@code.launchpad.net

Commit message

scene: surfaces that are not (and were not recently) visible shouldn't trigger compositing

Description of the change

scene: surfaces that are not (and were not recently) visible shouldn't trigger compositing

This is a significant part of the resolution to the discussion on the mir-devel mailing list "When should a nested server first post a buffer?"

We still need a test case for the nested server behaviour but this fixes the root cause (spurious "damage" triggering compositing).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

This proposal makes sense for improved efficiency at least, but I'm not sure if it improves correctness at all...

Theoretically the compositor should be treated as something that may sample the scene at any time, at any frequency. So briefly drawing a frame when there's not really any new changes to show shouldn't be an error. Unless the shell is broken and fails to draw itself on start-up when there are no active clients(?).

Feels like we're missing the mark on addressing the root cause. But also feels like a worthy change to have...

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> This proposal makes sense for improved efficiency at least, but I'm not sure
> if it improves correctness at all...
>
> Theoretically the compositor should be treated as something that may sample
> the scene at any time, at any frequency. So briefly drawing a frame when
> there's not really any new changes to show shouldn't be an error. Unless the
> shell is broken and fails to draw itself on start-up when there are no active
> clients(?).

Sure the compositor is permitted to sample the scene "when it wants". But it needs to decide how to respond to scene changes. The LegacySceneChangeNotification class is part of this decision mechanism. (It is called "Legacy" because the mechanism is knowingly unsophisticated - this MP makes it a little more sophisticated.)

> Feels like we're missing the mark on addressing the root cause. But also feels
> like a worthy change to have...

The "root cause" depends on what you think the problem is.

From the POV of USC the problem is that U8 posts a frame before it has drawn anything.

The reason U8 posts before it has drawn anything is that nested Mir posts a frame when the surface is created, not when it is drawn.

And that is fixed as a side-effect of the optimization proposed here.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Sounds OK, I think...

In the "already running" case you have to consider that there's possibly another client running that could cause you to get composited on-screen slightly sooner than you expect (or are ready for). Like in bug 1447896.

In Compiz I solved this by giving 1ms of grace time to the scene change notification so that temporally near-by changes in other windows make it into the same frame. Although the latency penalty is sub-optimal then, and my predictive-bypass branch will solve the problem completely.

Even on start-up though, if your shell is comprised of multiple surfaces there's no guarantee that the first visible frame will contain all of them. Because they race each other. But our logic does ensure in that case at least a second frame will replace it soon enough.

So we're potentially re-introducing the problem of having an incomplete shell visible on the first frame. But that's non-trivial to fix without the "wait for second frame" hack. Surfaces are asynchronous with each other and that's a very good thing, most of the time.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/scene/legacy_scene_change_notification.cpp'
2--- src/server/scene/legacy_scene_change_notification.cpp 2015-05-12 06:58:52 +0000
3+++ src/server/scene/legacy_scene_change_notification.cpp 2015-06-01 16:35:23 +0000
4@@ -41,8 +41,15 @@
5
6 void ms::LegacySceneChangeNotification::add_surface_observer(ms::Surface* surface)
7 {
8+ auto notifier = [surface, this, was_visible = false] () mutable
9+ {
10+ if (surface->visible() || was_visible)
11+ scene_notify_change();
12+ was_visible = surface->visible();
13+ };
14+
15 auto observer = std::make_shared<ms::LegacySurfaceChangeNotification>(
16- scene_notify_change, buffer_notify_change);
17+ notifier, buffer_notify_change);
18 surface->add_observer(observer);
19
20 {
21@@ -54,7 +61,6 @@
22 void ms::LegacySceneChangeNotification::surface_added(ms::Surface* surface)
23 {
24 add_surface_observer(surface);
25- scene_notify_change();
26 }
27
28 void ms::LegacySceneChangeNotification::surface_exists(ms::Surface* surface)
29@@ -73,7 +79,9 @@
30 surface_observers.erase(it);
31 }
32 }
33- scene_notify_change();
34+
35+ if (surface->visible())
36+ scene_notify_change();
37 }
38
39 void ms::LegacySceneChangeNotification::surfaces_reordered()
40
41=== modified file 'tests/integration-tests/test_surface_stack_with_compositor.cpp'
42--- tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-05-19 21:34:34 +0000
43+++ tests/integration-tests/test_surface_stack_with_compositor.cpp 2015-06-01 16:35:23 +0000
44@@ -183,7 +183,7 @@
45 EXPECT_TRUE(stub_secondary_db.has_posted_at_least(0, timeout));
46 }
47
48-TEST_F(SurfaceStackCompositor, adding_a_surface_that_has_been_swapped_triggers_a_composition)
49+TEST_F(SurfaceStackCompositor, swapping_a_surface_that_has_been_added_triggers_a_composition)
50 {
51 mc::MultiThreadedCompositor mt_compositor(
52 mt::fake_shared(stub_display),
53@@ -193,8 +193,8 @@
54 null_comp_report, false);
55 mt_compositor.start();
56
57+ stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
58 stub_surface->primary_buffer_stream()->swap_buffers(&stubbuf, [](mg::Buffer*){});
59- stack.add_surface(stub_surface, default_params.depth, default_params.input_mode);
60
61 EXPECT_TRUE(stub_primary_db.has_posted_at_least(1, timeout));
62 EXPECT_TRUE(stub_secondary_db.has_posted_at_least(1, timeout));
63
64=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
65--- tests/unit-tests/scene/test_basic_surface.cpp 2015-05-19 21:34:34 +0000
66+++ tests/unit-tests/scene/test_basic_surface.cpp 2015-06-01 16:35:23 +0000
67@@ -171,6 +171,7 @@
68 .Times(1);
69
70 surface.add_observer(observer);
71+ post_a_frame(surface);
72
73 EXPECT_EQ(rect.top_left, surface.top_left());
74
75@@ -187,6 +188,7 @@
76 .Times(1);
77
78 surface.add_observer(observer);
79+ post_a_frame(surface);
80
81 EXPECT_EQ(rect.size, surface.size());
82 EXPECT_NE(new_size, surface.size());
83@@ -223,6 +225,7 @@
84 .Times(1);
85
86 surface.add_observer(observer);
87+ post_a_frame(surface);
88
89 auto original_transformation = surface.compositor_snapshot(compositor_id)->transformation();
90 glm::mat4 trans{0.1f, 0.5f, 0.9f, 1.3f,
91@@ -243,6 +246,7 @@
92 .Times(1);
93
94 surface.add_observer(observer);
95+ post_a_frame(surface);
96
97 float alpha = 0.5f;
98 surface.set_alpha(0.5f);
99@@ -301,6 +305,7 @@
100 .Times(1);
101
102 surface.add_observer(observer);
103+ post_a_frame(surface);
104
105 surface.set_hidden(true);
106 }
107
108=== modified file 'tests/unit-tests/scene/test_legacy_scene_change_notification.cpp'
109--- tests/unit-tests/scene/test_legacy_scene_change_notification.cpp 2015-04-09 08:57:24 +0000
110+++ tests/unit-tests/scene/test_legacy_scene_change_notification.cpp 2015-06-01 16:35:23 +0000
111@@ -42,6 +42,10 @@
112
113 struct LegacySceneChangeNotificationTest : public testing::Test
114 {
115+ void SetUp() override
116+ {
117+ ON_CALL(surface,visible()).WillByDefault(testing::Return(true));
118+ }
119 testing::NiceMock<MockSceneCallback> scene_callback;
120 testing::NiceMock<MockBufferCallback> buffer_callback;
121 std::function<void(int)> buffer_change_callback{[this](int arg){buffer_callback.invoke(arg);}};
122@@ -52,8 +56,7 @@
123
124 TEST_F(LegacySceneChangeNotificationTest, fowards_all_observations_to_callback)
125 {
126- EXPECT_CALL(scene_callback, invoke())
127- .Times(3);
128+ EXPECT_CALL(scene_callback, invoke()).Times(2);
129
130 ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback);
131 observer.surface_added(&surface);
132@@ -87,10 +90,8 @@
133 .WillOnce(SaveArg<0>(&surface_observer));
134
135 int buffer_num{3};
136- EXPECT_CALL(scene_callback, invoke())
137- .Times(1);
138- EXPECT_CALL(buffer_callback, invoke(buffer_num))
139- .Times(1);
140+ EXPECT_CALL(scene_callback, invoke()).Times(0);
141+ EXPECT_CALL(buffer_callback, invoke(buffer_num)).Times(1);
142
143 ms::LegacySceneChangeNotification observer(scene_change_callback, buffer_change_callback);
144 observer.surface_added(&surface);
145@@ -105,8 +106,7 @@
146
147 EXPECT_CALL(surface, add_observer(_)).Times(1)
148 .WillOnce(SaveArg<0>(&surface_observer));
149- EXPECT_CALL(scene_callback, invoke())
150- .Times(2);
151+ EXPECT_CALL(scene_callback, invoke()).Times(1);
152
153 ms::LegacySceneChangeNotification observer(scene_change_callback,
154 buffer_change_callback);

Subscribers

People subscribed via source and target branches