Mir

Merge lp:~afrantzis/mir/drop-stale-frames into lp:mir

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1796
Proposed branch: lp:~afrantzis/mir/drop-stale-frames
Merge into: lp:mir
Diff against target: 531 lines (+315/-18)
15 files modified
include/server/mir/compositor/buffer_stream.h (+1/-0)
include/test/mir_test_doubles/mock_buffer_bundle.h (+1/-0)
include/test/mir_test_doubles/mock_buffer_stream.h (+1/-0)
include/test/mir_test_doubles/stub_buffer_stream.h (+2/-0)
server-ABI-sha1sums (+1/-1)
src/server/compositor/buffer_bundle.h (+1/-0)
src/server/compositor/buffer_queue.cpp (+30/-1)
src/server/compositor/buffer_queue.h (+2/-0)
src/server/compositor/buffer_stream_surfaces.cpp (+5/-0)
src/server/compositor/buffer_stream_surfaces.h (+1/-0)
src/server/scene/basic_surface.cpp (+2/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_stale_frames.cpp (+206/-0)
tests/integration-tests/test_swapinterval.cpp (+2/-16)
tests/unit-tests/compositor/test_buffer_queue.cpp (+59/-0)
To merge this branch: bzr merge lp:~afrantzis/mir/drop-stale-frames
Reviewer Review Type Date Requested Status
Kevin DuBois (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Robert Carr (community) Approve
Alan Griffiths Approve
Review via email: mp+227961@code.launchpad.net

Commit message

server: Drop stale frames when a surface becomes exposed

When a surface changes from occluded to exposed, at best there is no
point in showing older, stale frames in quick succession, at worst the
stale frames result in visual glitches. We only care about the latest
surface buffer.

Description of the change

server: Drop stale frames when a surface becomes exposed

When a surface changes from occluded to exposed, at best there is no
point in showing older, stale frames in quick succession, at worst the
stale frames result in visual glitches. We only care about the latest
surface buffer.

This fixes one aspect of https://bugs.launchpad.net/unity-system-compositor/+bug/1340510, the stale frame issue, the other aspect being the greeter rendering delay.

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
Alan Griffiths (alan-griffiths) wrote :

Nice

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

LGTM

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

lgtm too

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/compositor/buffer_stream.h'
2--- include/server/mir/compositor/buffer_stream.h 2014-05-22 10:00:05 +0000
3+++ include/server/mir/compositor/buffer_stream.h 2014-07-25 10:04:16 +0000
4@@ -53,6 +53,7 @@
5 virtual void allow_framedropping(bool) = 0;
6 virtual void force_requests_to_complete() = 0;
7 virtual int buffers_ready_for_compositor() const = 0;
8+ virtual void drop_old_buffers() = 0;
9 };
10
11 }
12
13=== modified file 'include/test/mir_test_doubles/mock_buffer_bundle.h'
14--- include/test/mir_test_doubles/mock_buffer_bundle.h 2014-05-21 05:30:50 +0000
15+++ include/test/mir_test_doubles/mock_buffer_bundle.h 2014-07-25 10:04:16 +0000
16@@ -48,6 +48,7 @@
17 MOCK_METHOD0(force_client_abort, void());
18 MOCK_METHOD0(force_requests_to_complete, void());
19 MOCK_METHOD1(resize, void(const geometry::Size &));
20+ MOCK_METHOD0(drop_old_buffers, void());
21 int buffers_ready_for_compositor() const override { return 1; }
22 int buffers_free_for_client() const override { return 1; }
23 };
24
25=== modified file 'include/test/mir_test_doubles/mock_buffer_stream.h'
26--- include/test/mir_test_doubles/mock_buffer_stream.h 2014-05-22 10:00:05 +0000
27+++ include/test/mir_test_doubles/mock_buffer_stream.h 2014-07-25 10:04:16 +0000
28@@ -57,6 +57,7 @@
29 MOCK_METHOD1(allow_framedropping, void(bool));
30 MOCK_METHOD0(force_requests_to_complete, void());
31 MOCK_CONST_METHOD0(buffers_ready_for_compositor, int());
32+ MOCK_METHOD0(drop_old_buffers, void());
33 };
34 }
35 }
36
37=== modified file 'include/test/mir_test_doubles/stub_buffer_stream.h'
38--- include/test/mir_test_doubles/stub_buffer_stream.h 2014-05-22 10:00:05 +0000
39+++ include/test/mir_test_doubles/stub_buffer_stream.h 2014-07-25 10:04:16 +0000
40@@ -81,6 +81,8 @@
41
42 int buffers_ready_for_compositor() const override { return 1; }
43
44+ void drop_old_buffers() override {}
45+
46 StubBuffer stub_client_buffer;
47 std::shared_ptr<graphics::Buffer> stub_compositor_buffer;
48 };
49
50=== modified file 'server-ABI-sha1sums'
51--- server-ABI-sha1sums 2014-07-24 11:44:53 +0000
52+++ server-ABI-sha1sums 2014-07-25 10:04:16 +0000
53@@ -9,7 +9,7 @@
54 ba79552edce545fafc9a4f401411c44d0cb3b2cf include/server/mir/graphics/gl_extensions_base.h
55 7ef3e99901168cda296d74d05a979f47bf9c3ff1 include/server/mir/server_action_queue.h
56 a3e8658107c0c13d41eaf2ca6a1be71d1dc59df6 include/server/mir/compositor/compositor_report.h
57-e439cc4a7fb8e975bed0771756e7949aad968c14 include/server/mir/compositor/buffer_stream.h
58+a4c8f0b0c30784ea6930a8ee6651f1d6efa47231 include/server/mir/compositor/buffer_stream.h
59 e787879afa46d9c8257ff1f4ae8c7900d4b5f1a4 include/server/mir/compositor/frame_dropping_policy.h
60 e0860c43bf5196a792ffd7216826925e16275d07 include/server/mir/compositor/renderer_factory.h
61 a9f284ba4b05d58fd3eeb628d1f56fe4ac188526 include/server/mir/compositor/compositor_id.h
62
63=== modified file 'src/server/compositor/buffer_bundle.h'
64--- src/server/compositor/buffer_bundle.h 2014-05-21 08:38:47 +0000
65+++ src/server/compositor/buffer_bundle.h 2014-07-25 10:04:16 +0000
66@@ -67,6 +67,7 @@
67 * nbuffers-1 as it might be less.
68 */
69 virtual int buffers_free_for_client() const = 0;
70+ virtual void drop_old_buffers() = 0;
71
72 protected:
73 BufferBundle() = default;
74
75=== modified file 'src/server/compositor/buffer_queue.cpp'
76--- src/server/compositor/buffer_queue.cpp 2014-07-21 03:35:31 +0000
77+++ src/server/compositor/buffer_queue.cpp 2014-07-25 10:04:16 +0000
78@@ -99,6 +99,7 @@
79 : nbuffers{nbuffers},
80 frame_dropping_enabled{false},
81 the_properties{props},
82+ force_new_compositor_buffer{false},
83 gralloc{gralloc}
84 {
85 if (nbuffers < 1)
86@@ -240,7 +241,16 @@
87 use_current_buffer = true;
88 current_buffer_users.push_back(user_id);
89 }
90- use_current_buffer |= ready_to_composite_queue.empty();
91+
92+ if (ready_to_composite_queue.empty())
93+ {
94+ use_current_buffer = true;
95+ }
96+ else if (force_new_compositor_buffer)
97+ {
98+ use_current_buffer = false;
99+ force_new_compositor_buffer = false;
100+ }
101
102 mg::Buffer* buffer_to_release = nullptr;
103 if (!use_current_buffer)
104@@ -473,3 +483,22 @@
105 }
106 give_buffer_to_client(buffer_to_give, std::move(lock));
107 }
108+
109+void mc::BufferQueue::drop_old_buffers()
110+{
111+ std::vector<mg::Buffer*> to_release;
112+
113+ {
114+ std::lock_guard<decltype(guard)> lock{guard};
115+ force_new_compositor_buffer = true;
116+
117+ while (ready_to_composite_queue.size() > 1)
118+ to_release.push_back(pop(ready_to_composite_queue));
119+ }
120+
121+ for (auto buffer : to_release)
122+ {
123+ std::unique_lock<decltype(guard)> lock{guard};
124+ release(buffer, std::move(lock));
125+ }
126+}
127
128=== modified file 'src/server/compositor/buffer_queue.h'
129--- src/server/compositor/buffer_queue.h 2014-07-17 10:28:51 +0000
130+++ src/server/compositor/buffer_queue.h 2014-07-25 10:04:16 +0000
131@@ -62,6 +62,7 @@
132 int buffers_free_for_client() const override;
133 bool framedropping_allowed() const;
134 bool is_a_current_buffer_user(void const* user_id) const;
135+ void drop_old_buffers() override;
136
137 private:
138 void give_buffer_to_client(graphics::Buffer* buffer,
139@@ -87,6 +88,7 @@
140 int nbuffers;
141 bool frame_dropping_enabled;
142 graphics::BufferProperties the_properties;
143+ bool force_new_compositor_buffer;
144
145 std::condition_variable snapshot_released;
146 std::shared_ptr<graphics::GraphicBufferAllocator> gralloc;
147
148=== modified file 'src/server/compositor/buffer_stream_surfaces.cpp'
149--- src/server/compositor/buffer_stream_surfaces.cpp 2014-05-22 10:00:05 +0000
150+++ src/server/compositor/buffer_stream_surfaces.cpp 2014-07-25 10:04:16 +0000
151@@ -89,3 +89,8 @@
152 {
153 return buffer_bundle->buffers_ready_for_compositor();
154 }
155+
156+void mc::BufferStreamSurfaces::drop_old_buffers()
157+{
158+ buffer_bundle->drop_old_buffers();
159+}
160
161=== modified file 'src/server/compositor/buffer_stream_surfaces.h'
162--- src/server/compositor/buffer_stream_surfaces.h 2014-05-22 10:00:05 +0000
163+++ src/server/compositor/buffer_stream_surfaces.h 2014-07-25 10:04:16 +0000
164@@ -52,6 +52,7 @@
165 void allow_framedropping(bool) override;
166 void force_requests_to_complete() override;
167 int buffers_ready_for_compositor() const override;
168+ void drop_old_buffers() override;
169
170 protected:
171 BufferStreamSurfaces(const BufferStreamSurfaces&) = delete;
172
173=== modified file 'src/server/scene/basic_surface.cpp'
174--- src/server/scene/basic_surface.cpp 2014-07-21 03:35:31 +0000
175+++ src/server/scene/basic_surface.cpp 2014-07-25 10:04:16 +0000
176@@ -583,6 +583,8 @@
177 {
178 attrib_values[mir_surface_attrib_visibility] = new_visibility;
179 lg.unlock();
180+ if (new_visibility == mir_surface_visibility_exposed)
181+ surface_buffer_stream->drop_old_buffers();
182 observers.attrib_changed(mir_surface_attrib_visibility, attrib_values[mir_surface_attrib_visibility]);
183 }
184
185
186=== modified file 'tests/acceptance-tests/CMakeLists.txt'
187--- tests/acceptance-tests/CMakeLists.txt 2014-07-22 03:26:35 +0000
188+++ tests/acceptance-tests/CMakeLists.txt 2014-07-25 10:04:16 +0000
189@@ -40,6 +40,7 @@
190 test_client_surface_visibility.cpp
191 test_client_with_custom_display_config_deadlock.cpp
192 test_macros.cpp
193+ test_stale_frames.cpp
194 ${GENERATED_PROTOBUF_SRCS}
195 ${GENERATED_PROTOBUF_HDRS}
196 )
197
198=== added file 'tests/acceptance-tests/test_stale_frames.cpp'
199--- tests/acceptance-tests/test_stale_frames.cpp 1970-01-01 00:00:00 +0000
200+++ tests/acceptance-tests/test_stale_frames.cpp 2014-07-25 10:04:16 +0000
201@@ -0,0 +1,206 @@
202+/*
203+ * Copyright © 2014 Canonical Ltd.
204+ *
205+ * This program is free software: you can redistribute it and/or modify it
206+ * under the terms of the GNU General Public License version 3,
207+ * as published by the Free Software Foundation.
208+ *
209+ * This program is distributed in the hope that it will be useful,
210+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
211+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
212+ * GNU General Public License for more details.
213+ *
214+ * You should have received a copy of the GNU General Public License
215+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
216+ *
217+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
218+ */
219+
220+#include "mir_toolkit/mir_client_library.h"
221+#include "mir_toolkit/mir_client_library_debug.h"
222+
223+#include "mir/compositor/compositor.h"
224+#include "mir/compositor/renderer_factory.h"
225+#include "mir/graphics/renderable.h"
226+#include "mir/graphics/buffer.h"
227+#include "mir/graphics/buffer_id.h"
228+
229+#include "mir_test_framework/using_stub_client_platform.h"
230+#include "mir_test_framework/stubbed_server_configuration.h"
231+#include "mir_test_framework/basic_client_server_fixture.h"
232+#include "mir_test_doubles/stub_renderer.h"
233+
234+#include <gtest/gtest.h>
235+#include <gmock/gmock.h>
236+
237+#include <mutex>
238+#include <condition_variable>
239+
240+namespace mtf = mir_test_framework;
241+namespace mtd = mir::test::doubles;
242+namespace mc = mir::compositor;
243+namespace mg = mir::graphics;
244+namespace geom = mir::geometry;
245+
246+namespace
247+{
248+
249+struct StubRenderer : mtd::StubRenderer
250+{
251+ void render(mg::RenderableList const& renderables) const override
252+ {
253+ std::lock_guard<std::mutex> lock{mutex};
254+ for (auto const& r : renderables)
255+ rendered_buffers_.push_back(r->buffer()->id());
256+
257+ if (renderables.size() > 0)
258+ new_rendered_buffer_cv.notify_all();
259+ }
260+
261+ std::vector<mg::BufferID> rendered_buffers()
262+ {
263+ std::lock_guard<std::mutex> lock{mutex};
264+ return rendered_buffers_;
265+ }
266+
267+ std::vector<mg::BufferID> wait_for_new_rendered_buffers()
268+ {
269+ std::unique_lock<std::mutex> lock{mutex};
270+
271+ new_rendered_buffer_cv.wait_for(
272+ lock, std::chrono::seconds{2},
273+ [this] { return rendered_buffers_.size() != 0; });
274+
275+ auto const rendered = std::move(rendered_buffers_);
276+ return rendered;
277+ }
278+
279+ mutable std::mutex mutex;
280+ mutable std::condition_variable new_rendered_buffer_cv;
281+ mutable std::vector<mg::BufferID> rendered_buffers_;
282+};
283+
284+class StubRendererFactory : public mc::RendererFactory
285+{
286+public:
287+ std::unique_ptr<mc::Renderer> create_renderer_for(
288+ geom::Rectangle const&, mc::DestinationAlpha) override
289+ {
290+ std::lock_guard<std::mutex> lock{mutex};
291+ renderer_ = new StubRenderer();
292+ renderer_created_cv.notify_all();
293+ return std::unique_ptr<mc::Renderer>{renderer_};
294+ }
295+
296+ StubRenderer* renderer()
297+ {
298+ std::unique_lock<std::mutex> lock{mutex};
299+
300+ renderer_created_cv.wait_for(
301+ lock, std::chrono::seconds{2},
302+ [this] { return renderer_ != nullptr; });
303+
304+ return renderer_;
305+ }
306+
307+ void clear_renderer()
308+ {
309+ std::lock_guard<std::mutex> lock{mutex};
310+ renderer_ = nullptr;
311+ }
312+
313+ std::mutex mutex;
314+ std::condition_variable renderer_created_cv;
315+ StubRenderer* renderer_ = nullptr;
316+};
317+
318+struct StubServerConfig : mtf::StubbedServerConfiguration
319+{
320+ std::shared_ptr<StubRendererFactory> the_stub_renderer_factory()
321+ {
322+ return stub_renderer_factory(
323+ [] { return std::make_shared<StubRendererFactory>(); });
324+ }
325+
326+ std::shared_ptr<mc::RendererFactory> the_renderer_factory() override
327+ {
328+ return the_stub_renderer_factory();
329+ }
330+
331+ mir::CachedPtr<StubRendererFactory> stub_renderer_factory;
332+};
333+
334+using BasicFixture = mtf::BasicClientServerFixture<StubServerConfig>;
335+
336+struct StaleFrames : BasicFixture
337+{
338+ void SetUp()
339+ {
340+ BasicFixture::SetUp();
341+
342+ client_create_surface();
343+ }
344+
345+ void TearDown()
346+ {
347+ mir_surface_release_sync(surface);
348+
349+ BasicFixture::TearDown();
350+ }
351+
352+ void client_create_surface()
353+ {
354+ MirSurfaceParameters const request_params =
355+ {
356+ __PRETTY_FUNCTION__,
357+ 640, 480,
358+ mir_pixel_format_abgr_8888,
359+ mir_buffer_usage_hardware,
360+ mir_display_output_id_invalid
361+ };
362+
363+ surface = mir_connection_create_surface_sync(connection, &request_params);
364+ ASSERT_TRUE(mir_surface_is_valid(surface));
365+ }
366+
367+ std::vector<mg::BufferID> wait_for_new_rendered_buffers()
368+ {
369+ return server_configuration.the_stub_renderer_factory()->renderer()->wait_for_new_rendered_buffers();
370+ }
371+
372+ void stop_compositor()
373+ {
374+ server_configuration.the_compositor()->stop();
375+ server_configuration.the_stub_renderer_factory()->clear_renderer();
376+ }
377+
378+ void start_compositor()
379+ {
380+ server_configuration.the_compositor()->start();
381+ }
382+
383+ MirSurface* surface;
384+ mtf::UsingStubClientPlatform using_stub_client_platform;
385+};
386+
387+}
388+
389+TEST_F(StaleFrames, are_dropped_when_restarting_compositor)
390+{
391+ using namespace testing;
392+
393+ stop_compositor();
394+
395+ auto const stale_buffer_id1 = mg::BufferID{mir_debug_surface_current_buffer_id(surface)};
396+ mir_surface_swap_buffers_sync(surface);
397+
398+ auto const stale_buffer_id2 = mg::BufferID{mir_debug_surface_current_buffer_id(surface)};
399+ mir_surface_swap_buffers_sync(surface);
400+
401+ mir_surface_swap_buffers_sync(surface);
402+
403+ start_compositor();
404+
405+ auto const new_buffers = wait_for_new_rendered_buffers();
406+ EXPECT_THAT(new_buffers, Not(AnyOf(Contains(stale_buffer_id1), Contains(stale_buffer_id2))));
407+}
408
409=== modified file 'tests/integration-tests/test_swapinterval.cpp'
410--- tests/integration-tests/test_swapinterval.cpp 2014-05-22 10:00:05 +0000
411+++ tests/integration-tests/test_swapinterval.cpp 2014-07-25 10:04:16 +0000
412@@ -28,6 +28,7 @@
413
414 #include "mir_test_framework/display_server_test_fixture.h"
415 #include "mir_test_doubles/stub_buffer.h"
416+#include "mir_test_doubles/stub_buffer_stream.h"
417
418 #include "mir_toolkit/mir_client_library.h"
419
420@@ -48,7 +49,7 @@
421 {
422 char const* const mir_test_socket = mtf::test_socket_file().c_str();
423
424-class CountingBufferStream : public mc::BufferStream
425+class CountingBufferStream : public mtd::StubBufferStream
426 {
427 public:
428 CountingBufferStream(int render_operations_fd)
429@@ -56,16 +57,6 @@
430 {
431 }
432
433- void acquire_client_buffer(
434- std::function<void(mg::Buffer* buffer)> complete) override
435- {
436- complete(&stub_buffer);
437- }
438-
439- void release_client_buffer(mg::Buffer*) override
440- {
441- }
442-
443 std::shared_ptr<mg::Buffer> lock_compositor_buffer(void const*) override
444 { return std::make_shared<mtd::StubBuffer>(); }
445
446@@ -75,18 +66,13 @@
447 MirPixelFormat get_stream_pixel_format() override
448 { return mir_pixel_format_abgr_8888; }
449
450- geom::Size stream_size() override { return geom::Size{}; }
451- void resize(geom::Size const&) override {}
452- void force_requests_to_complete() override {}
453 void allow_framedropping(bool) override
454 {
455 while (write(render_operations_fd, "a", 1) != 1) continue;
456 }
457- int buffers_ready_for_compositor() const override { return 1; }
458
459 private:
460 int render_operations_fd;
461- mtd::StubBuffer stub_buffer;
462 };
463
464 class StubStreamFactory : public ms::BufferStreamFactory
465
466=== modified file 'tests/unit-tests/compositor/test_buffer_queue.cpp'
467--- tests/unit-tests/compositor/test_buffer_queue.cpp 2014-07-11 05:14:40 +0000
468+++ tests/unit-tests/compositor/test_buffer_queue.cpp 2014-07-25 10:04:16 +0000
469@@ -1455,3 +1455,62 @@
470 q.compositor_release(comp);
471 }
472 }
473+
474+TEST_F(BufferQueueTest, gives_compositor_a_valid_buffer_after_dropping_old_buffers_without_clients)
475+{
476+ int const nbuffers = 3;
477+ mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory);
478+
479+ q.drop_old_buffers();
480+
481+ auto comp = q.compositor_acquire(this);
482+ ASSERT_THAT(comp->id(), Ne(mg::BufferID{}));
483+}
484+
485+TEST_F(BufferQueueTest, gives_compositor_the_newest_buffer_after_dropping_old_buffers)
486+{
487+ int const nbuffers = 3;
488+ mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory);
489+
490+ auto handle1 = client_acquire_async(q);
491+ ASSERT_THAT(handle1->has_acquired_buffer(), Eq(true));
492+ handle1->release_buffer();
493+
494+ auto handle2 = client_acquire_async(q);
495+ ASSERT_THAT(handle2->has_acquired_buffer(), Eq(true));
496+ handle2->release_buffer();
497+
498+ q.drop_old_buffers();
499+
500+ auto comp = q.compositor_acquire(this);
501+ ASSERT_THAT(comp->id(), Eq(handle2->id()));
502+ q.compositor_release(comp);
503+
504+ comp = q.compositor_acquire(this);
505+ ASSERT_THAT(comp->id(), Eq(handle2->id()));
506+}
507+
508+TEST_F(BufferQueueTest, gives_new_compositor_the_newest_buffer_after_dropping_old_buffers)
509+{
510+ int const nbuffers = 3;
511+ void const* const new_compositor_id{&nbuffers};
512+
513+ mc::BufferQueue q(nbuffers, allocator, basic_properties, policy_factory);
514+
515+ auto handle1 = client_acquire_async(q);
516+ ASSERT_THAT(handle1->has_acquired_buffer(), Eq(true));
517+ handle1->release_buffer();
518+
519+ auto comp = q.compositor_acquire(this);
520+ ASSERT_THAT(comp->id(), Eq(handle1->id()));
521+ q.compositor_release(comp);
522+
523+ auto handle2 = client_acquire_async(q);
524+ ASSERT_THAT(handle2->has_acquired_buffer(), Eq(true));
525+ handle2->release_buffer();
526+
527+ q.drop_old_buffers();
528+
529+ auto comp2 = q.compositor_acquire(new_compositor_id);
530+ ASSERT_THAT(comp2->id(), Eq(handle2->id()));
531+}

Subscribers

People subscribed via source and target branches