Mir

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

Proposed by Daniel van Vugt
Status: Superseded
Proposed branch: lp:~vanvugt/mir/judder
Merge into: lp:mir
Diff against target: 276 lines (+156/-35)
3 files modified
src/server/compositor/multi_threaded_compositor.cpp (+20/-27)
tests/acceptance-tests/test_client_surface_swap_buffers.cpp (+3/-4)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+133/-4)
To merge this branch: bzr merge lp:~vanvugt/mir/judder
Reviewer Review Type Date Requested Status
Alan Griffiths Disapprove
PS Jenkins bot (community) continuous-integration Approve
Daniel van Vugt Abstain
Alexandros Frantzis (community) Needs Information
Review via email: mp+216694@code.launchpad.net

This proposal has been superseded by a proposal from 2014-05-28.

Commit message

Fix frame skipping/stealing/beating regressions (LP: #1308843) by ensuring
that fake frames don't occur if real frames are being composited.

This also happens to solve LP: #1308844.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

There's an intermittent failure in MirSurfaceSwapBuffersTest.swap_buffers_does_not_block_when_surface_is_not_composited due to stub class changes in r1567. That needs fixing.

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

This reintroduces eglSwapBuffers() blocking for buffers that are not rendered by the real compositors (i.e. for surfaces that are either occluded or off screen). That is, it only keeps eglSwapBuffers() running when all monitors are off. This can't be a long term solution if we want eglSwapBuffers() to be non-blocking in general, although perhaps it can serve as a workaround for our Qt 5.2 issues.

For a proper solution we need a more fine-grained approach, making decisions per renderable. Chris' approach https://code.launchpad.net/~raof/mir/1hz-rendering-always/+merge/216246 does this an the SwitchingBundle level, but we can achieve the same results at the MultiThreadedCompositor level. I will work on a proposal for that and we can decided on the best approach.

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

> Chris' approach https://code.launchpad.net/~raof/mir/1hz-rendering-always/+merge/216246
> does this an the SwitchingBundle level, but we can achieve the same results at the
> MultiThreadedCompositor level

You can find it here: https://code.launchpad.net/~afrantzis/mir/consume-only-not-rendered-buffers/+merge/216725

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

Alexandros:

I know. Hence https://code.launchpad.net/~vanvugt/mir/unocclude/+merge/216690

I think these two proposals are both safer and more elegant than the 1Hz branch.

lp:~vanvugt/mir/judder updated
1573. By Daniel van Vugt

Ensure load/increment is atomic

Revision history for this message
Chris Halse Rogers (raof) wrote :

I disagree that adding a fake display is more elegant than dropping frames after a timeout; the former is a hack, the latter is a direct implementation of the conceptual behaviour we desire.

I suspect that the 1Hz branch will be safer over time as code changes; it implements the client behaviour we care about in the code that implements client behaviour, rather than as a side-effect of the default compositor.

Hm. How does this interact with the Qt scenegraph thingy? Isn't that replacing the compositor?

That said, it might well be that this is safer than the 1Hz branch *right now*.

lp:~vanvugt/mir/judder updated
1574. By Daniel van Vugt

Fix failing test:
TEST_F(MirSurfaceSwapBuffersTest, swap_buffers_does_not_block_when_surface_is_not_composited)

due to it employing a fake display with a single fake output, yielding a
real compositing functor thread that was racing the fake compositor.

Revision history for this message
Daniel van Vugt (vanvugt) :
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

While this seems to have the desired effect it isn't an elegant solution. (I don't have an elegant solution [yet], but there are a lot of MPs around this area pulling in different directions. Maybe there's a nice solution trying to get out.)

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

After today's standup the consensus is that when SwitchingBundle cannot supply a buffer (before a timeout) we should to be explicitly dropping frames to provide one and not faking it by consuming the frames in the compositor. In broad terms that's Chris's approach - but we need to review the detail.

So, while I think the tests included here are valid, the changes are not the way we want to go forward.

review: Disapprove
lp:~vanvugt/mir/judder updated
1575. By Daniel van Vugt

Merge latest development-branch and fix conflicts.

1576. By Daniel van Vugt

Fix tabs -> spaces

1577. By Daniel van Vugt

More debug output

1578. By Daniel van Vugt

Remove more tabs

1579. By Daniel van Vugt

Remember to keep fake compositing as required

1580. By Daniel van Vugt

Remember to set the continuation flag correctly... even on frames when
we're not consuming frames.

1581. By Daniel van Vugt

Clean up

1582. By Daniel van Vugt

Merge latest development-branch

1583. By Daniel van Vugt

Fix missing brace, FTBFS

1584. By Daniel van Vugt

Tidy up

1585. By Daniel van Vugt

Adjust sleep to yield 10Hz client wakeups

1586. By Daniel van Vugt

Merge latest development-branch

1587. By Daniel van Vugt

Merge latest development-branch

Unmerged revisions

1587. By Daniel van Vugt

Merge latest development-branch

1586. By Daniel van Vugt

Merge latest development-branch

1585. By Daniel van Vugt

Adjust sleep to yield 10Hz client wakeups

1584. By Daniel van Vugt

Tidy up

1583. By Daniel van Vugt

Fix missing brace, FTBFS

1582. By Daniel van Vugt

Merge latest development-branch

1581. By Daniel van Vugt

Clean up

1580. By Daniel van Vugt

Remember to set the continuation flag correctly... even on frames when
we're not consuming frames.

1579. By Daniel van Vugt

Remember to keep fake compositing as required

1578. By Daniel van Vugt

Remove more tabs

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-04-17 01:34:35 +0000
+++ src/server/compositor/multi_threaded_compositor.cpp 2014-04-23 07:01:57 +0000
@@ -28,6 +28,7 @@
28#include <condition_variable>28#include <condition_variable>
29#include <cstdint>29#include <cstdint>
30#include <chrono>30#include <chrono>
31#include <atomic>
3132
32namespace mc = mir::compositor;33namespace mc = mir::compositor;
33namespace mg = mir::graphics;34namespace mg = mir::graphics;
@@ -81,6 +82,8 @@
81 mg::DisplayBuffer& buffer;82 mg::DisplayBuffer& buffer;
82};83};
8384
85std::atomic<int> real_frames(0);
86
84class CompositingFunctor87class CompositingFunctor
85{88{
86public:89public:
@@ -174,7 +177,12 @@
174 r.top_left.x.as_int(), r.top_left.y.as_int(),177 r.top_left.x.as_int(), r.top_left.y.as_int(),
175 report_id);178 report_id);
176179
177 run_compositing_loop([&] { return display_buffer_compositor->composite();});180 run_compositing_loop([&]
181 {
182 bool ret = display_buffer_compositor->composite();
183 real_frames.fetch_add(1);
184 return ret;
185 });
178 }186 }
179187
180private:188private:
@@ -196,40 +204,25 @@
196 run_compositing_loop(204 run_compositing_loop(
197 [this]205 [this]
198 {206 {
199 std::vector<std::shared_ptr<mg::Buffer>> saved_resources;207 int old_real_frames = real_frames;
200208
201 auto const& renderables = scene->generate_renderable_list();209 // Emulate a moderately slow fake display
202210 std::this_thread::sleep_for(std::chrono::milliseconds(100));
203 for (auto const& r : renderables)211
212 // Now only composite a fake frame if no real display has
213 // composited during the sleep.
214 if (real_frames == old_real_frames)
204 {215 {
205 if (r->visible())216 auto const& all = scene->generate_renderable_list();
206 saved_resources.push_back(r->buffer(this));217 for (auto const& r : all)
218 (void)r->buffer(this);
207 }219 }
208220
209 wait_until_next_fake_vsync();
210
211 return false;221 return false;
212 });222 });
213 }223 }
214224
215private:225private:
216 void wait_until_next_fake_vsync()
217 {
218 using namespace std::chrono;
219 typedef duration<int64_t, std::ratio<1, 60>> vsync_periods;
220
221 /* Truncate to vsync periods */
222 auto const previous_vsync =
223 duration_cast<vsync_periods>(steady_clock::now().time_since_epoch());
224 /* Convert back to a timepoint */
225 auto const previous_vsync_tp =
226 time_point<steady_clock, vsync_periods>{previous_vsync};
227 /* Next vsync time point */
228 auto const next_vsync = previous_vsync_tp + vsync_periods(1);
229
230 std::this_thread::sleep_until(next_vsync);
231 }
232
233 std::shared_ptr<mc::Scene> const scene;226 std::shared_ptr<mc::Scene> const scene;
234};227};
235228
236229
=== modified file 'tests/acceptance-tests/test_client_surface_swap_buffers.cpp'
--- tests/acceptance-tests/test_client_surface_swap_buffers.cpp 2014-04-10 07:44:47 +0000
+++ tests/acceptance-tests/test_client_surface_swap_buffers.cpp 2014-04-23 07:01:57 +0000
@@ -21,7 +21,7 @@
21#include "mir_test_framework/stubbed_server_configuration.h"21#include "mir_test_framework/stubbed_server_configuration.h"
22#include "mir_test_framework/in_process_server.h"22#include "mir_test_framework/in_process_server.h"
23#include "mir_test_framework/using_stub_client_platform.h"23#include "mir_test_framework/using_stub_client_platform.h"
24#include "mir_test_doubles/null_display_buffer_compositor_factory.h"24#include "mir_test_doubles/null_display.h"
25#include "mir_test/spin_wait.h"25#include "mir_test/spin_wait.h"
2626
27#include <gtest/gtest.h>27#include <gtest/gtest.h>
@@ -37,10 +37,9 @@
3737
38struct StubServerConfig : mtf::StubbedServerConfiguration38struct StubServerConfig : mtf::StubbedServerConfiguration
39{39{
40 auto the_display_buffer_compositor_factory()40 std::shared_ptr<mir::graphics::Display> the_display()
41 -> std::shared_ptr<mc::DisplayBufferCompositorFactory> override
42 {41 {
43 return std::make_shared<mtd::NullDisplayBufferCompositorFactory>();42 return std::make_shared<mtd::NullDisplay>();
44 }43 }
45};44};
4645
4746
=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-04-16 14:49:54 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-04-23 07:01:57 +0000
@@ -135,6 +135,55 @@
135 mg::RenderableList renderable_list;135 mg::RenderableList renderable_list;
136};136};
137137
138class StubDisplayBufferCompositor : public mc::DisplayBufferCompositor
139{
140public:
141 StubDisplayBufferCompositor(std::shared_ptr<mc::Scene> scene,
142 std::chrono::milliseconds frame_time)
143 : scene{scene}, frame_time{frame_time}
144 {
145 }
146
147 bool composite() override
148 {
149 auto const& all = scene->generate_renderable_list();
150 for (auto const& r : all)
151 r->buffer(this); // Consume a frame
152
153 std::this_thread::sleep_for(frame_time);
154 return false;
155 }
156
157private:
158 std::shared_ptr<mc::Scene> const scene;
159 std::chrono::milliseconds const frame_time;
160};
161
162
163class StubDisplayBufferCompositorFactory :
164 public mc::DisplayBufferCompositorFactory
165{
166public:
167 StubDisplayBufferCompositorFactory(std::shared_ptr<mc::Scene> scene,
168 int hz)
169 : scene{scene},
170 frame_time{std::chrono::milliseconds(1000 / hz)}
171 {
172 }
173
174 std::unique_ptr<mc::DisplayBufferCompositor>
175 create_compositor_for(mg::DisplayBuffer&) override
176 {
177 std::unique_ptr<mc::DisplayBufferCompositor> ret(
178 new StubDisplayBufferCompositor(scene, frame_time));
179 return ret;
180 }
181
182private:
183 std::shared_ptr<mc::Scene> const scene;
184 std::chrono::milliseconds const frame_time;
185};
186
138class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor187class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor
139{188{
140public:189public:
@@ -593,12 +642,12 @@
593{642{
594 using namespace testing;643 using namespace testing;
595644
596 unsigned int const nbuffers{2};
597 auto renderable = std::make_shared<BufferCountingRenderable>();645 auto renderable = std::make_shared<BufferCountingRenderable>();
598 auto display = std::make_shared<StubDisplay>(nbuffers);646
647 // Defining zero outputs is the simplest way to emulate what would
648 // happen if all your displays were blocked in swapping...
649 auto display = std::make_shared<StubDisplay>(0);
599 auto stub_scene = std::make_shared<StubScene>(mg::RenderableList{renderable});650 auto stub_scene = std::make_shared<StubScene>(mg::RenderableList{renderable});
600 // We use NullDisplayBufferCompositors to simulate DisplayBufferCompositors
601 // not rendering a renderable.
602 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();651 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();
603652
604 mc::MultiThreadedCompositor compositor{653 mc::MultiThreadedCompositor compositor{
@@ -623,6 +672,86 @@
623 compositor.stop();672 compositor.stop();
624}673}
625674
675TEST(MultiThreadedCompositor, never_steals_frames_from_real_displays)
676{
677 /*
678 * Verify dummy frames are consumed slower than any physical display would
679 * consume them, so that the two types of consumers don't race and don't
680 * visibly skip (LP: #1308843)
681 */
682 using namespace testing;
683
684 unsigned int const nbuffers{2};
685 auto renderable = std::make_shared<BufferCountingRenderable>();
686 auto display = std::make_shared<StubDisplay>(nbuffers);
687 auto stub_scene = std::make_shared<StubScene>(mg::RenderableList{renderable});
688 // We use NullDisplayBufferCompositors to simulate DisplayBufferCompositors
689 // not rendering a renderable.
690 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();
691
692 mc::MultiThreadedCompositor compositor{
693 display, stub_scene, db_compositor_factory, null_report, true};
694
695 compositor.start();
696
697 // Realistically I've only ever seen LCDs go as low as 40Hz. But some TV
698 // outputs will go as low as 24Hz (traditional movie frame rate). So we
699 // need to ensure the dummy consumer is slower than that...
700 int const min_real_refresh_rate = 20;
701
702 int const secs = 5;
703 auto const duration = std::chrono::seconds{secs};
704 mir::test::spin_wait_for_condition_or_timeout(
705 [&] { return renderable->buffers_requested() <= 1; },
706 duration);
707
708 auto const end = std::chrono::steady_clock::now() + duration;
709 while (std::chrono::steady_clock::now() < end)
710 {
711 stub_scene->emit_change_event();
712 std::this_thread::yield();
713 }
714
715 int const max_dummy_frames = min_real_refresh_rate * secs;
716 ASSERT_GT(max_dummy_frames, renderable->buffers_requested());
717
718 compositor.stop();
719}
720
721TEST(MultiThreadedCompositor, only_real_displays_limit_consumption_rate)
722{
723 using namespace testing;
724
725 auto display = std::make_shared<StubDisplay>(1);
726 auto renderable = std::make_shared<BufferCountingRenderable>();
727 auto stub_scene = std::make_shared<StubScene>(mg::RenderableList{renderable});
728
729 int const framerate = 75; // Simulate a nice fast frame rate
730 auto db_compositor_factory =
731 std::make_shared<StubDisplayBufferCompositorFactory>(stub_scene,
732 framerate);
733
734 mc::MultiThreadedCompositor compositor{
735 display, stub_scene, db_compositor_factory, null_report, false};
736
737 compositor.start();
738
739 int const secs = 5;
740 auto const duration = std::chrono::seconds{secs};
741 auto const end = std::chrono::steady_clock::now() + duration;
742 while (std::chrono::steady_clock::now() < end)
743 {
744 stub_scene->emit_change_event();
745 std::this_thread::yield();
746 }
747
748 int const expected_frames = framerate * secs;
749 ASSERT_GT(expected_frames * 1.2f, renderable->buffers_requested());
750 ASSERT_LT(expected_frames * 0.8f, renderable->buffers_requested());
751
752 compositor.stop();
753}
754
626TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start)755TEST(MultiThreadedCompositor, cleans_up_after_throw_in_start)
627{756{
628 unsigned int const nbuffers{3};757 unsigned int const nbuffers{3};

Subscribers

People subscribed via source and target branches