Mir

Merge lp:~alan-griffiths/mir/fix-1535894-alt into lp:mir

Proposed by Alan Griffiths
Status: Rejected
Rejected by: Alan Griffiths
Proposed branch: lp:~alan-griffiths/mir/fix-1535894-alt
Merge into: lp:mir
Diff against target: 984 lines (+181/-263)
26 files modified
examples/render_surfaces.cpp (+3/-2)
examples/server_example_adorning_compositor.cpp (+12/-1)
examples/server_example_adorning_compositor.h (+1/-1)
include/server/mir/compositor/display_buffer_compositor.h (+1/-1)
include/server/mir/compositor/scene.h (+0/-10)
include/server/mir/compositor/scene_element.h (+1/-0)
include/test/mir/test/doubles/null_display_buffer_compositor_factory.h (+2/-1)
playground/demo-shell/demo_compositor.cpp (+13/-1)
playground/demo-shell/demo_compositor.h (+1/-1)
src/server/compositor/default_display_buffer_compositor.cpp (+43/-46)
src/server/compositor/default_display_buffer_compositor.h (+1/-1)
src/server/compositor/multi_threaded_compositor.cpp (+3/-11)
src/server/scene/rendering_tracker.cpp (+9/-0)
src/server/scene/rendering_tracker.h (+1/-0)
src/server/scene/surface_stack.cpp (+10/-24)
src/server/scene/surface_stack.h (+0/-1)
tests/acceptance-tests/test_buffer_stream_arrangement.cpp (+2/-2)
tests/acceptance-tests/test_client_scaling.cpp (+2/-1)
tests/acceptance-tests/test_render_override.cpp (+3/-1)
tests/include/mir/test/doubles/stub_scene.h (+0/-4)
tests/include/mir/test/doubles/stub_scene_element.h (+5/-0)
tests/integration-tests/test_server_shutdown.cpp (+1/-1)
tests/unit-tests/compositor/test_compositing_screencast.cpp (+4/-3)
tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp (+44/-10)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+19/-22)
tests/unit-tests/scene/test_surface_stack.cpp (+0/-118)
To merge this branch: bzr merge lp:~alan-griffiths/mir/fix-1535894-alt
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir CI Bot continuous-integration Approve
Daniel van Vugt Needs Fixing
Review via email: mp+285223@code.launchpad.net

Commit message

compositor, scene: Use the DisplayBufferCompositor filtering to decide which surface have buffers that may still need compositing.

Description of the change

compositor, scene: Use the DisplayBufferCompositor filtering to decide which surface have buffers that may still need compositing.

This doesn't include logic that allows -c 3274 to be reverted, but if this approach is favoured, then I'll rework that to put the DisplayBufferCompositor(s) in control.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Errors look unrelated:

15:52:50 Setting up libuuid1:amd64 (2.27.1-1ubuntu4) ...
15:52:50 dpkg: error processing package libuuid1:amd64 (--configure):
15:52:50 subprocess installed post-installation script returned error exit status 1
15:52:50 Processing triggers for libc-bin (2.21-0ubuntu5) ...
15:52:51 Errors were encountered while processing:
15:52:51 libuuid1:amd64
15:52:51 W: No sandbox user '_apt' on the system, can not drop privileges
15:52:51 E: Sub-process /usr/bin/dpkg returned an error code (1)
15:52:51 apt-get failed.
15:52:51 Package installation failed

15:52:55 dpkg: error processing package libuuid1:i386 (--configure):
15:52:55 subprocess installed post-installation script returned error exit status 1
15:52:55 Processing triggers for libc-bin (2.21-0ubuntu5) ...
15:52:55 Errors were encountered while processing:
15:52:55 libuuid1:i386
15:52:55 W: No sandbox user '_apt' on the system, can not drop privileges
15:52:55 E: Sub-process /usr/bin/dpkg returned an error code (1)
15:52:55 apt-get failed.
15:52:55 Package installation failed

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3300
https://mir-jenkins.ubuntu.com/job/mir-ci/255/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build/43
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/49
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/45
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/45
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=amd64,compiler=gcc,platform=mesa,release=xenial/45
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=amd64,compiler=gcc,platform=mesa,release=xenial/45/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/45
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/45/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/45
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/45/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=mesa,release=xenial/45
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=mesa,release=xenial/45/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/255/rebuild

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

PASSED: Continuous integration, rev:3300
http://jenkins.qa.ubuntu.com/job/mir-ci/6231/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5831
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4738
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5787
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/404/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/555
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/555/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/555
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/555/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5784
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5784/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8189
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27394
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/400
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/400/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/254/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27397

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6231/rebuild

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

1. Needs info: I thought the goal here was to skip rendering on idle displays. That code doesn't seem to be present yet, so maybe it comes later? It would be: composite() returns "I had nothing new to render" so then post() never gets called. You either need to composite and post, or do neither. But never composite without post (wasted of GPU), and never post without composite (uninitialized pixels appear on screen).

Although that raises an important question: Is the bug safe to fix at all under the new display groups architecture? How do we safely tell Android to only post one of the displays in the group but not the other? I'm wondering if it's even possible to fix this under the display group approach... because you must either have one composite and one post, or zero of all.

2. Needs fixing: Please add the FIXME comment back in. It's an important one we shouldn't forget.

I'm also nervous that we're rewriting so much code that covers years of regression fixes. But that's not quite a blocking issue.

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

3. This looks like important code that will cause regressions if removed:
- int result = scene_changed ? 1 : 0;

That tells us to redraw the screen if a window has been dragged, but no windows contents changed. Obviously was missing a test.

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

OK, assuming Android has some magic workaround built-in to cope with posting a display group where only half of that group has been rendered, what we actually want instead of this branch is:

bool composited = false
for each compositor:
   composited |= compositor->composite();
if (composited)
   group.post();

That also means you can keep the frame counting logic where it is in the scene. No need to duplicate it into each compositor implementation.

But if Android can't handle half the monitors being rendered and then being told to post the whole group, then we actually can't safely do any of this at all...

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

> 1. Needs info: I thought the goal here was to skip rendering on idle displays.
> That code doesn't seem to be present yet, so maybe it comes later? It would
> be: composite() returns "I had nothing new to render" so then post() never
> gets called. You either need to composite and post, or do neither. But never
> composite without post (wasted of GPU), and never post without composite
> (uninitialized pixels appear on screen).

Nope.

The point of *this* MP is that the DisplayBufferCompositor knows which surfaces it has rendered and interrogates them to see if they have further buffers pending.

With the logic in scene we were counting buffers belonging to surfaces that had not contributed to rendering. (And would not be consumed by the consequent render pass.)

In the scenario from the linked bug the nested server would repeatedly schedule a recomposite of whichever display on which nothing had been posted.

> 2. Needs fixing: Please add the FIXME comment back in. It's an important one
> we shouldn't forget.

OK

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

> 3. This looks like important code that will cause regressions if removed:
> - int result = scene_changed ? 1 : 0;
>
> That tells us to redraw the screen if a window has been dragged, but no
> windows contents changed. Obviously was missing a test.

Obviously, this code duplicated the effect of CompositingFunctor::schedule_compositing(int num) and was redundant.

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

Oh cool. That all sounds like good news then. I'll need to re-evaluate later.

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

(1) Thanks for explaining. So I took some time today adding and analysing some debug logging to try and catch Mir in the act of repeatedly scheduling frames on some display but never consuming them. Unfortunately I can't yet reproduce such a busy wait but that could be just that I'm using desktop, or it could just be bad luck. So I'm assuming there's still a theoretical problem needing fixing.

But now I'm curious: You've clarified you're just fixing a busy wait. Or as I would say it "made Mir sleep properly". That's very good for power management, but only a small piece of the performance story. Even if this branch is working perfectly, you're still re-posting all displays on every frame (including the idle one) while any one of them is animating. So this is really a power management fix, but how is it an improvement to performance? You haven't changed the rendering load, and haven't reduced the number of posts either.

(2) Needs fixing: FIXME comment is still missing.

(4) I'm not yet confident this branch is working. As stated in (1) I can't yet put together a manual test case on desktop to reproduce any issue, and the diff below shows no new regression tests for anything getting fixed. So I think you need to try and write a regression test for the issue.

(5) In theory there's a much simpler solution you should consider. That is just make every call to BasicSurface::generate_renderables acquire its compositor buffer immediately, instead of the lazy acquisition it does in SurfaceSnapshot::buffer(). In theory that's a tiny change, it would solve the same problem, and is easier to test.

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

(1) "You haven't changed the rendering load, and haven't reduced the number of posts either" [while any display is changing anyway]

(5) Here's possible alternative solution:
https://code.launchpad.net/~mir-team/mir/compositing-always-finishes/+merge/285684

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

> but only a small piece of the performance story. Even if this branch is
> working perfectly, you're still re-posting all displays on every frame
> (including the idle one) while any one of them is animating.

Remember, this is in the nested server which has a CompositingFunctor for each output.

The earlier change (that you have valid concerns about) ensures that the CompositingFunctor doesn't run for changes that do not apply to its display buffers compositor.

> So this is really
> a power management fix, but how is it an improvement to performance? You
> haven't changed the rendering load, and haven't reduced the number of posts
> either.

The problem this addresses occurs whenever the "idle" CompositingFunctor runs. First it composites the surfaces visible to its DBC and then checks the scene for unconsumed frames. Updates on the other screen cause it to increment frames_scheduled and composite unnecessarily.

By only checking for unconsumed frames in the surfaces that have been rendered we avoid that extra pass.

> (2) Needs fixing: FIXME comment is still missing.
>
> (4) I'm not yet confident this branch is working. As stated in (1) I can't yet
> put together a manual test case on desktop to reproduce any issue, and the
> diff below shows no new regression tests for anything getting fixed. So I
> think you need to try and write a regression test for the issue.

Ack.

3301. By Alan Griffiths

merge lp:mir

3302. By Alan Griffiths

Reinstate FIXME comment

3303. By Alan Griffiths

Remove some unused headers

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

nm. -c 3301 seems to have addressed the problem I was seeing. (Not sure how, so I must have misinterpreted the system behaviour - we need better reporting out of the compositor.)

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3303
https://mir-jenkins.ubuntu.com/job/mir-ci/285/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build/73
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/79
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/75
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/75
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=amd64,compiler=gcc,platform=mesa,release=xenial/75
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=amd64,compiler=gcc,platform=mesa,release=xenial/75/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/75
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/75/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/75
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/75/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=mesa,release=xenial/75
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-advanced/arch=i386,compiler=gcc,platform=mesa,release=xenial/75/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/285/rebuild

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

PASSED: Continuous integration, rev:3303
http://jenkins.qa.ubuntu.com/job/mir-ci/6271/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5882
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4789
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5838
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/423
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/595
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/595/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/595
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/595/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5835
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5835/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8232
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27501
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/419
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/419/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/272
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27507

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6271/rebuild

review: Approve (continuous-integration)

Unmerged revisions

3303. By Alan Griffiths

Remove some unused headers

3302. By Alan Griffiths

Reinstate FIXME comment

3301. By Alan Griffiths

merge lp:mir

3300. By Alan Griffiths

Cleaner code in examples

3299. By Alan Griffiths

Cleaner code (that also passes StaleFrames.* tests)

3298. By Alan Griffiths

Add corresponding DefaultDisplayBufferCompositor tests

3297. By Alan Griffiths

Remove unnecessary Scene::frames_pending()

3296. By Alan Griffiths

Fixup broken test

3295. By Alan Griffiths

First pass at pending buffer count

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'examples/render_surfaces.cpp'
--- examples/render_surfaces.cpp 2016-01-29 08:18:22 +0000
+++ examples/render_surfaces.cpp 2016-02-11 14:50:57 +0000
@@ -241,7 +241,7 @@
241 {241 {
242 }242 }
243243
244 void composite(mc::SceneElementSequence&& scene_sequence) override244 int composite(mc::SceneElementSequence&& scene_sequence) override
245 {245 {
246 while (!created) std::this_thread::yield();246 while (!created) std::this_thread::yield();
247 stop_watch.stop();247 stop_watch.stop();
@@ -252,12 +252,13 @@
252 stop_watch.restart();252 stop_watch.restart();
253 }253 }
254254
255 db_compositor->composite(std::move(scene_sequence));255 auto const result = db_compositor->composite(std::move(scene_sequence));
256256
257 for (auto& m : moveables)257 for (auto& m : moveables)
258 m.step();258 m.step();
259259
260 frames++;260 frames++;
261 return result;
261 }262 }
262263
263private:264private:
264265
=== modified file 'examples/server_example_adorning_compositor.cpp'
--- examples/server_example_adorning_compositor.cpp 2016-02-08 17:58:09 +0000
+++ examples/server_example_adorning_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -174,7 +174,7 @@
174}174}
175}175}
176176
177void me::AdorningDisplayBufferCompositor::composite(compositor::SceneElementSequence&& scene_sequence)177int me::AdorningDisplayBufferCompositor::composite(compositor::SceneElementSequence&& scene_sequence)
178{178{
179 report->began_frame(this);179 report->began_frame(this);
180180
@@ -250,4 +250,15 @@
250 render_target->swap_buffers();250 render_target->swap_buffers();
251251
252 report->finished_frame(this);252 report->finished_frame(this);
253
254 auto max_pending_buffers = 0;
255 for (auto const& element : scene_sequence)
256 {
257 auto buffers_for_element = element->pending_buffers();
258
259 if (max_pending_buffers < buffers_for_element)
260 max_pending_buffers = buffers_for_element;
261 }
262
263 return max_pending_buffers;
253}264}
254265
=== modified file 'examples/server_example_adorning_compositor.h'
--- examples/server_example_adorning_compositor.h 2016-01-29 08:18:22 +0000
+++ examples/server_example_adorning_compositor.h 2016-02-11 14:50:57 +0000
@@ -40,7 +40,7 @@
40 graphics::DisplayBuffer&, std::tuple<float, float, float> const& background_rgb,40 graphics::DisplayBuffer&, std::tuple<float, float, float> const& background_rgb,
41 std::shared_ptr<compositor::CompositorReport> const& report);41 std::shared_ptr<compositor::CompositorReport> const& report);
4242
43 void composite(compositor::SceneElementSequence&& scene_sequence) override;43 int composite(compositor::SceneElementSequence&& scene_sequence) override;
44private:44private:
45 graphics::DisplayBuffer& db;45 graphics::DisplayBuffer& db;
46 renderer::gl::RenderTarget* const render_target;46 renderer::gl::RenderTarget* const render_target;
4747
=== modified file 'include/server/mir/compositor/display_buffer_compositor.h'
--- include/server/mir/compositor/display_buffer_compositor.h 2015-02-22 07:46:25 +0000
+++ include/server/mir/compositor/display_buffer_compositor.h 2016-02-11 14:50:57 +0000
@@ -32,7 +32,7 @@
32public:32public:
33 virtual ~DisplayBufferCompositor() = default;33 virtual ~DisplayBufferCompositor() = default;
3434
35 virtual void composite(SceneElementSequence&& scene_sequence) = 0;35 virtual int composite(SceneElementSequence&& scene_sequence) = 0;
3636
37protected:37protected:
38 DisplayBufferCompositor() = default;38 DisplayBufferCompositor() = default;
3939
=== modified file 'include/server/mir/compositor/scene.h'
--- include/server/mir/compositor/scene.h 2015-02-22 07:46:25 +0000
+++ include/server/mir/compositor/scene.h 2016-02-11 14:50:57 +0000
@@ -55,16 +55,6 @@
55 */55 */
56 virtual SceneElementSequence scene_elements_for(CompositorID id) = 0;56 virtual SceneElementSequence scene_elements_for(CompositorID id) = 0;
5757
58 /**
59 * Return the number of additional frames that you need to render to get
60 * fully up to date with the latest data in the scene. For a generic
61 * "scene change" this will be just 1. For surfaces that have multiple
62 * frames queued up however, it could be greater than 1. When the result
63 * reaches zero, you know you have consumed all the latest data from the
64 * scene.
65 */
66 virtual int frames_pending(CompositorID id) const = 0;
67
68 virtual void register_compositor(CompositorID id) = 0;58 virtual void register_compositor(CompositorID id) = 0;
69 virtual void unregister_compositor(CompositorID id) = 0;59 virtual void unregister_compositor(CompositorID id) = 0;
7060
7161
=== modified file 'include/server/mir/compositor/scene_element.h'
--- include/server/mir/compositor/scene_element.h 2015-06-17 05:20:42 +0000
+++ include/server/mir/compositor/scene_element.h 2016-02-11 14:50:57 +0000
@@ -38,6 +38,7 @@
38 virtual std::shared_ptr<graphics::Renderable> renderable() const = 0;38 virtual std::shared_ptr<graphics::Renderable> renderable() const = 0;
39 virtual void rendered() = 0;39 virtual void rendered() = 0;
40 virtual void occluded() = 0;40 virtual void occluded() = 0;
41 virtual int pending_buffers() const = 0;
4142
42 //TODO: Decoration is opaque on purpose. It is only used by an internal example,43 //TODO: Decoration is opaque on purpose. It is only used by an internal example,
43 // and this function should be removed from the public API.44 // and this function should be removed from the public API.
4445
=== modified file 'include/test/mir/test/doubles/null_display_buffer_compositor_factory.h'
--- include/test/mir/test/doubles/null_display_buffer_compositor_factory.h 2016-01-29 08:18:22 +0000
+++ include/test/mir/test/doubles/null_display_buffer_compositor_factory.h 2016-02-11 14:50:57 +0000
@@ -39,11 +39,12 @@
39 {39 {
40 struct NullDisplayBufferCompositor : compositor::DisplayBufferCompositor40 struct NullDisplayBufferCompositor : compositor::DisplayBufferCompositor
41 {41 {
42 void composite(compositor::SceneElementSequence&&)42 int composite(compositor::SceneElementSequence&&)
43 {43 {
44 // yield() is needed to ensure reasonable runtime under44 // yield() is needed to ensure reasonable runtime under
45 // valgrind for some tests45 // valgrind for some tests
46 std::this_thread::yield();46 std::this_thread::yield();
47 return 0;
47 }48 }
48 };49 };
4950
5051
=== modified file 'playground/demo-shell/demo_compositor.cpp'
--- playground/demo-shell/demo_compositor.cpp 2016-01-29 08:18:22 +0000
+++ playground/demo-shell/demo_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -58,7 +58,7 @@
58 f(*i);58 f(*i);
59}59}
6060
61void me::DemoCompositor::composite(mc::SceneElementSequence&& elements)61int me::DemoCompositor::composite(mc::SceneElementSequence&& elements)
62{62{
63 report->began_frame(this);63 report->began_frame(this);
64 //a simple filtering out of renderables that shouldn't be drawn64 //a simple filtering out of renderables that shouldn't be drawn
@@ -106,6 +106,16 @@
106 nonrenderlist_elements |= embellished;106 nonrenderlist_elements |= embellished;
107 }107 }
108108
109 auto max_pending_buffers = 0;
110 for (auto const& element : elements)
111 {
112 // We're about to render a buffer, so don't count that one
113 auto buffers_for_element = element->pending_buffers() - 1;
114
115 if (max_pending_buffers < buffers_for_element)
116 max_pending_buffers = buffers_for_element;
117 }
118
109 /*119 /*
110 * Note: Buffer lifetimes are ensured by the two objects holding120 * Note: Buffer lifetimes are ensured by the two objects holding
111 * references to them; elements and renderable_list.121 * references to them; elements and renderable_list.
@@ -141,6 +151,8 @@
141 }151 }
142152
143 report->finished_frame(this);153 report->finished_frame(this);
154
155 return max_pending_buffers;
144}156}
145157
146void me::DemoCompositor::on_cursor_movement(158void me::DemoCompositor::on_cursor_movement(
147159
=== modified file 'playground/demo-shell/demo_compositor.h'
--- playground/demo-shell/demo_compositor.h 2015-02-22 07:46:25 +0000
+++ playground/demo-shell/demo_compositor.h 2016-02-11 14:50:57 +0000
@@ -50,7 +50,7 @@
50 std::shared_ptr<compositor::CompositorReport> const& report);50 std::shared_ptr<compositor::CompositorReport> const& report);
51 ~DemoCompositor();51 ~DemoCompositor();
5252
53 void composite(compositor::SceneElementSequence&& elements) override;53 int composite(compositor::SceneElementSequence&& elements) override;
5454
55 void zoom(float mag);55 void zoom(float mag);
56 void on_cursor_movement(geometry::Point const& p);56 void on_cursor_movement(geometry::Point const& p);
5757
=== modified file 'src/server/compositor/default_display_buffer_compositor.cpp'
--- src/server/compositor/default_display_buffer_compositor.cpp 2016-01-29 08:18:22 +0000
+++ src/server/compositor/default_display_buffer_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2012 Canonical Ltd.2 * Copyright © 2012-2016 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,5 * under the terms of the GNU General Public License version 3,
@@ -19,16 +19,13 @@
1919
20#include "default_display_buffer_compositor.h"20#include "default_display_buffer_compositor.h"
2121
22#include "mir/compositor/scene.h"
23#include "mir/compositor/scene_element.h"22#include "mir/compositor/scene_element.h"
24#include "mir/compositor/renderer.h"23#include "mir/compositor/renderer.h"
25#include "mir/graphics/renderable.h"24#include "mir/graphics/renderable.h"
26#include "mir/graphics/display_buffer.h"25#include "mir/graphics/display_buffer.h"
27#include "mir/graphics/buffer.h"26#include "mir/graphics/buffer.h"
28#include "mir/compositor/buffer_stream.h"
29#include "occlusion.h"27#include "occlusion.h"
30#include <mutex>28
31#include <cstdlib>
32#include <algorithm>29#include <algorithm>
3330
34namespace mc = mir::compositor;31namespace mc = mir::compositor;
@@ -44,52 +41,52 @@
44{41{
45}42}
4643
47void mc::DefaultDisplayBufferCompositor::composite(mc::SceneElementSequence&& scene_elements)44int mc::DefaultDisplayBufferCompositor::composite(SceneElementSequence&& scene_elements)
48{45{
49 report->began_frame(this);46 report->began_frame(this);
5047
51 auto const& view_area = display_buffer.view_area();48 for (auto const& element : filter_occlusions_from(scene_elements, display_buffer.view_area()))
52 auto const& occlusions = mc::filter_occlusions_from(scene_elements, view_area);
53
54 for (auto const& element : occlusions)
55 element->occluded();49 element->occluded();
5650
57 mg::RenderableList renderable_list;51 {
58 renderable_list.reserve(scene_elements.size());52 mg::RenderableList renderable_list;
53 renderable_list.reserve(scene_elements.size());
54
55 transform(begin(scene_elements), end(scene_elements), back_inserter(renderable_list), [] (auto& element)
56 {
57 element->rendered();
58 return element->renderable();
59 });
60
61 if (display_buffer.post_renderables_if_optimizable(renderable_list))
62 {
63 report->renderables_in_frame(this, renderable_list);
64 renderer->suspend();
65 }
66 else
67 {
68 renderer->set_rotation(display_buffer.orientation());
69 renderer->render(renderable_list);
70
71 report->renderables_in_frame(this, renderable_list);
72 report->rendered_frame(this);
73 }
74 }
75
76 report->finished_frame(this);
77
78 auto max_pending_buffers = 0;
59 for (auto const& element : scene_elements)79 for (auto const& element : scene_elements)
60 {80 {
61 element->rendered();81 auto buffers_for_element = element->pending_buffers();
62 renderable_list.push_back(element->renderable());82
63 }83 if (max_pending_buffers < buffers_for_element)
6484 max_pending_buffers = buffers_for_element;
65 /*85 }
66 * Note: Buffer lifetimes are ensured by the two objects holding86
67 * references to them; scene_elements and renderable_list.87 return max_pending_buffers;
68 * So no buffer is going to be released back to the client till88
69 * both of those containers get destroyed (end of the function).89 // Returning destroys the local renderable_list and the temporary scene_elements which releases
70 * Actually, there's a third reference held by the texture cache90 // the buffers we did use back to the clients, before starting on the potentially slow flip().
71 * in GLRenderer, but that gets released earlier in render().91 // FIXME: releasing the buffers is blocking a little because we drive IPC here (LP: #1395421)
72 */
73 scene_elements.clear(); // Those in use are still in renderable_list
74
75 if (display_buffer.post_renderables_if_optimizable(renderable_list))
76 {
77 report->renderables_in_frame(this, renderable_list);
78 renderer->suspend();
79 }
80 else
81 {
82 renderer->set_rotation(display_buffer.orientation());
83 renderer->render(renderable_list);
84
85 report->renderables_in_frame(this, renderable_list);
86 report->rendered_frame(this);
87
88 // Release the buffers we did use back to the clients, before starting
89 // on the potentially slow flip().
90 // FIXME: This clear() call is blocking a little because we drive IPC here (LP: #1395421)
91 renderable_list.clear();
92 }
93
94 report->finished_frame(this);
95}92}
9693
=== modified file 'src/server/compositor/default_display_buffer_compositor.h'
--- src/server/compositor/default_display_buffer_compositor.h 2015-04-28 07:54:10 +0000
+++ src/server/compositor/default_display_buffer_compositor.h 2016-02-11 14:50:57 +0000
@@ -43,7 +43,7 @@
43 std::shared_ptr<Renderer> const& renderer,43 std::shared_ptr<Renderer> const& renderer,
44 std::shared_ptr<CompositorReport> const& report);44 std::shared_ptr<CompositorReport> const& report);
4545
46 void composite(SceneElementSequence&& scene_sequence) override;46 int composite(SceneElementSequence&& scene_sequence) override;
4747
48private:48private:
49 graphics::DisplayBuffer& display_buffer;49 graphics::DisplayBuffer& display_buffer;
5050
=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
--- src/server/compositor/multi_threaded_compositor.cpp 2016-02-09 16:40:57 +0000
+++ src/server/compositor/multi_threaded_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -131,14 +131,15 @@
131 * frames_scheduled indicates the number of frames that are scheduled131 * frames_scheduled indicates the number of frames that are scheduled
132 * to ensure all surfaces' queues are fully drained.132 * to ensure all surfaces' queues are fully drained.
133 */133 */
134 frames_scheduled--;134 int pending = --frames_scheduled;
135 not_posted_yet = false;135 not_posted_yet = false;
136 lock.unlock();136 lock.unlock();
137137
138 for (auto& tuple : compositors)138 for (auto& tuple : compositors)
139 {139 {
140 auto& compositor = std::get<1>(tuple);140 auto& compositor = std::get<1>(tuple);
141 compositor->composite(scene->scene_elements_for(compositor.get()));141 auto max_pending_frames = compositor->composite(scene->scene_elements_for(compositor.get()));
142 if (pending < max_pending_frames) pending = max_pending_frames;
142 }143 }
143 group.post();144 group.post();
144145
@@ -161,15 +162,6 @@
161 * important to re-count number of frames pending, separately162 * important to re-count number of frames pending, separately
162 * to the initial scene_elements_for()...163 * to the initial scene_elements_for()...
163 */164 */
164 int pending = 0;
165 for (auto& compositor : compositors)
166 {
167 auto const comp_id = std::get<1>(compositor).get();
168 int pend = scene->frames_pending(comp_id);
169 if (pend > pending)
170 pending = pend;
171 }
172
173 if (pending > frames_scheduled)165 if (pending > frames_scheduled)
174 frames_scheduled = pending;166 frames_scheduled = pending;
175 }167 }
176168
=== modified file 'src/server/scene/rendering_tracker.cpp'
--- src/server/scene/rendering_tracker.cpp 2016-01-29 08:18:22 +0000
+++ src/server/scene/rendering_tracker.cpp 2016-02-11 14:50:57 +0000
@@ -80,6 +80,15 @@
80 return occlusions.find(cid) == occlusions.end();80 return occlusions.find(cid) == occlusions.end();
81}81}
8282
83int ms::RenderingTracker::pending_buffers_for(compositor::CompositorID cid) const
84{
85 if (auto const surface = weak_surface.lock())
86 return surface->buffers_ready_for_compositor(cid);
87 else
88 return 0;
89}
90
91
83bool ms::RenderingTracker::occluded_in_all_active_compositors()92bool ms::RenderingTracker::occluded_in_all_active_compositors()
84{93{
85 return occlusions == active_compositors_;94 return occlusions == active_compositors_;
8695
=== modified file 'src/server/scene/rendering_tracker.h'
--- src/server/scene/rendering_tracker.h 2016-01-29 08:18:22 +0000
+++ src/server/scene/rendering_tracker.h 2016-02-11 14:50:57 +0000
@@ -43,6 +43,7 @@
43 void occluded_in(compositor::CompositorID cid);43 void occluded_in(compositor::CompositorID cid);
44 void active_compositors(std::set<compositor::CompositorID> const& cids);44 void active_compositors(std::set<compositor::CompositorID> const& cids);
45 bool is_exposed_in(compositor::CompositorID cid) const;45 bool is_exposed_in(compositor::CompositorID cid) const;
46 int pending_buffers_for(compositor::CompositorID cid) const;
4647
47private:48private:
48 bool occluded_in_all_active_compositors();49 bool occluded_in_all_active_compositors();
4950
=== modified file 'src/server/scene/surface_stack.cpp'
--- src/server/scene/surface_stack.cpp 2016-01-29 08:18:22 +0000
+++ src/server/scene/surface_stack.cpp 2016-02-11 14:50:57 +0000
@@ -73,6 +73,11 @@
73 tracker->occluded_in(cid);73 tracker->occluded_in(cid);
74 }74 }
7575
76 int pending_buffers() const override
77 {
78 return tracker->pending_buffers_for(cid);
79 }
80
76 std::unique_ptr<mc::Decoration> decoration() const override81 std::unique_ptr<mc::Decoration> decoration() const override
77 {82 {
78 return std::make_unique<mc::Decoration>(mc::Decoration::Type::surface, surface_name);83 return std::make_unique<mc::Decoration>(mc::Decoration::Type::surface, surface_name);
@@ -108,6 +113,11 @@
108 {113 {
109 }114 }
110115
116 int pending_buffers() const override
117 {
118 return 0;
119 }
120
111 std::unique_ptr<mc::Decoration> decoration() const override121 std::unique_ptr<mc::Decoration> decoration() const override
112 {122 {
113 return std::make_unique<mc::Decoration>();123 return std::make_unique<mc::Decoration>();
@@ -154,30 +164,6 @@
154 return elements;164 return elements;
155}165}
156166
157int ms::SurfaceStack::frames_pending(mc::CompositorID id) const
158{
159 RecursiveReadLock lg(guard);
160
161 int result = scene_changed ? 1 : 0;
162 for (auto const& surface : surfaces)
163 {
164 if (surface->visible())
165 {
166 auto const tracker = rendering_trackers.find(surface.get());
167 if (tracker != rendering_trackers.end() && tracker->second->is_exposed_in(id))
168 {
169 // Note that we ask the surface and not a Renderable.
170 // This is because we don't want to waste time and resources
171 // on a snapshot till we're sure we need it...
172 int ready = surface->buffers_ready_for_compositor(id);
173 if (ready > result)
174 result = ready;
175 }
176 }
177 }
178 return result;
179}
180
181void ms::SurfaceStack::register_compositor(mc::CompositorID cid)167void ms::SurfaceStack::register_compositor(mc::CompositorID cid)
182{168{
183 RecursiveWriteLock lg(guard);169 RecursiveWriteLock lg(guard);
184170
=== modified file 'src/server/scene/surface_stack.h'
--- src/server/scene/surface_stack.h 2016-01-29 08:18:22 +0000
+++ src/server/scene/surface_stack.h 2016-02-11 14:50:57 +0000
@@ -74,7 +74,6 @@
7474
75 // From Scene75 // From Scene
76 compositor::SceneElementSequence scene_elements_for(compositor::CompositorID id) override;76 compositor::SceneElementSequence scene_elements_for(compositor::CompositorID id) override;
77 int frames_pending(compositor::CompositorID) const override;
78 void register_compositor(compositor::CompositorID id) override;77 void register_compositor(compositor::CompositorID id) override;
79 void unregister_compositor(compositor::CompositorID id) override;78 void unregister_compositor(compositor::CompositorID id) override;
8079
8180
=== modified file 'tests/acceptance-tests/test_buffer_stream_arrangement.cpp'
--- tests/acceptance-tests/test_buffer_stream_arrangement.cpp 2016-01-29 08:18:22 +0000
+++ tests/acceptance-tests/test_buffer_stream_arrangement.cpp 2016-02-11 14:50:57 +0000
@@ -167,10 +167,10 @@
167 {167 {
168 }168 }
169169
170 void composite(mc::SceneElementSequence&& scene_sequence) override170 int composite(mc::SceneElementSequence&& scene_sequence) override
171 {171 {
172 ordering->note_scene_element_sequence(scene_sequence);172 ordering->note_scene_element_sequence(scene_sequence);
173 wrapped->composite(std::move(scene_sequence));173 return wrapped->composite(std::move(scene_sequence));
174 }174 }
175175
176 std::shared_ptr<mc::DisplayBufferCompositor> const wrapped;176 std::shared_ptr<mc::DisplayBufferCompositor> const wrapped;
177177
=== modified file 'tests/acceptance-tests/test_client_scaling.cpp'
--- tests/acceptance-tests/test_client_scaling.cpp 2016-01-29 08:18:22 +0000
+++ tests/acceptance-tests/test_client_scaling.cpp 2016-02-11 14:50:57 +0000
@@ -120,10 +120,11 @@
120 {120 {
121 }121 }
122122
123 void composite(mc::SceneElementSequence&& seq)123 int composite(mc::SceneElementSequence&& seq)
124 {124 {
125 watch->note_renderable_sizes(this, seq);125 watch->note_renderable_sizes(this, seq);
126 std::this_thread::yield();126 std::this_thread::yield();
127 return 0;
127 }128 }
128129
129 std::shared_ptr<SizeWatcher> const watch;130 std::shared_ptr<SizeWatcher> const watch;
130131
=== modified file 'tests/acceptance-tests/test_render_override.cpp'
--- tests/acceptance-tests/test_render_override.cpp 2015-06-17 05:20:42 +0000
+++ tests/acceptance-tests/test_render_override.cpp 2016-02-11 14:50:57 +0000
@@ -74,7 +74,7 @@
74 {74 {
75 }75 }
7676
77 void composite(mc::SceneElementSequence&& sequence) override77 int composite(mc::SceneElementSequence&& sequence) override
78 {78 {
79 for(auto const& element : sequence)79 for(auto const& element : sequence)
80 {80 {
@@ -82,6 +82,8 @@
82 renderable->buffer(); //consume buffer to stop compositor from spinning82 renderable->buffer(); //consume buffer to stop compositor from spinning
83 tracker->surface_rendered_with_size(renderable->screen_position().size);83 tracker->surface_rendered_with_size(renderable->screen_position().size);
84 }84 }
85
86 return 0;
85 }87 }
86private:88private:
87 std::shared_ptr<CompositionTracker> const tracker;89 std::shared_ptr<CompositionTracker> const tracker;
8890
=== modified file 'tests/include/mir/test/doubles/stub_scene.h'
--- tests/include/mir/test/doubles/stub_scene.h 2015-02-22 07:46:25 +0000
+++ tests/include/mir/test/doubles/stub_scene.h 2016-02-11 14:50:57 +0000
@@ -36,10 +36,6 @@
36 {36 {
37 return {};37 return {};
38 }38 }
39 int frames_pending(compositor::CompositorID) const override
40 {
41 return 0;
42 }
43 void register_compositor(compositor::CompositorID) override39 void register_compositor(compositor::CompositorID) override
44 {40 {
45 }41 }
4642
=== modified file 'tests/include/mir/test/doubles/stub_scene_element.h'
--- tests/include/mir/test/doubles/stub_scene_element.h 2015-06-17 05:20:42 +0000
+++ tests/include/mir/test/doubles/stub_scene_element.h 2016-02-11 14:50:57 +0000
@@ -56,6 +56,11 @@
56 {56 {
57 }57 }
5858
59 int pending_buffers() const override
60 {
61 return 0;
62 }
63
59 std::unique_ptr<compositor::Decoration> decoration() const override64 std::unique_ptr<compositor::Decoration> decoration() const override
60 {65 {
61 return nullptr;66 return nullptr;
6267
=== modified file 'tests/integration-tests/test_server_shutdown.cpp'
--- tests/integration-tests/test_server_shutdown.cpp 2016-01-29 08:18:22 +0000
+++ tests/integration-tests/test_server_shutdown.cpp 2016-02-11 14:50:57 +0000
@@ -189,7 +189,7 @@
189 {189 {
190 struct ExceptionThrowingDisplayBufferCompositor : mc::DisplayBufferCompositor190 struct ExceptionThrowingDisplayBufferCompositor : mc::DisplayBufferCompositor
191 {191 {
192 void composite(mc::SceneElementSequence&&) override192 int composite(mc::SceneElementSequence&&) override
193 {193 {
194 throw std::runtime_error("ExceptionThrowingDisplayBufferCompositor");194 throw std::runtime_error("ExceptionThrowingDisplayBufferCompositor");
195 }195 }
196196
=== modified file 'tests/unit-tests/compositor/test_compositing_screencast.cpp'
--- tests/unit-tests/compositor/test_compositing_screencast.cpp 2016-02-01 22:53:06 +0000
+++ tests/unit-tests/compositor/test_compositing_screencast.cpp 2016-02-11 14:50:57 +0000
@@ -118,9 +118,10 @@
118118
119struct MockDisplayBufferCompositor : mc::DisplayBufferCompositor119struct MockDisplayBufferCompositor : mc::DisplayBufferCompositor
120{120{
121 void composite(mc::SceneElementSequence&& seq)121 int composite(mc::SceneElementSequence&& seq)
122 {122 {
123 composite_(seq);123 composite_(seq);
124 return 0;
124 }125 }
125126
126 MOCK_METHOD1(composite_, void(mc::SceneElementSequence&));127 MOCK_METHOD1(composite_, void(mc::SceneElementSequence&));
@@ -134,9 +135,9 @@
134 {135 {
135 }136 }
136137
137 void composite(mc::SceneElementSequence&& elements)138 int composite(mc::SceneElementSequence&& elements)
138 {139 {
139 comp.composite(std::move(elements));140 return comp.composite(std::move(elements));
140 }141 }
141142
142private:143private:
143144
=== modified file 'tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp'
--- tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2016-01-29 08:18:22 +0000
+++ tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -48,6 +48,7 @@
4848
49namespace mt = mir::test;49namespace mt = mir::test;
50namespace mtd = mir::test::doubles;50namespace mtd = mir::test::doubles;
51using namespace testing;
5152
52namespace53namespace
53{54{
@@ -72,6 +73,11 @@
72 {73 {
73 }74 }
7475
76 int pending_buffers() const override
77 {
78 return 0;
79 }
80
75 std::unique_ptr<mc::Decoration> decoration() const override81 std::unique_ptr<mc::Decoration> decoration() const override
76 {82 {
77 return nullptr;83 return nullptr;
@@ -97,7 +103,6 @@
97 big(std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{5, 10},{100, 200}})),103 big(std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{5, 10},{100, 200}})),
98 fullscreen(std::make_shared<mtd::FakeRenderable>(screen))104 fullscreen(std::make_shared<mtd::FakeRenderable>(screen))
99 {105 {
100 using namespace testing;
101 ON_CALL(display_buffer, orientation())106 ON_CALL(display_buffer, orientation())
102 .WillByDefault(Return(mir_orientation_normal));107 .WillByDefault(Return(mir_orientation_normal));
103 ON_CALL(display_buffer, view_area())108 ON_CALL(display_buffer, view_area())
@@ -106,7 +111,7 @@
106 .WillByDefault(Return(false));111 .WillByDefault(Return(false));
107 }112 }
108113
109 testing::NiceMock<mtd::MockRenderer> mock_renderer;114 NiceMock<mtd::MockRenderer> mock_renderer;
110 geom::Rectangle screen{{0, 0}, {1366, 768}};115 geom::Rectangle screen{{0, 0}, {1366, 768}};
111 testing::NiceMock<mtd::MockDisplayBuffer> display_buffer;116 testing::NiceMock<mtd::MockDisplayBuffer> display_buffer;
112 std::shared_ptr<mtd::FakeRenderable> small;117 std::shared_ptr<mtd::FakeRenderable> small;
@@ -132,7 +137,6 @@
132137
133TEST_F(DefaultDisplayBufferCompositor, optimization_skips_composition)138TEST_F(DefaultDisplayBufferCompositor, optimization_skips_composition)
134{139{
135 using namespace testing;
136 auto report = std::make_shared<mtd::MockCompositorReport>();140 auto report = std::make_shared<mtd::MockCompositorReport>();
137141
138 Sequence seq;142 Sequence seq;
@@ -190,7 +194,6 @@
190194
191TEST_F(DefaultDisplayBufferCompositor, calls_renderer_in_sequence)195TEST_F(DefaultDisplayBufferCompositor, calls_renderer_in_sequence)
192{196{
193 using namespace testing;
194 Sequence render_seq;197 Sequence render_seq;
195 EXPECT_CALL(mock_renderer, suspend())198 EXPECT_CALL(mock_renderer, suspend())
196 .Times(0);199 .Times(0);
@@ -215,7 +218,6 @@
215218
216TEST_F(DefaultDisplayBufferCompositor, optimization_toggles_seamlessly)219TEST_F(DefaultDisplayBufferCompositor, optimization_toggles_seamlessly)
217{220{
218 using namespace testing;
219 ON_CALL(display_buffer, view_area())221 ON_CALL(display_buffer, view_area())
220 .WillByDefault(Return(screen));222 .WillByDefault(Return(screen));
221 ON_CALL(display_buffer, orientation())223 ON_CALL(display_buffer, orientation())
@@ -262,7 +264,6 @@
262264
263TEST_F(DefaultDisplayBufferCompositor, occluded_surfaces_are_not_rendered)265TEST_F(DefaultDisplayBufferCompositor, occluded_surfaces_are_not_rendered)
264{266{
265 using namespace testing;
266 EXPECT_CALL(display_buffer, view_area())267 EXPECT_CALL(display_buffer, view_area())
267 .WillRepeatedly(Return(screen));268 .WillRepeatedly(Return(screen));
268 EXPECT_CALL(display_buffer, orientation())269 EXPECT_CALL(display_buffer, orientation())
@@ -305,13 +306,12 @@
305 MOCK_CONST_METHOD0(decoration, std::unique_ptr<mc::Decoration>());306 MOCK_CONST_METHOD0(decoration, std::unique_ptr<mc::Decoration>());
306 MOCK_METHOD0(rendered, void());307 MOCK_METHOD0(rendered, void());
307 MOCK_METHOD0(occluded, void());308 MOCK_METHOD0(occluded, void());
309 MOCK_CONST_METHOD0(pending_buffers, int());
308};310};
309}311}
310312
311TEST_F(DefaultDisplayBufferCompositor, marks_rendered_scene_elements)313TEST_F(DefaultDisplayBufferCompositor, marks_rendered_scene_elements)
312{314{
313 using namespace testing;
314
315 auto element0_rendered = std::make_shared<NiceMock<MockSceneElement>>(315 auto element0_rendered = std::make_shared<NiceMock<MockSceneElement>>(
316 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{99,99},{2,2}}));316 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{99,99},{2,2}}));
317 auto element1_rendered = std::make_shared<NiceMock<MockSceneElement>>(317 auto element1_rendered = std::make_shared<NiceMock<MockSceneElement>>(
@@ -330,8 +330,6 @@
330330
331TEST_F(DefaultDisplayBufferCompositor, marks_occluded_scene_elements)331TEST_F(DefaultDisplayBufferCompositor, marks_occluded_scene_elements)
332{332{
333 using namespace testing;
334
335 auto element0_occluded = std::make_shared<NiceMock<MockSceneElement>>(333 auto element0_occluded = std::make_shared<NiceMock<MockSceneElement>>(
336 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{10,10},{20,20}}));334 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{10,10},{20,20}}));
337 auto element1_rendered = std::make_shared<NiceMock<MockSceneElement>>(335 auto element1_rendered = std::make_shared<NiceMock<MockSceneElement>>(
@@ -351,3 +349,39 @@
351 compositor.composite({element0_occluded, element1_rendered, element2_occluded});349 compositor.composite({element0_occluded, element1_rendered, element2_occluded});
352}350}
353351
352TEST_F(DefaultDisplayBufferCompositor, reports_pending_frames_accurately)
353{
354 auto const element = std::make_shared<NiceMock<MockSceneElement>>(
355 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{{10,10},{20,20}}));
356
357 mc::DefaultDisplayBufferCompositor compositor(
358 display_buffer,
359 mt::fake_shared(mock_renderer),
360 mr::null_compositor_report());
361
362 EXPECT_THAT(compositor.composite({element}), Eq(0));
363
364 auto const pending_frames_before = 3;
365 auto const pending_frames_after = pending_frames_before-1;
366
367 EXPECT_CALL(*element, pending_buffers()).WillOnce(Return(pending_frames_after));
368 EXPECT_THAT(compositor.composite({element}), Eq(pending_frames_after));
369}
370
371TEST_F(DefaultDisplayBufferCompositor, doesnt_count_pending_frames_from_occluded_surfaces)
372{
373 auto const element = std::make_shared<NiceMock<MockSceneElement>>(
374 std::make_shared<mtd::FakeRenderable>(geom::Rectangle{screen.bottom_right(),{20,20}}));
375
376 mc::DefaultDisplayBufferCompositor compositor(
377 display_buffer,
378 mt::fake_shared(mock_renderer),
379 mr::null_compositor_report());
380
381 EXPECT_THAT(compositor.composite({element}), Eq(0));
382
383 auto const pending_frames = 3;
384 ON_CALL(*element, pending_buffers()).WillByDefault(Return(pending_frames));
385
386 EXPECT_THAT(compositor.composite({element}), Eq(0));
387}
354388
=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-02-01 09:42:10 +0000
+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2016-02-11 14:50:57 +0000
@@ -96,8 +96,8 @@
96class StubScene : public mtd::StubScene96class StubScene : public mtd::StubScene
97{97{
98public:98public:
99 StubScene()99 StubScene() :
100 : pending{0}, throw_on_add_observer_{false}100 throw_on_add_observer_{false}
101 {101 {
102 }102 }
103103
@@ -136,19 +136,7 @@
136 throw_on_add_observer_ = flag;136 throw_on_add_observer_ = flag;
137 }137 }
138138
139 int frames_pending(mc::CompositorID) const override
140 {
141 return pending;
142 }
143
144 void set_pending(int n)
145 {
146 pending = n;
147 observer->scene_changed();
148 }
149
150private:139private:
151 std::atomic<int> pending;
152 std::mutex observer_mutex;140 std::mutex observer_mutex;
153 std::shared_ptr<ms::Observer> observer;141 std::shared_ptr<ms::Observer> observer;
154 bool throw_on_add_observer_;142 bool throw_on_add_observer_;
@@ -157,20 +145,21 @@
157class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor145class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor
158{146{
159public:147public:
160 RecordingDisplayBufferCompositor(std::function<void()> const& mark_render_buffer)148 RecordingDisplayBufferCompositor(std::function<int()> const& mark_render_buffer)
161 : mark_render_buffer{mark_render_buffer}149 : mark_render_buffer{mark_render_buffer}
162 {150 {
163 }151 }
164152
165 void composite(mc::SceneElementSequence&&)153 int composite(mc::SceneElementSequence&&)
166 {154 {
167 mark_render_buffer();155 auto const result = mark_render_buffer();
168 /* Reduce run-time under valgrind */156 /* Reduce run-time under valgrind */
169 std::this_thread::yield();157 std::this_thread::yield();
158 return result;
170 }159 }
171160
172private:161private:
173 std::function<void()> const mark_render_buffer;162 std::function<int()> const mark_render_buffer;
174};163};
175164
176165
@@ -182,12 +171,12 @@
182 auto raw = new RecordingDisplayBufferCompositor{171 auto raw = new RecordingDisplayBufferCompositor{
183 [&display_buffer,this]()172 [&display_buffer,this]()
184 {173 {
185 mark_render_buffer(display_buffer);174 return mark_render_buffer(display_buffer);
186 }};175 }};
187 return std::unique_ptr<RecordingDisplayBufferCompositor>(raw);176 return std::unique_ptr<RecordingDisplayBufferCompositor>(raw);
188 }177 }
189178
190 void mark_render_buffer(mg::DisplayBuffer& display_buffer)179 int mark_render_buffer(mg::DisplayBuffer& display_buffer)
191 {180 {
192 std::lock_guard<std::mutex> lk{m};181 std::lock_guard<std::mutex> lk{m};
193182
@@ -196,6 +185,8 @@
196185
197 ++records[&display_buffer].first;186 ++records[&display_buffer].first;
198 records[&display_buffer].second.insert(std::this_thread::get_id());187 records[&display_buffer].second.insert(std::this_thread::get_id());
188
189 return pending;
199 }190 }
200191
201 bool enough_records_gathered(unsigned int nbuffers, unsigned int min_record_count = 1000)192 bool enough_records_gathered(unsigned int nbuffers, unsigned int min_record_count = 1000)
@@ -264,7 +255,10 @@
264 return true;255 return true;
265 }256 }
266257
258 void set_pending(int n) { pending = n; }
259
267private:260private:
261 std::atomic<int> pending{0};
268 std::mutex m;262 std::mutex m;
269 typedef std::pair<unsigned int, std::unordered_set<std::thread::id>> Record;263 typedef std::pair<unsigned int, std::unordered_set<std::thread::id>> Record;
270 std::unordered_map<mg::DisplayBuffer*,Record> records;264 std::unordered_map<mg::DisplayBuffer*,Record> records;
@@ -278,11 +272,12 @@
278 {272 {
279 }273 }
280274
281 void composite(mc::SceneElementSequence&&) override275 int composite(mc::SceneElementSequence&&) override
282 {276 {
283 fake_surface_update();277 fake_surface_update();
284 /* Reduce run-time under valgrind */278 /* Reduce run-time under valgrind */
285 std::this_thread::yield();279 std::this_thread::yield();
280 return 0;
286 }281 }
287282
288private:283private:
@@ -331,6 +326,7 @@
331 {326 {
332 std::lock_guard<std::mutex> lock{thread_names_mutex};327 std::lock_guard<std::mutex> lock{thread_names_mutex};
333 thread_names.emplace_back(mt::current_thread_name());328 thread_names.emplace_back(mt::current_thread_name());
329 return 0;
334 }};330 }};
335 return std::unique_ptr<RecordingDisplayBufferCompositor>(raw);331 return std::unique_ptr<RecordingDisplayBufferCompositor>(raw);
336 }332 }
@@ -595,7 +591,8 @@
595 ASSERT_LT(retry, max_retries);591 ASSERT_LT(retry, max_retries);
596592
597 // Some surface queues up multiple frames593 // Some surface queues up multiple frames
598 scene->set_pending(3);594 factory->set_pending(3);
595 scene->emit_change_event();
599596
600 // Make sure they all get composited separately (2 + 3 more)597 // Make sure they all get composited separately (2 + 3 more)
601 retry = 0;598 retry = 0;
602599
=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
--- tests/unit-tests/scene/test_surface_stack.cpp 2016-01-29 08:18:22 +0000
+++ tests/unit-tests/scene/test_surface_stack.cpp 2016-02-11 14:50:57 +0000
@@ -335,124 +335,6 @@
335 EXPECT_EQ("username@hostname: ~/Documents", decor->name);335 EXPECT_EQ("username@hostname: ~/Documents", decor->name);
336}336}
337337
338TEST_F(SurfaceStack, scene_counts_pending_accurately)
339{
340 using namespace testing;
341 ms::SurfaceStack stack{report};
342 stack.register_compositor(this);
343 mtd::StubBuffer stub_buffer;
344 int ready = 0;
345 auto mock_queue = std::make_shared<testing::NiceMock<mtd::MockBufferBundle>>();
346 ON_CALL(*mock_queue, buffers_ready_for_compositor(_))
347 .WillByDefault(InvokeWithoutArgs([&]{return ready;}));
348 ON_CALL(*mock_queue, client_release(_))
349 .WillByDefault(InvokeWithoutArgs([&]{ready++;}));
350 ON_CALL(*mock_queue, compositor_acquire(_))
351 .WillByDefault(InvokeWithoutArgs([&]{ready--; return mt::fake_shared(stub_buffer); }));
352
353 auto surface = std::make_shared<ms::BasicSurface>(
354 std::string("stub"),
355 geom::Rectangle{{},{}},
356 false,
357 std::make_shared<mc::BufferStreamSurfaces>(mock_queue),
358 std::shared_ptr<mir::input::InputChannel>(),
359 std::shared_ptr<mir::input::InputSender>(),
360 std::shared_ptr<mg::CursorImage>(),
361 report);
362 stack.add_surface(surface, default_params.input_mode);
363 surface->configure(mir_surface_attrib_visibility,
364 mir_surface_visibility_exposed);
365
366 EXPECT_EQ(0, stack.frames_pending(this));
367 post_a_frame(*surface);
368 post_a_frame(*surface);
369 post_a_frame(*surface);
370 EXPECT_EQ(3, stack.frames_pending(this));
371
372 for (int expect = 3; expect >= 0; --expect)
373 {
374 ASSERT_EQ(expect, stack.frames_pending(this));
375 auto snap = stack.scene_elements_for(compositor_id);
376 for (auto& element : snap)
377 {
378 auto consumed = element->renderable()->buffer();
379 }
380 }
381}
382
383TEST_F(SurfaceStack, scene_doesnt_count_pending_frames_from_occluded_surfaces)
384{ // Regression test for LP: #1418081
385 using namespace testing;
386
387 ms::SurfaceStack stack{report};
388 stack.register_compositor(this);
389 auto surface = std::make_shared<ms::BasicSurface>(
390 std::string("stub"),
391 geom::Rectangle{{},{}},
392 false,
393 std::make_shared<mtd::StubBufferStream>(),
394 std::shared_ptr<mir::input::InputChannel>(),
395 std::shared_ptr<mir::input::InputSender>(),
396 std::shared_ptr<mg::CursorImage>(),
397 report);
398
399 stack.add_surface(surface, default_params.input_mode);
400 auto elements = stack.scene_elements_for(this);
401 for (auto const& elem : elements)
402 elem->occluded();
403
404 EXPECT_EQ(0, stack.frames_pending(this));
405 post_a_frame(*surface);
406 post_a_frame(*surface);
407 post_a_frame(*surface);
408 EXPECT_EQ(0, stack.frames_pending(this));
409}
410
411TEST_F(SurfaceStack, scene_doesnt_count_pending_frames_from_partially_exposed_surfaces)
412{ // Regression test for LP: #1499039
413 using namespace testing;
414
415 // Partially exposed means occluded in one compositor but not another
416 ms::SurfaceStack stack{report};
417 auto const comp1 = reinterpret_cast<mc::CompositorID>(0);
418 auto const comp2 = reinterpret_cast<mc::CompositorID>(1);
419
420 stack.register_compositor(comp1);
421 stack.register_compositor(comp2);
422 auto surface = std::make_shared<ms::BasicSurface>(
423 std::string("stub"),
424 geom::Rectangle{{},{}},
425 false,
426 std::make_shared<mtd::StubBufferStream>(),
427 std::shared_ptr<mir::input::InputChannel>(),
428 std::shared_ptr<mir::input::InputSender>(),
429 std::shared_ptr<mg::CursorImage>(),
430 report);
431
432 stack.add_surface(surface, default_params.input_mode);
433 post_a_frame(*surface);
434 post_a_frame(*surface);
435 post_a_frame(*surface);
436
437 EXPECT_EQ(3, stack.frames_pending(comp1));
438 EXPECT_EQ(3, stack.frames_pending(comp2));
439
440 auto elements = stack.scene_elements_for(comp1);
441 for (auto const& elem : elements)
442 {
443 elem->rendered();
444 }
445
446 elements = stack.scene_elements_for(comp2);
447 for (auto const& elem : elements)
448 {
449 elem->occluded();
450 }
451
452 EXPECT_EQ(3, stack.frames_pending(comp1));
453 EXPECT_EQ(0, stack.frames_pending(comp2));
454}
455
456TEST_F(SurfaceStack, surfaces_are_emitted_by_layer)338TEST_F(SurfaceStack, surfaces_are_emitted_by_layer)
457{339{
458 using namespace testing;340 using namespace testing;

Subscribers

People subscribed via source and target branches