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
=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
--- src/server/compositor/multi_threaded_compositor.cpp 2014-09-06 14:28:26 +0000
+++ src/server/compositor/multi_threaded_compositor.cpp 2014-09-09 03:47:53 +0000
@@ -22,6 +22,7 @@
22#include "mir/compositor/display_buffer_compositor.h"22#include "mir/compositor/display_buffer_compositor.h"
23#include "mir/compositor/display_buffer_compositor_factory.h"23#include "mir/compositor/display_buffer_compositor_factory.h"
24#include "mir/compositor/scene.h"24#include "mir/compositor/scene.h"
25#include "mir/compositor/scene_element.h"
25#include "mir/compositor/compositor_report.h"26#include "mir/compositor/compositor_report.h"
26#include "mir/scene/legacy_scene_change_notification.h"27#include "mir/scene/legacy_scene_change_notification.h"
27#include "mir/scene/surface_observer.h"28#include "mir/scene/surface_observer.h"
@@ -60,6 +61,26 @@
60 std::function<void()> const apply;61 std::function<void()> const apply;
61};62};
6263
64void drop_frames(mc::Scene& scene, int nframes)
65{
66 // Consume nframes+1. The +1 is required for force the multi-monitor
67 // frame sync logic to release old buffers. It will only do so on the
68 // second and subsequent calls for a given CompositorID (0).
69
70 for (int f = 0; f < nframes + 1; ++f)
71 {
72 auto const& elements = scene.scene_elements_for(0);
73 for (auto& element : elements)
74 (void)element->renderable()->buffer();
75 }
76}
77
78/*
79 * Annoyingly, we can't use milliseconds::max() because that's actually
80 * negative and will trigger immediate wakeups (!?)
81 */
82std::chrono::milliseconds const forever(INT_MAX);
83
63}84}
6485
65namespace mir86namespace mir
@@ -123,8 +144,14 @@
123 std::unique_lock<std::mutex> lock{run_mutex};144 std::unique_lock<std::mutex> lock{run_mutex};
124 while (running)145 while (running)
125 {146 {
126 /* Wait until compositing has been scheduled or we are stopped */147 while (running && !frames_scheduled)
127 run_cv.wait(lock, [&]{ return (frames_scheduled > 0) || !running; });148 {
149 auto status = run_cv.wait_for(lock, timeout);
150 if (!frames_scheduled && status == std::cv_status::timeout)
151 frames_scheduled = deferred_frames_scheduled;
152 }
153
154 timeout = forever;
128155
129 /*156 /*
130 * Check if we are running before compositing, since we may have157 * Check if we are running before compositing, since we may have
@@ -153,11 +180,20 @@
153 mir::terminate_with_current_exception();180 mir::terminate_with_current_exception();
154 }181 }
155182
156 void schedule_compositing(int num_frames)183 void schedule_compositing(int num_frames,
184 std::chrono::milliseconds delay =
185 std::chrono::milliseconds::zero())
157 {186 {
158 std::lock_guard<std::mutex> lock{run_mutex};187 std::lock_guard<std::mutex> lock{run_mutex};
159188
160 if (num_frames > frames_scheduled)189 if (delay != std::chrono::milliseconds::zero())
190 {
191 timeout = delay;
192 deferred_frames_scheduled = num_frames;
193 frames_scheduled = 0;
194 run_cv.notify_one();
195 }
196 else if (num_frames > frames_scheduled)
161 {197 {
162 frames_scheduled = num_frames;198 frames_scheduled = num_frames;
163 run_cv.notify_one();199 run_cv.notify_one();
@@ -176,6 +212,8 @@
176 mg::DisplayBuffer& buffer;212 mg::DisplayBuffer& buffer;
177 bool running;213 bool running;
178 int frames_scheduled;214 int frames_scheduled;
215 int deferred_frames_scheduled = 0;
216 std::chrono::milliseconds timeout = forever;
179 std::mutex run_mutex;217 std::mutex run_mutex;
180 std::condition_variable run_cv;218 std::condition_variable run_cv;
181 std::shared_ptr<CompositorReport> const report;219 std::shared_ptr<CompositorReport> const report;
@@ -189,13 +227,15 @@
189 std::shared_ptr<mc::Scene> const& scene,227 std::shared_ptr<mc::Scene> const& scene,
190 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,228 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
191 std::shared_ptr<CompositorReport> const& compositor_report,229 std::shared_ptr<CompositorReport> const& compositor_report,
192 bool compose_on_start)230 bool compose_on_start,
231 std::chrono::milliseconds restart_delay)
193 : display{display},232 : display{display},
194 scene{scene},233 scene{scene},
195 display_buffer_compositor_factory{db_compositor_factory},234 display_buffer_compositor_factory{db_compositor_factory},
196 report{compositor_report},235 report{compositor_report},
197 state{CompositorState::stopped},236 state{CompositorState::stopped},
198 compose_on_start{compose_on_start},237 compose_on_start{compose_on_start},
238 restart_delay{restart_delay},
199 thread_pool{1}239 thread_pool{1}
200{240{
201 observer = std::make_shared<ms::LegacySceneChangeNotification>(241 observer = std::make_shared<ms::LegacySceneChangeNotification>(
@@ -214,13 +254,14 @@
214 stop();254 stop();
215}255}
216256
217void mc::MultiThreadedCompositor::schedule_compositing(int num)257void mc::MultiThreadedCompositor::schedule_compositing(int num,
258 std::chrono::milliseconds delay)
218{259{
219 std::unique_lock<std::mutex> lk(state_guard);260 std::unique_lock<std::mutex> lk(state_guard);
220261
221 report->scheduled();262 report->scheduled();
222 for (auto& f : thread_functors)263 for (auto& f : thread_functors)
223 f->schedule_compositing(num);264 f->schedule_compositing(num, delay);
224}265}
225266
226void mc::MultiThreadedCompositor::start()267void mc::MultiThreadedCompositor::start()
@@ -249,8 +290,11 @@
249 /* Optional first render */290 /* Optional first render */
250 if (compose_on_start)291 if (compose_on_start)
251 {292 {
293 int const max_buffer_queue_depth = 3;
294 drop_frames(*scene, max_buffer_queue_depth);
295
252 lk.unlock();296 lk.unlock();
253 schedule_compositing(1);297 schedule_compositing(1, restart_delay);
254 }298 }
255}299}
256300
257301
=== modified file 'src/server/compositor/multi_threaded_compositor.h'
--- src/server/compositor/multi_threaded_compositor.h 2014-09-06 14:28:26 +0000
+++ src/server/compositor/multi_threaded_compositor.h 2014-09-09 03:47:53 +0000
@@ -26,6 +26,8 @@
26#include <memory>26#include <memory>
27#include <vector>27#include <vector>
28#include <future>28#include <future>
29#include <thread>
30#include <chrono>
2931
30namespace mir32namespace mir
31{33{
@@ -61,7 +63,9 @@
61 std::shared_ptr<Scene> const& scene,63 std::shared_ptr<Scene> const& scene,
62 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,64 std::shared_ptr<DisplayBufferCompositorFactory> const& db_compositor_factory,
63 std::shared_ptr<CompositorReport> const& compositor_report,65 std::shared_ptr<CompositorReport> const& compositor_report,
64 bool compose_on_start);66 bool compose_on_start,
67 std::chrono::milliseconds restart_delay =
68 std::chrono::milliseconds(500));
65 ~MultiThreadedCompositor();69 ~MultiThreadedCompositor();
6670
67 void start();71 void start();
@@ -82,8 +86,10 @@
82 std::mutex state_guard;86 std::mutex state_guard;
83 CompositorState state;87 CompositorState state;
84 bool compose_on_start;88 bool compose_on_start;
89 std::chrono::milliseconds const restart_delay;
8590
86 void schedule_compositing(int number_composites);91 void schedule_compositing(int number_composites,
92 std::chrono::milliseconds delay = std::chrono::milliseconds::zero());
8793
88 std::shared_ptr<mir::scene::Observer> observer;94 std::shared_ptr<mir::scene::Observer> observer;
89 mir::thread::BasicThreadPool thread_pool;95 mir::thread::BasicThreadPool thread_pool;
9096
=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-09-05 03:37:19 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-09-09 03:47:53 +0000
@@ -608,6 +608,120 @@
608 compositor.stop();608 compositor.stop();
609}609}
610610
611TEST(MultiThreadedCompositor, restart_flushes_the_scene)
612{
613 using namespace testing;
614
615 int const ndisplays = 1;
616 int const buffer_queue_depth = 3;
617 auto display = std::make_shared<StubDisplayWithMockBuffers>(ndisplays);
618 auto mock_scene = std::make_shared<mtd::MockScene>();
619 auto factory = std::make_shared<NullDisplayBufferCompositorFactory>();
620 auto mock_report =
621 std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();
622
623 int const nstarts = 2;
624 int const nstops = nstarts;
625 EXPECT_CALL(*mock_report, started())
626 .Times(nstarts);
627 EXPECT_CALL(*mock_report, stopped())
628 .Times(nstops);
629 EXPECT_CALL(*mock_scene, add_observer(_))
630 .Times(nstarts);
631 EXPECT_CALL(*mock_scene, remove_observer(_))
632 .Times(nstarts);
633
634 EXPECT_CALL(*mock_scene, scene_elements_for(_))
635 .Times(nstarts * (buffer_queue_depth + 1))
636 .WillRepeatedly(Return(mc::SceneElementSequence{}));
637
638 mc::MultiThreadedCompositor compositor{display, mock_scene, factory,
639 mock_report, true};
640
641 compositor.start();
642 compositor.stop();
643 compositor.start();
644 compositor.stop();
645}
646
647TEST(MultiThreadedCompositor, resume_on_idle_scene_is_delayed)
648{
649 using namespace testing;
650
651 unsigned int const nbuffers = 3;
652
653 auto display = std::make_shared<StubDisplay>(nbuffers);
654 auto scene = std::make_shared<StubScene>();
655 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
656
657 std::chrono::milliseconds const delay(500);
658 mc::MultiThreadedCompositor compositor{display, scene, factory,
659 null_report, true, delay};
660
661 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
662
663 compositor.start();
664
665 scene->emit_change_event();
666 while (!factory->check_record_count_for_each_buffer(nbuffers, 1))
667 std::this_thread::sleep_for(std::chrono::milliseconds(10));
668 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 1, 1));
669
670 compositor.stop();
671
672 using namespace std::chrono;
673 auto from = system_clock::now();
674
675 compositor.start();
676 while (!factory->check_record_count_for_each_buffer(nbuffers, 2))
677 std::this_thread::sleep_for(std::chrono::milliseconds(10));
678 auto to = system_clock::now();
679 auto measured_delay = duration_cast<milliseconds>(to - from);
680 EXPECT_THAT(measured_delay, Ge(delay));
681
682 compositor.stop();
683}
684
685TEST(MultiThreadedCompositor, resume_on_busy_scene_is_not_delayed)
686{
687 using namespace testing;
688
689 unsigned int const nbuffers = 3;
690
691 auto display = std::make_shared<StubDisplay>(nbuffers);
692 auto scene = std::make_shared<StubScene>();
693 auto factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
694 std::chrono::milliseconds const long_delay(10000);
695 std::chrono::milliseconds const max_frame_time(1000); // Valgrind :)
696 ASSERT_LT(max_frame_time, long_delay);
697 mc::MultiThreadedCompositor compositor{display, scene, factory,
698 null_report, true, long_delay};
699
700 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 0, 0));
701
702 compositor.start();
703
704 scene->emit_change_event();
705 while (!factory->check_record_count_for_each_buffer(nbuffers, 1))
706 std::this_thread::sleep_for(std::chrono::milliseconds(10));
707 EXPECT_TRUE(factory->check_record_count_for_each_buffer(nbuffers, 1, 1));
708
709 compositor.stop();
710
711 using namespace std::chrono;
712 auto from = system_clock::now();
713
714 compositor.start();
715 scene->emit_change_event();
716 while (!factory->check_record_count_for_each_buffer(nbuffers, 2))
717 std::this_thread::sleep_for(std::chrono::milliseconds(10));
718 auto to = system_clock::now();
719 auto measured_delay = duration_cast<milliseconds>(to - from);
720 EXPECT_THAT(measured_delay, Lt(max_frame_time));
721
722 compositor.stop();
723}
724
611TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start)725TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start)
612{726{
613 unsigned int const nbuffers{3};727 unsigned int const nbuffers{3};

Subscribers

People subscribed via source and target branches