Mir

Merge lp:~vanvugt/mir/flush into lp:mir

Proposed by Daniel van Vugt
Status: Work in progress
Proposed branch: lp:~vanvugt/mir/flush
Merge into: lp:mir
Diff against target: 295 lines (+174/-10)
3 files modified
src/server/compositor/multi_threaded_compositor.cpp (+52/-8)
src/server/compositor/multi_threaded_compositor.h (+8/-2)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+114/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/flush
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alberto Aguirre (community) Needs Information
Alexandros Frantzis (community) Needs Information
Kevin DuBois (community) Needs Fixing
Review via email: mp+230436@code.launchpad.net

Commit message

When restarting the compositor (including waking from sleep), flush
the scene and wait briefly for fresh frames. Then composite as soon as the
client (nested server) provides a new frame or the timeout of 500ms expires,
whichever happens first. This avoids the user seeing any stale frames on
wakeup.

This feature will also be useful in future as clients adjust to
display config changes seamlessly without visibly lagging behind the new
config (like resolution changes when a window is maximized).

Description of the change

This branch was intended to help with LP: #1340510. Although it's now clear that bug is more to do with Unity8/dbus and less to do with Mir. So this improvement to Mir isn't expected to solve LP: #1340510 by itself.

Still, this proposal is useful to have for other potential shells that respond to wakeup more quickly than Unity8 does right now.

To post a comment you must log in.
Revision history for this message
Kevin DuBois (kdub) wrote :

needs fixing:

166 + void schedule_compositing(int number_composites,
167 + std::chrono::milliseconds delay = std::chrono::milliseconds::zero());

default arguments ( http://unity.ubuntu.com/mir/cppguide/index.html?showone=Default_Arguments#Default_Arguments )

suggestion:
The solution isn't bad, but alternatively, we could have the objects implementing mir::DisplayChanger/mf::DisplayChanger trigger a display composition on wakeup. This could avoid messing with the timeouts.

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

Note that we already have support for dropping older frames when a surface becomes visible (restarting a compositor is a special case of this). The frame dropping supported was added by https://code.launchpad.net/~afrantzis/mir/drop-stale-frames/+merge/227961 as a partial fix for the same bug targetted by this MP.

The existing mechanism ensures that we use the latest client frame instead of rapidly showing stale queued frames. This MP is slightly different in that it tries to force the clients to redraw, then waits for some amount of time hoping that they have produced a newer frame. However, for the time intervals we are using ( > 100ms which is larger than our BufferQueues's framedropping policy timeout), I don't think forcing new frames will make much difference, and I am not sure if the complexity is worth it.

As you note, the real problem is the slow greeter rendering.

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

> This MP is slightly different in that it

Well not slightly different, rather just "different".

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 :

Yeah, I'm undecided about this branch now. The complexity is a bit ugly. I had a simpler version prepared but intentionally didn't propose it thinking more people would prefer this one.

Anyway, now my ABI problems are apparently resolved today I will do some system testing on the phone.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

Yeah I though this was already handled by the update to BufferQueue from Alexandros.

Do we need more than that?

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

It's a handy feature. And it is still required (after Unity8 gets fixed) to fully resolve bug 1340510, based on my own experiments inserting delays into clients. Not sure why Alexandros' existing code wasn't enough...? But either way, this solution scales better than putting too much intelligence in BufferQueue (which is instantiated for every surface).

So yes, I think we will still need something like this. But it's not needed right now.

lp:~vanvugt/mir/flush updated
1843. By Daniel van Vugt

Merge latest development-branch

1844. By Daniel van Vugt

Merge latest development-branch

1845. By Daniel van Vugt

Merge latest development-branch

1846. By Daniel van Vugt

Merge latest development-branch

1847. By Daniel van Vugt

Merge latest development-branch

1848. By Daniel van Vugt

Merge latest development-branch

1849. By Daniel van Vugt

Merge latest development-branch

1850. By Daniel van Vugt

Merge latest development-branch

1851. By Daniel van Vugt

Merge latest development-branch

1852. By Daniel van Vugt

Merge latest development-branch

1853. By Daniel van Vugt

Merge latest development-branch

1854. By Daniel van Vugt

Merge latest development-branch

1855. By Daniel van Vugt

Merge latest development-branch

1856. By Daniel van Vugt

Merge latest development-branch

1857. By Daniel van Vugt

Merge latest development-branch

1858. By Daniel van Vugt

Merge latest development-branch

1859. By Daniel van Vugt

Merge latest development-branch

1860. By Daniel van Vugt

Merge latest development-branch and fix conflicts

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Unmerged revisions

1860. By Daniel van Vugt

Merge latest development-branch and fix conflicts

1859. By Daniel van Vugt

Merge latest development-branch

1858. By Daniel van Vugt

Merge latest development-branch

1857. By Daniel van Vugt

Merge latest development-branch

1856. By Daniel van Vugt

Merge latest development-branch

1855. By Daniel van Vugt

Merge latest development-branch

1854. By Daniel van Vugt

Merge latest development-branch

1853. By Daniel van Vugt

Merge latest development-branch

1852. By Daniel van Vugt

Merge latest development-branch

1851. By Daniel van Vugt

Merge latest development-branch

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
2--- src/server/compositor/multi_threaded_compositor.cpp 2014-09-06 14:28:26 +0000
3+++ src/server/compositor/multi_threaded_compositor.cpp 2014-09-09 03:47:53 +0000
4@@ -22,6 +22,7 @@
5 #include "mir/compositor/display_buffer_compositor.h"
6 #include "mir/compositor/display_buffer_compositor_factory.h"
7 #include "mir/compositor/scene.h"
8+#include "mir/compositor/scene_element.h"
9 #include "mir/compositor/compositor_report.h"
10 #include "mir/scene/legacy_scene_change_notification.h"
11 #include "mir/scene/surface_observer.h"
12@@ -60,6 +61,26 @@
13 std::function<void()> const apply;
14 };
15
16+void drop_frames(mc::Scene& scene, int nframes)
17+{
18+ // Consume nframes+1. The +1 is required for force the multi-monitor
19+ // frame sync logic to release old buffers. It will only do so on the
20+ // second and subsequent calls for a given CompositorID (0).
21+
22+ for (int f = 0; f < nframes + 1; ++f)
23+ {
24+ auto const& elements = scene.scene_elements_for(0);
25+ for (auto& element : elements)
26+ (void)element->renderable()->buffer();
27+ }
28+}
29+
30+/*
31+ * Annoyingly, we can't use milliseconds::max() because that's actually
32+ * negative and will trigger immediate wakeups (!?)
33+ */
34+std::chrono::milliseconds const forever(INT_MAX);
35+
36 }
37
38 namespace mir
39@@ -123,8 +144,14 @@
40 std::unique_lock<std::mutex> lock{run_mutex};
41 while (running)
42 {
43- /* Wait until compositing has been scheduled or we are stopped */
44- run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });
45+ while (running && !frames_scheduled)
46+ {
47+ auto status = run_cv.wait_for(lock, timeout);
48+ if (!frames_scheduled && status == std::cv_status::timeout)
49+ frames_scheduled = deferred_frames_scheduled;
50+ }
51+
52+ timeout = forever;
53
54 /*
55 * Check if we are running before compositing, since we may have
56@@ -153,11 +180,20 @@
57 mir::terminate_with_current_exception();
58 }
59
60- void schedule_compositing(int num_frames)
61+ void schedule_compositing(int num_frames,
62+ std::chrono::milliseconds delay =
63+ std::chrono::milliseconds::zero())
64 {
65 std::lock_guard<std::mutex> lock{run_mutex};
66
67- if (num_frames > frames_scheduled)
68+ if (delay != std::chrono::milliseconds::zero())
69+ {
70+ timeout = delay;
71+ deferred_frames_scheduled = num_frames;
72+ frames_scheduled = 0;
73+ run_cv.notify_one();
74+ }
75+ else if (num_frames > frames_scheduled)
76 {
77 frames_scheduled = num_frames;
78 run_cv.notify_one();
79@@ -176,6 +212,8 @@
80 mg::DisplayBuffer& buffer;
81 bool running;
82 int frames_scheduled;
83+ int deferred_frames_scheduled = 0;
84+ std::chrono::milliseconds timeout = forever;
85 std::mutex run_mutex;
86 std::condition_variable run_cv;
87 std::shared_ptr<CompositorReport> const report;
88@@ -189,13 +227,15 @@
89 std::shared_ptr<mc::Scene> const& scene,
90 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
91 std::shared_ptr<CompositorReport> const& compositor_report,
92- bool compose_on_start)
93+ bool compose_on_start,
94+ std::chrono::milliseconds restart_delay)
95 : display{display},
96 scene{scene},
97 display_buffer_compositor_factory{db_compositor_factory},
98 report{compositor_report},
99 state{CompositorState::stopped},
100 compose_on_start{compose_on_start},
101+ restart_delay{restart_delay},
102 thread_pool{1}
103 {
104 observer = std::make_shared<ms::LegacySceneChangeNotification>(
105@@ -214,13 +254,14 @@
106 stop();
107 }
108
109-void mc::MultiThreadedCompositor::schedule_compositing(int num)
110+void mc::MultiThreadedCompositor::schedule_compositing(int num,
111+ std::chrono::milliseconds delay)
112 {
113 std::unique_lock<std::mutex> lk(state_guard);
114
115 report->scheduled();
116 for (auto& f : thread_functors)
117- f->schedule_compositing(num);
118+ f->schedule_compositing(num, delay);
119 }
120
121 void mc::MultiThreadedCompositor::start()
122@@ -249,8 +290,11 @@
123 /* Optional first render */
124 if (compose_on_start)
125 {
126+ int const max_buffer_queue_depth = 3;
127+ drop_frames(*scene, max_buffer_queue_depth);
128+
129 lk.unlock();
130- schedule_compositing(1);
131+ schedule_compositing(1, restart_delay);
132 }
133 }
134
135
136=== modified file 'src/server/compositor/multi_threaded_compositor.h'
137--- src/server/compositor/multi_threaded_compositor.h 2014-09-06 14:28:26 +0000
138+++ src/server/compositor/multi_threaded_compositor.h 2014-09-09 03:47:53 +0000
139@@ -26,6 +26,8 @@
140 #include <memory>
141 #include <vector>
142 #include <future>
143+#include <thread>
144+#include <chrono>
145
146 namespace mir
147 {
148@@ -61,7 +63,9 @@
149 std::shared_ptr<Scene> const& scene,
150 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
151 std::shared_ptr<CompositorReport> const& compositor_report,
152- bool compose_on_start);
153+ bool compose_on_start,
154+ std::chrono::milliseconds restart_delay =
155+ std::chrono::milliseconds(500));
156 ~MultiThreadedCompositor();
157
158 void start();
159@@ -82,8 +86,10 @@
160 std::mutex state_guard;
161 CompositorState state;
162 bool compose_on_start;
163+ std::chrono::milliseconds const restart_delay;
164
165- void schedule_compositing(int number_composites);
166+ void schedule_compositing(int number_composites,
167+ std::chrono::milliseconds delay = std::chrono::milliseconds::zero());
168
169 std::shared_ptr<mir::scene::Observer> observer;
170 mir::thread::BasicThreadPool thread_pool;
171
172=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
173--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-09-05 03:37:19 +0000
174+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-09-09 03:47:53 +0000
175@@ -608,6 +608,120 @@
176 compositor.stop();
177 }
178
179+TEST(MultiThreadedCompositor, restart_flushes_the_scene)
180+{
181+ using namespace testing;
182+
183+ int const ndisplays = 1;
184+ int const buffer_queue_depth = 3;
185+ auto display = std::make_shared<StubDisplayWithMockBuffers>(ndisplays);
186+ auto mock_scene = std::make_shared<mtd::MockScene>();
187+ auto factory = std::make_shared<NullDisplayBufferCompositorFactory>();
188+ auto mock_report =
189+ std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();
190+
191+ int const nstarts = 2;
192+ int const nstops = nstarts;
193+ EXPECT_CALL(*mock_report, started())
194+ .Times(nstarts);
195+ EXPECT_CALL(*mock_report, stopped())
196+ .Times(nstops);
197+ EXPECT_CALL(*mock_scene, add_observer(_))
198+ .Times(nstarts);
199+ EXPECT_CALL(*mock_scene, remove_observer(_))
200+ .Times(nstarts);
201+
202+ EXPECT_CALL(*mock_scene, scene_elements_for(_))
203+ .Times(nstarts * (buffer_queue_depth + 1))
204+ .WillRepeatedly(Return(mc::SceneElementSequence{}));
205+
206+ mc::MultiThreadedCompositor compositor{display, mock_scene, factory,
207+ mock_report, true};
208+
209+ compositor.start();
210+ compositor.stop();
211+ compositor.start();
212+ compositor.stop();
213+}
214+
215+TEST(MultiThreadedCompositor, resume_on_idle_scene_is_delayed)
216+{
217+ using namespace testing;
218+
219+ unsigned int const nbuffers = 3;
220+
221+ auto display = std::make_shared<StubDisplay>(nbuffers);
222+ auto scene = std::make_shared<StubScene>();
223+ auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
224+
225+ std::chrono::milliseconds const delay(500);
226+ mc::MultiThreadedCompositor compositor{display, scene, factory,
227+ null_report, true, delay};
228+
229+ EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
230+
231+ compositor.start();
232+
233+ scene->emit_change_event();
234+ while (!factory->check_record_count_for_each_buffer(nbuffers, 1))
235+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
236+ EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 1, 1));
237+
238+ compositor.stop();
239+
240+ using namespace std::chrono;
241+ auto from = system_clock::now();
242+
243+ compositor.start();
244+ while (!factory->check_record_count_for_each_buffer(nbuffers, 2))
245+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
246+ auto to = system_clock::now();
247+ auto measured_delay = duration_cast<milliseconds>(to - from);
248+ EXPECT_THAT(measured_delay, Ge(delay));
249+
250+ compositor.stop();
251+}
252+
253+TEST(MultiThreadedCompositor, resume_on_busy_scene_is_not_delayed)
254+{
255+ using namespace testing;
256+
257+ unsigned int const nbuffers = 3;
258+
259+ auto display = std::make_shared<StubDisplay>(nbuffers);
260+ auto scene = std::make_shared<StubScene>();
261+ auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
262+ std::chrono::milliseconds const long_delay(10000);
263+ std::chrono::milliseconds const max_frame_time(1000); // Valgrind :)
264+ ASSERT_LT(max_frame_time, long_delay);
265+ mc::MultiThreadedCompositor compositor{display, scene, factory,
266+ null_report, true, long_delay};
267+
268+ EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
269+
270+ compositor.start();
271+
272+ scene->emit_change_event();
273+ while (!factory->check_record_count_for_each_buffer(nbuffers, 1))
274+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
275+ EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 1, 1));
276+
277+ compositor.stop();
278+
279+ using namespace std::chrono;
280+ auto from = system_clock::now();
281+
282+ compositor.start();
283+ scene->emit_change_event();
284+ while (!factory->check_record_count_for_each_buffer(nbuffers, 2))
285+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
286+ auto to = system_clock::now();
287+ auto measured_delay = duration_cast<milliseconds>(to - from);
288+ EXPECT_THAT(measured_delay, Lt(max_frame_time));
289+
290+ compositor.stop();
291+}
292+
293 TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start)
294 {
295 unsigned int const nbuffers{3};

Subscribers

People subscribed via source and target branches