Mir

Merge lp:~vanvugt/mir/bypass into lp:~mir-team/mir/trunk

Proposed by Daniel van Vugt
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 1033
Proposed branch: lp:~vanvugt/mir/bypass
Merge into: lp:~mir-team/mir/trunk
Prerequisite: lp:~vanvugt/mir/lockable-scene
Diff against target: 1377 lines (+981/-13)
27 files modified
include/platform/mir/graphics/buffer.h (+1/-0)
include/platform/mir/graphics/display_buffer.h (+6/-0)
include/server/mir/compositor/basic_display_buffer_compositor.h (+1/-1)
include/test/mir_test_doubles/mock_buffer.h (+2/-0)
include/test/mir_test_doubles/mock_compositing_criteria.h (+0/-1)
include/test/mir_test_doubles/mock_display_buffer.h (+1/-0)
include/test/mir_test_doubles/null_display_buffer.h (+1/-0)
include/test/mir_test_doubles/stub_buffer.h (+2/-0)
include/test/mir_test_doubles/stub_compositing_criteria.h (+71/-0)
src/server/compositor/CMakeLists.txt (+1/-0)
src/server/compositor/bypass.cpp (+118/-0)
src/server/compositor/bypass.h (+64/-0)
src/server/compositor/default_display_buffer_compositor.cpp (+49/-0)
src/server/compositor/default_display_buffer_compositor.h (+2/-0)
src/server/compositor/temporary_buffers.cpp (+5/-0)
src/server/compositor/temporary_buffers.h (+1/-0)
src/server/graphics/android/buffer.cpp (+5/-0)
src/server/graphics/android/buffer.h (+1/-0)
src/server/graphics/android/gpu_hwc_android_display_buffer_factory.cpp (+5/-0)
src/server/graphics/gbm/gbm_buffer.cpp (+13/-2)
src/server/graphics/gbm/gbm_buffer.h (+9/-0)
src/server/graphics/gbm/gbm_buffer_allocator.cpp (+20/-1)
src/server/graphics/gbm/gbm_display_buffer.cpp (+48/-8)
src/server/graphics/gbm/gbm_display_buffer.h (+5/-0)
tests/unit-tests/compositor/CMakeLists.txt (+1/-0)
tests/unit-tests/compositor/test_bypass.cpp (+258/-0)
tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp (+291/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/bypass
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Robert Ancell Needs Fixing
Alan Griffiths Approve
Daniel van Vugt Abstain
Alexandros Frantzis (community) Approve
kevin gunn (community) Approve
Review via email: mp+181003@code.launchpad.net

This proposal supersedes a proposal from 2013-08-16.

Commit message

Add "composition bypass" support, whereby fullscreen surfaces are allowed
to go straight to the display hardware without being composited at all,
hence avoiding the overhead of any OpenGL or texturing where possible.

Hardware support:
  intel: Excellent
  radeon: Good, but REQUIRES kernel 3.11.0
  nouveau: Good, but REQUIRES kernel 3.11.0
  android: No bypass implemented yet.

Description of the change

Testing status:

intel: Excellent. No issues observed anywhere.

nouveau: Good. Requires kernel 3.11.0+, otherwise crashes with page flip failures.
Also beware of bug 1211700 which is amplified by bug 1195811!

radeon: Good (according to Alexandros). Requires kernel 3.11.0+, otherwise you will just see corruption on bypassed surfaces. I can't test this due to bug 1212977 and other bugs which prevent me from using radeon+3.11.0 on raring.

android: Should have no effect, but untestable due to bug 1211694.

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

Update: Failures to schedule pageflips on nouveau are fixed. The problem was a known nouveau bug in older kernels, now fixed in 3.11.0-rc*... But nouveau does seem to still have issues with some (not all) bypass clients hanging. Looks similar to 1211700.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Update 2: nouveau's client hangs seem to be bug 1211700, which is nothing new, but made worse the higher the client frame rate (hence much worse when bypassing).

So I think nouveau is now bypass feature-complete, so long as you make sure you have kernel 3.11 and are aware of bug 1211700.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Update 3: I am told that radeon also works with kernel 3.11+. However I cannot test it for myself due to various kernel problems including bug 1212977.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

A quick first pass:

37 + virtual void post_update(std::shared_ptr<Buffer> /* bypass_buf */) {}

DisplayBuffer should be a pure abstract (interface) class.

701 + last_flipped_bypass_buf = bypass_buf; // Can be nullptr

The variable isn't used anywhere else.

673 + auto native = bypass_buf->native_buffer_handle();
674 + struct gbm_bo *bo = *(struct gbm_bo**)native->data;
675 + bufobj = get_buffer_object(bo);

Since we are making the assumption that the mg::Buffer is a GBMBuffer, why not cast to a GBMBuffer and provide a GBMBuffer::get_buffer_object() method?

review: Needs Fixing
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

> Since we are making the assumption that the mg::Buffer is a GBMBuffer, why not cast to a GBMBuffer and provide a GBMBuffer::get_buffer_object() method?

GBMBuffer::get_gbm_bo()

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Alexandros,

1. Purifying: virtual void post_update(std::shared_ptr<Buffer> /* bypass_buf */) {}
I think it would be worse to ask platforms which don't implement bypass to implement an unused stub. That's just going to confuse people and bloat the code with unused stub methods.

2. "Unused" last_flipped_bypass_buf is actually important and very much used. It holds a reference to the TemporaryBuffer being scanned out for the lifetime of the frame. Without it the buffer will get reused too soon and you will see that as tearing.

3. Ugly casting... Your suggestion probably is not possible exactly how you put it. It's a TemporaryBuffer so the get_gbm_bo function would have to exist in the common interfaces for all platforms. I did have a similar generic solution implemented until r891, but decided using native_buffer_handle was more appropriate and avoided polluting non-GBM platforms. Please bzr diff -r891..890 and tell me if that would be preferable.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

> 1. Purifying: virtual void post_update(std::shared_ptr<Buffer> /* bypass_buf */) {}
> I think it would be worse to ask platforms which don't implement bypass to implement
> an unused stub. That's just going to confuse people and bloat the code with unused stub methods.

All of our interface classes are pure abstract, and it pays to be consistent. I don't think it's such a big overhead to add a stub method to a platform if it doesn't support bypass.

> 2. "Unused" last_flipped_bypass_buf is actually important and very much used.
> It holds a reference to the TemporaryBuffer being scanned out for the lifetime of the frame.
> Without it the buffer will get reused too soon and you will see that as tearing.

OK. Perhaps a comment here would help to make the intent more apparent.

> 3. Ugly casting... Your suggestion probably is not possible exactly how you put it.
> It's a TemporaryBuffer so the get_gbm_bo function would have to exist in the common
> interfaces for all platforms. I did have a similar generic solution implemented until
> r891, but decided using native_buffer_handle was more appropriate and avoided polluting
> non-GBM platforms. Please bzr diff -r891..890 and tell me if that would be preferable.

How about having a MirGBMBufferPackage struct which extends MirBufferPackage? We can then cast the returned MirNativeBuffer struct that GBMBuffer::native_buffer_handle() returns to MirGBMBufferPackage to gain access to GBM specific server-side data (like the gbm_bo).

235 +BypassFilter::BypassFilter(const graphics::DisplayBuffer &display_buffer)
...and at other points

'T const&' is the preferred style.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Alexandros,

1. Pure virtual: Disagree
"Consistency" in this case would just produce unreachable and confusing code. It would increase code bloat, reduce test coverage and likely confuse anyone reading it.

2. last_flipped_bypass_buf: Fixed with a comment

3. Ugly casting: Fixed as you suggested

4. Tconst&: Disagree
I've been using "const T&" ever since I raised the issue;
https://lists.ubuntu.com/archives/mir-devel/2013-April/000098.html
https://lists.ubuntu.com/archives/mir-devel/2013-April/000106.html
I hoped that others would follow, but the conversation went quiet.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> 4. Tconst&: Disagree
> I've been using "const T&" ever since I raised the issue;
> https://lists.ubuntu.com/archives/mir-devel/2013-April/000098.html
> https://lists.ubuntu.com/archives/mir-devel/2013-April/000106.html
> I hoped that others would follow, but the conversation went quiet.

"We favor the form int const* foo to const int* foo. This keeps the const with the type modifier (& or *).

"That said, while we encourage putting const after the type, we do not require it. But be consistent with the code around you!"

http://unity.ubuntu.com/mir/cppguide/index.html?showone=Use_of_const#Use_of_const

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

50 -private:
51 +protected:

BasicDisplayBufferCompositor is a weird piece of design an this makes it weirder. (Protected member data increases coupling and suggests poor coherence.)

Existing issue here: What on earth does "void BasicDisplayBufferCompositor::composite()" convey to the user?

But this MP is probably not the place to address this (unless a better design makes other things simpler).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

416 +void mc::DefaultDisplayBufferCompositor::composite()
417 +{
418 + bool bypassed = false;
419 +
420 + if (display_buffer.can_bypass())
421 + {
422 + mc::BypassFilter filter(display_buffer);
423 + mc::BypassMatch match;
424 +
425 + // It would be *really* nice if Scene had an iterator to simplify this
426 + scene->for_each_if(filter, match);
427 +
428 + if (filter.fullscreen_on_top())
429 + {
430 + auto bypass_buf =
431 + match.topmost_fullscreen()->lock_compositor_buffer();
432 +
433 + if (bypass_buf->can_bypass())
434 + {
435 + display_buffer.post_update(bypass_buf);
436 + bypassed = true;
437 + }
438 + }
439 + }
440 +
441 + if (!bypassed)
442 + mc::BasicDisplayBufferCompositor::composite();
443 +}

On an initial reading this looks as though it violates "Tell, Don't Ask".

I'd rather it read:

void mc::DefaultDisplayBufferCompositor::composite()
{
    if (!display_buffer.effects_bypass(scene))
        mc::BasicDisplayBufferCompositor::composite();
}

With the logic to handle determining if bypass is possible and effecting it owned by a single method in the DisplayBuffer implementation instead of the proposed pair of can_bypass() and post_update(...).

"Needs Information" as there's no "Needs Discussion"

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

BypassMatch is used to capture a pointer into a data structure guarded by a mutex - and that pointer is later used after the mutex is unlocked.

(Also, BypassMatch would be more readable as a lambda.)

review: Needs Fixing
Revision history for this message
Robert Ancell (robert-ancell) wrote : Posted in a previous version of this proposal

This feature has been requested to be tested on the QA test machines before merging. So I'll put a "needs fixing" block on it until that has been completed.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

There's a lot going on here. But it looks like only Alan's raising new issues that "Needs Fixing" right now...

Alan,

I agree BasicDisplayBufferCompositor is a bit odd and could possibly be cleaned up. But that's a pre-existing issue.

I dislike BypassFilter/BypassMatch too. But that's all the Scene interface gives us right now. I would prefer that Scene simply provided an iterator (which points to surfaces) instead. But I thought in the interest of keeping diffs small and delivering on time, I should work with the existing iteration interface.

On first read I like your proposal of DisplayBuffer::effects_bypass. But then I realized that would either move the bypass decision logic into the interface class DisplayBuffer, or it would move the platform-independent decision logic into GBMDisplayBuffer. Hence rendering it inaccessible to other platforms/drivers. So I think the decision logic is best placed where it is -- in the compositing logic common to all platforms.

Re: lambdas... Again, I would prefer to see a simple Scene::iterator instead. But that's a pre-existing problem.

pointer used after mutex unlocked: I'm not sure there's significant risk yet. Will have to investigate and likely (regretfully) have to change the Scene interface. The big issue is that I cannot use OperatorForScene it is current form. Because I need to ensure only one surface gets bypassed per frame. That means doing it after for_each_if().

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

-"it is"
+"in its"

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Work in progress. Waiting on new prerequisite: lp:~vanvugt/mir/lockable-scene
to solve the locking issue Alan mentioned.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Robert Ancell (robert-ancell) wrote : Posted in a previous version of this proposal

When testing with ppa:mir-team/qa-testing I'm seeing a ~50% drop in framerates when running glmark2. Unity otherwise seems to run with no perceptible performance hit and is stable.

From archive:

=======================================================
    glmark2 2012.08
=======================================================
    OpenGL Information
    GL_VENDOR: Intel Open Source Technology Center
    GL_RENDERER: Mesa DRI Intel(R) Ironlake Mobile
    GL_VERSION: 2.1 Mesa 9.2.0-devel
=======================================================
[build] use-vbo=false: FPS: 113 FrameTime: 8.850 ms
[build] use-vbo=true: FPS: 215 FrameTime: 4.651 ms
[texture] texture-filter=nearest: FPS: 280 FrameTime: 3.571 ms
[texture] texture-filter=linear: FPS: 302 FrameTime: 3.311 ms
[texture] texture-filter=mipmap: FPS: 276 FrameTime: 3.623 ms
[shading] shading=gouraud: FPS: 248 FrameTime: 4.032 ms
[shading] shading=blinn-phong-inf: FPS: 200 FrameTime: 5.000 ms
[shading] shading=phong: FPS: 169 FrameTime: 5.917 ms
[bump] bump-render=high-poly: FPS: 105 FrameTime: 9.524 ms
[bump] bump-render=normals: FPS: 244 FrameTime: 4.098 ms
[bump] bump-render=height: FPS: 251 FrameTime: 3.984 ms
[effect2d] kernel=0,1,0;1,-4,1;0,1,0;: FPS: 134 FrameTime: 7.463 ms
[effect2d] kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: FPS: 66 FrameTime: 15.152 ms
[pulsar] light=false:quads=5:texture=false: FPS: 222 FrameTime: 4.505 ms

From PPA:

=======================================================
    glmark2 2012.08
=======================================================
    OpenGL Information
    GL_VENDOR: Intel Open Source Technology Center
    GL_RENDERER: Mesa DRI Intel(R) Ironlake Mobile
    GL_VERSION: 2.1 Mesa 9.2.0-devel
=======================================================
[build] use-vbo=false: FPS: 114 FrameTime: 8.772 ms
[build] use-vbo=true: FPS: 116 FrameTime: 8.621 ms
[texture] texture-filter=nearest: FPS: 120 FrameTime: 8.333 ms
[texture] texture-filter=linear: FPS: 118 FrameTime: 8.475 ms
[texture] texture-filter=mipmap: FPS: 118 FrameTime: 8.475 ms
[shading] shading=gouraud: FPS: 101 FrameTime: 9.901 ms
[shading] shading=blinn-phong-inf: FPS: 117 FrameTime: 8.547 ms
[shading] shading=phong: FPS: 105 FrameTime: 9.524 ms
[bump] bump-render=high-poly: FPS: 90 FrameTime: 11.111 ms
[bump] bump-render=normals: FPS: 120 FrameTime: 8.333 ms
[bump] bump-render=height: FPS: 117 FrameTime: 8.547 ms
[effect2d] kernel=0,1,0;1,-4,1;0,1,0;: FPS: 102 FrameTime: 9.804 ms
[effect2d] kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;: FPS: 59 FrameTime: 16.949 ms
[pulsar] light=false:quads=5:texture=false: FPS: 116 FrameTime: 8.621 ms

(both crash after pulsar due to bug 1203742)

Revision history for this message
Robert Ancell (robert-ancell) :
review: Needs Fixing
Revision history for this message
kevin gunn (kgunn72) wrote :

ok, so the metric is phoronix
with the qa-testing ppa created from this branch on latest ubuntu archive
openarena = 10 fps on standalone X
openarena = 10 fps on Xmir

then i cross checked on a machine running xmir from archive updated just now
openarena = 60 fps on standalone X
openarena = 60 fps on Xmir

which indicates something effected standalone X (with xmir components installed, but type=unity commented out)

review: Needs Fixing
Revision history for this message
Robert Ancell (robert-ancell) wrote :

I get with phoronix openarena [1]:

With traditional X:
800x600 - 28.92
1366x768 - 17.27

With XMir bypass:
800x600 - 29.30
1366x768 - 16.98

So no significant impact.

[1] $ phoronix-test-suite benchmark pts/openarena

Revision history for this message
Robert Ancell (robert-ancell) wrote :

For completeness:

With archive XMir:
800x600 - 21.07
1366x768 - 14.97

So there is an improvement from the archive XMir to XMir with bypass.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Re-running the glmark2 tests doesn't show a big change anymore, so I think there might have been something wrong with the way I first tested it.

Revision history for this message
kevin gunn (kgunn72) wrote :

ok, reran on a more well known machine - good results
openarena 800x600
w/ bypass 109.33 w/o bypass 103 fps
openarena 1280 x 800
w/ bypass 58.4 w/o bypass 56 fps

also, rebuilt local bypass - compared to main for egltriangle demo client
w/ bypass 264 fps
w/o bypass 173 fps

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

I don't think it's appropriate to discuss detailed XMir results here, but while we are...

Platform / glmark2-window / glmark2-fullscreen / glxgears / OpenArena
XMir / 1531 / 1044 / 5600 / 265
XMir+bypass / 1628 / 1119 / 6000 / 280
Legacy X / 1847 / 1125 / 6600 / 270

* OpenArena seems very erratic for me so those numbers can't be trusted.
But any XMir results are going to be suboptimal while XMir can't do full direct GLX bypass yet.

For native Mir apps like eglplasma/egltriangle, the fullscreen performance benefit is much more exciting. Anywhere from 15% to 50% improvement.

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

Looks good overall.

Ideally, we could make some design improvements in terms of how we deal with display buffers, as mentioned in previous comments, but I haven't thought through the details of how to not push too much responsibility and the platform independent logic to the display buffer while doing this. If we can we work out something in this MP, great... otherwise I am OK with improving this later.

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

Well, it's a compositing decision so the logic going in the compositor (as it is) seems logical to me. But at the same time, I know I keep saying "Compositor" should not be a /thing/. It's too abstract. Composition should be the role of some more concrete class. Maybe it's just a naming problem...

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

I found an issue, that requires investigation, but can't be reproduced without modifying the code...

On one particular machine, I can make page flips fail and the server die, if I:
  (a) Remove 627 + if (buffer_properties.usage == BufferUsage::hardware &&
  (b) Run a fullscreen software client.

The failure is:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::runtime_error> >'
  what(): Failed to schedule page flip

However this is not something that's possible with the current code, and is something I tested many times before adding the hardware check. It used to work, at least on a different machine...

To make matters worse, bug 1215754 (which is a recent regression) prevents you from testing this with most software clients.

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

Hmm, the above scenario *does* still work on my desktop. Only fails on the laptop. And only by modifying code.

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

Robert,

Can you please change your review to something that won't mislead readers? Like "needs information". As I understand it, you're just blocking waiting on further PPA testing.

Revision history for this message
Robert Ancell (robert-ancell) :
review: Needs Information
Revision history for this message
Robert Ancell (robert-ancell) wrote :

Passed testing on the QA-8

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

There are still design issues around BasicDisplayBufferCompositor, BypassFilter, BypassMatch, Scene::lock()/unlock() and DefaultDisplayBufferCompositor::composite().

But some of that is pre-existing and all of it is better sorted after landing this stuff.

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

I've seen that test failure before this morning. I thought it a fluke: rebuild...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Robert Ancell (robert-ancell) wrote :

On ps-radeon-hd5750-le this branch is failing with screen corruption.

A standard build works fine:
http://10.97.2.12:8080/job/openarena-benchmark-ps-radeon-hd5750-le-xmir/44/

A build with just bypass fails (from ppa:mir-team/qa-testing)
http://10.97.2.12:8080/view/qa-ppa/job/qa-ppa-openarena-ps-radeon-hd5750-le/5/

Revision history for this message
Robert Ancell (robert-ancell) :
review: Needs Fixing
Revision history for this message
Robert Ancell (robert-ancell) wrote :

Image showing this machine running openarena: http://imgur.com/1k7tQE5

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

Removed today's experiments. They should live in separate branches. Back to the old version we've been testing for the past few weeks...

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
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/platform/mir/graphics/buffer.h'
2--- include/platform/mir/graphics/buffer.h 2013-08-06 11:56:40 +0000
3+++ include/platform/mir/graphics/buffer.h 2013-08-29 01:27:06 +0000
4@@ -42,6 +42,7 @@
5 virtual geometry::Stride stride() const = 0;
6 virtual geometry::PixelFormat pixel_format() const = 0;
7 virtual void bind_to_texture() = 0;
8+ virtual bool can_bypass() const = 0;
9
10 protected:
11 Buffer() = default;
12
13=== modified file 'include/platform/mir/graphics/display_buffer.h'
14--- include/platform/mir/graphics/display_buffer.h 2013-08-06 11:56:40 +0000
15+++ include/platform/mir/graphics/display_buffer.h 2013-08-29 01:27:06 +0000
16@@ -20,12 +20,15 @@
17 #define MIR_GRAPHICS_DISPLAY_BUFFER_H_
18
19 #include <mir/geometry/rectangle.h>
20+#include <memory>
21
22 namespace mir
23 {
24 namespace graphics
25 {
26
27+class Buffer;
28+
29 /**
30 * Interface to an output framebuffer.
31 */
32@@ -43,6 +46,9 @@
33 /** Posts the DisplayBuffer to the screen. */
34 virtual void post_update() = 0;
35
36+ virtual bool can_bypass() const = 0;
37+ virtual void post_update(std::shared_ptr<Buffer> /* bypass_buf */) {}
38+
39 protected:
40 DisplayBuffer() = default;
41 DisplayBuffer(DisplayBuffer const& c) = delete;
42
43=== modified file 'include/server/mir/compositor/basic_display_buffer_compositor.h'
44--- include/server/mir/compositor/basic_display_buffer_compositor.h 2013-07-30 01:30:15 +0000
45+++ include/server/mir/compositor/basic_display_buffer_compositor.h 2013-08-29 01:27:06 +0000
46@@ -49,7 +49,7 @@
47 mir::geometry::Rectangle const& view_area,
48 std::function<void(std::shared_ptr<void> const&)> save_resource) = 0;
49
50-private:
51+protected:
52 graphics::DisplayBuffer& display_buffer;
53 };
54 }
55
56=== modified file 'include/test/mir_test_doubles/mock_buffer.h'
57--- include/test/mir_test_doubles/mock_buffer.h 2013-07-24 05:22:03 +0000
58+++ include/test/mir_test_doubles/mock_buffer.h 2013-08-29 01:27:06 +0000
59@@ -65,6 +65,8 @@
60
61 MOCK_METHOD0(bind_to_texture, void());
62 MOCK_CONST_METHOD0(id, graphics::BufferID());
63+
64+ MOCK_CONST_METHOD0(can_bypass, bool());
65 };
66
67 }
68
69=== modified file 'include/test/mir_test_doubles/mock_compositing_criteria.h'
70--- include/test/mir_test_doubles/mock_compositing_criteria.h 2013-07-31 02:22:28 +0000
71+++ include/test/mir_test_doubles/mock_compositing_criteria.h 2013-08-29 01:27:06 +0000
72@@ -38,7 +38,6 @@
73 MOCK_CONST_METHOD1(should_be_rendered_in, bool(geometry::Rectangle const&));
74 };
75
76-typedef ::testing::NiceMock<MockCompositingCriteria> StubCompositingCriteria;
77 }
78 }
79 }
80
81=== modified file 'include/test/mir_test_doubles/mock_display_buffer.h'
82--- include/test/mir_test_doubles/mock_display_buffer.h 2013-05-21 15:11:41 +0000
83+++ include/test/mir_test_doubles/mock_display_buffer.h 2013-08-29 01:27:06 +0000
84@@ -37,6 +37,7 @@
85 MOCK_METHOD0(make_current, void());
86 MOCK_METHOD0(release_current, void());
87 MOCK_METHOD0(post_update, void());
88+ MOCK_CONST_METHOD0(can_bypass, bool());
89 };
90
91 }
92
93=== modified file 'include/test/mir_test_doubles/null_display_buffer.h'
94--- include/test/mir_test_doubles/null_display_buffer.h 2013-07-24 05:22:03 +0000
95+++ include/test/mir_test_doubles/null_display_buffer.h 2013-08-29 01:27:06 +0000
96@@ -35,6 +35,7 @@
97 void make_current() {}
98 void release_current() {}
99 void post_update() {}
100+ bool can_bypass() const override { return false; }
101 };
102
103 }
104
105=== modified file 'include/test/mir_test_doubles/stub_buffer.h'
106--- include/test/mir_test_doubles/stub_buffer.h 2013-08-26 03:43:20 +0000
107+++ include/test/mir_test_doubles/stub_buffer.h 2013-08-29 01:27:06 +0000
108@@ -62,6 +62,8 @@
109 }
110 virtual void bind_to_texture() {}
111
112+ virtual bool can_bypass() const override { return false; }
113+
114 geometry::Size const buf_size;
115 geometry::PixelFormat const buf_pixel_format;
116 };
117
118=== added file 'include/test/mir_test_doubles/stub_compositing_criteria.h'
119--- include/test/mir_test_doubles/stub_compositing_criteria.h 1970-01-01 00:00:00 +0000
120+++ include/test/mir_test_doubles/stub_compositing_criteria.h 2013-08-29 01:27:06 +0000
121@@ -0,0 +1,71 @@
122+/*
123+ * Copyright © 2013 Canonical Ltd.
124+ *
125+ * This program is free software: you can redistribute it and/or modify it
126+ * under the terms of the GNU General Public License version 3,
127+ * as published by the Free Software Foundation.
128+ *
129+ * This program is distributed in the hope that it will be useful,
130+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
131+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
132+ * GNU General Public License for more details.
133+ *
134+ * You should have received a copy of the GNU General Public License
135+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
136+ *
137+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
138+ */
139+
140+#ifndef MIR_TEST_DOUBLES_STUB_COMPOSITING_CRITERIA_H_
141+#define MIR_TEST_DOUBLES_STUB_COMPOSITING_CRITERIA_H_
142+
143+#include "mir/compositor/compositing_criteria.h"
144+#include <glm/gtc/matrix_transform.hpp>
145+#include <gmock/gmock.h>
146+
147+namespace mir
148+{
149+namespace test
150+{
151+namespace doubles
152+{
153+
154+class StubCompositingCriteria : public compositor::CompositingCriteria
155+{
156+public:
157+ StubCompositingCriteria(int x, int y, int width, int height,
158+ float opacity=1.0f)
159+ : rect{{x, y}, {width, height}},
160+ opacity(opacity)
161+ {
162+ const glm::mat4 ident;
163+ glm::vec3 size(width, height, 0.0f);
164+ glm::vec3 pos(x + width / 2, y + height / 2, 0.0f);
165+ trans = glm::scale( glm::translate(ident, pos), size);
166+ }
167+
168+ float alpha() const override
169+ {
170+ return opacity;
171+ }
172+
173+ glm::mat4 const& transformation() const override
174+ {
175+ return trans;
176+ }
177+
178+ bool should_be_rendered_in(const mir::geometry::Rectangle &r) const override
179+ {
180+ return rect.overlaps(r);
181+ }
182+
183+private:
184+ mir::geometry::Rectangle rect;
185+ glm::mat4 trans;
186+ float opacity;
187+};
188+
189+} // namespace doubles
190+} // namespace test
191+} // namespace mir
192+#endif // MIR_TEST_DOUBLES_STUB_COMPOSITING_CRITERIA_H_
193
194=== modified file 'src/server/compositor/CMakeLists.txt'
195--- src/server/compositor/CMakeLists.txt 2013-07-30 01:30:15 +0000
196+++ src/server/compositor/CMakeLists.txt 2013-08-29 01:27:06 +0000
197@@ -12,6 +12,7 @@
198 gl_renderer_factory.cpp
199 multi_threaded_compositor.cpp
200 switching_bundle.cpp
201+ bypass.cpp
202 )
203
204 ADD_LIBRARY(
205
206=== added file 'src/server/compositor/bypass.cpp'
207--- src/server/compositor/bypass.cpp 1970-01-01 00:00:00 +0000
208+++ src/server/compositor/bypass.cpp 2013-08-29 01:27:06 +0000
209@@ -0,0 +1,118 @@
210+/*
211+ * Copyright © 2013 Canonical Ltd.
212+ *
213+ * This program is free software: you can redistribute it and/or modify it
214+ * under the terms of the GNU General Public License version 3,
215+ * as published by the Free Software Foundation.
216+ *
217+ * This program is distributed in the hope that it will be useful,
218+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
219+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
220+ * GNU General Public License for more details.
221+ *
222+ * You should have received a copy of the GNU General Public License
223+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
224+ *
225+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
226+ */
227+
228+#include "mir/compositor/compositing_criteria.h"
229+#include "mir/graphics/display_buffer.h"
230+#include "bypass.h"
231+
232+using namespace mir;
233+using namespace mir::compositor;
234+
235+BypassFilter::BypassFilter(const graphics::DisplayBuffer &display_buffer)
236+ : display_buffer(display_buffer)
237+{
238+ const geometry::Rectangle &rect = display_buffer.view_area();
239+ int width = rect.size.width.as_int();
240+ int height = rect.size.height.as_int();
241+
242+ /*
243+ * For a surface to exactly fit the display_buffer, its transformation
244+ * will look exactly like this:
245+ */
246+ fullscreen[0][0] = width;
247+ fullscreen[0][1] = 0.0f;
248+ fullscreen[0][2] = 0.0f;
249+ fullscreen[0][3] = 0.0f;
250+
251+ fullscreen[1][0] = 0.0f;
252+ fullscreen[1][1] = height;
253+ fullscreen[1][2] = 0.0f;
254+ fullscreen[1][3] = 0.0f;
255+
256+ fullscreen[2][0] = 0.0f;
257+ fullscreen[2][1] = 0.0f;
258+ fullscreen[2][2] = 0.0f;
259+ fullscreen[2][3] = 0.0f;
260+
261+ fullscreen[3][0] = rect.top_left.x.as_int() + width / 2;
262+ fullscreen[3][1] = rect.top_left.y.as_int() + height / 2;
263+ fullscreen[3][2] = 0.0f;
264+ fullscreen[3][3] = 1.0f;
265+}
266+
267+bool BypassFilter::operator()(const CompositingCriteria &criteria)
268+{
269+ if (criteria.alpha() != 1.0f)
270+ return false;
271+
272+ if (!all_orthogonal)
273+ return false;
274+
275+ const glm::mat4 &trans = criteria.transformation();
276+ bool orthogonal =
277+ trans[0][1] == 0.0f &&
278+ trans[0][2] == 0.0f &&
279+ trans[0][3] == 0.0f &&
280+ trans[1][0] == 0.0f &&
281+ trans[1][2] == 0.0f &&
282+ trans[1][3] == 0.0f &&
283+ trans[2][0] == 0.0f &&
284+ trans[2][1] == 0.0f &&
285+ trans[2][2] == 0.0f &&
286+ trans[2][3] == 0.0f &&
287+ trans[3][2] == 0.0f &&
288+ trans[3][3] == 1.0f;
289+
290+ // Any weird transformations? Then we can't risk any bypass
291+ if (!orthogonal)
292+ {
293+ all_orthogonal = false;
294+ return false;
295+ }
296+
297+ // This could be replaced by adding a "CompositingCriteria::rect()"
298+ int width = trans[0][0];
299+ int height = trans[1][1];
300+ int x = trans[3][0] - width / 2;
301+ int y = trans[3][1] - height / 2;
302+ geometry::Rectangle window{{x, y}, {width, height}};
303+
304+ // Not weirdly transformed but also not on this monitor? Don't care...
305+ if (!window.overlaps(display_buffer.view_area()))
306+ return false;
307+
308+ // Transformed perfectly to fit the monitor? Bypass!
309+ topmost_fits = criteria.transformation() == fullscreen;
310+ return topmost_fits;
311+}
312+
313+bool BypassFilter::fullscreen_on_top() const
314+{
315+ return all_orthogonal && topmost_fits;
316+}
317+
318+void BypassMatch::operator()(const CompositingCriteria &,
319+ surfaces::BufferStream &stream)
320+{
321+ latest = &stream;
322+}
323+
324+surfaces::BufferStream *BypassMatch::topmost_fullscreen() const
325+{
326+ return latest;
327+}
328
329=== added file 'src/server/compositor/bypass.h'
330--- src/server/compositor/bypass.h 1970-01-01 00:00:00 +0000
331+++ src/server/compositor/bypass.h 2013-08-29 01:27:06 +0000
332@@ -0,0 +1,64 @@
333+/*
334+ * Copyright © 2013 Canonical Ltd.
335+ *
336+ * This program is free software: you can redistribute it and/or modify it
337+ * under the terms of the GNU General Public License version 3,
338+ * as published by the Free Software Foundation.
339+ *
340+ * This program is distributed in the hope that it will be useful,
341+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
342+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
343+ * GNU General Public License for more details.
344+ *
345+ * You should have received a copy of the GNU General Public License
346+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
347+ *
348+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
349+ */
350+
351+#ifndef MIR_COMPOSITOR_BYPASS_H_
352+#define MIR_COMPOSITOR_BYPASS_H_
353+
354+#include "mir/compositor/scene.h"
355+#include "glm/glm.hpp"
356+
357+namespace mir
358+{
359+namespace graphics
360+{
361+class DisplayBuffer;
362+}
363+namespace compositor
364+{
365+class CompositingCriteria;
366+
367+class BypassFilter : public FilterForScene
368+{
369+public:
370+ BypassFilter(const graphics::DisplayBuffer &display_buffer);
371+ bool operator()(const CompositingCriteria &criteria) override;
372+ bool fullscreen_on_top() const;
373+
374+private:
375+ bool all_orthogonal = true;
376+ bool topmost_fits = false;
377+ glm::mat4 fullscreen;
378+ const graphics::DisplayBuffer &display_buffer;
379+};
380+
381+class BypassMatch : public OperatorForScene
382+{
383+public:
384+ void operator()(const CompositingCriteria &,
385+ surfaces::BufferStream &stream) override;
386+ surfaces::BufferStream *topmost_fullscreen() const;
387+
388+private:
389+ // This has to be a pointer. We have no control over BufferStream lifetime
390+ surfaces::BufferStream *latest = nullptr;
391+};
392+
393+} // namespace compositor
394+} // namespace mir
395+
396+#endif // MIR_COMPOSITOR_BYPASS_H_
397
398=== modified file 'src/server/compositor/default_display_buffer_compositor.cpp'
399--- src/server/compositor/default_display_buffer_compositor.cpp 2013-07-31 02:22:28 +0000
400+++ src/server/compositor/default_display_buffer_compositor.cpp 2013-08-29 01:27:06 +0000
401@@ -22,6 +22,12 @@
402 #include "mir/compositor/overlay_renderer.h"
403 #include "mir/compositor/scene.h"
404 #include "mir/compositor/compositing_criteria.h"
405+#include "mir/graphics/display_buffer.h"
406+#include "mir/graphics/buffer.h"
407+#include "mir/surfaces/buffer_stream.h"
408+#include "bypass.h"
409+#include <mutex>
410+#include <cstdlib>
411
412 namespace mc = mir::compositor;
413 namespace mg = mir::graphics;
414@@ -57,6 +63,49 @@
415 {
416 }
417
418+void mc::DefaultDisplayBufferCompositor::composite()
419+{
420+ static bool got_bypass_env = false;
421+ static bool bypass_env = true;
422+ bool bypassed = false;
423+
424+ if (!got_bypass_env)
425+ {
426+ const char *env = getenv("MIR_BYPASS");
427+ if (env != NULL)
428+ bypass_env = env[0] != '0';
429+
430+ got_bypass_env = true;
431+ }
432+
433+ if (bypass_env && display_buffer.can_bypass())
434+ {
435+ std::unique_lock<Scene> lock(*scene);
436+
437+ mc::BypassFilter filter(display_buffer);
438+ mc::BypassMatch match;
439+
440+ // It would be *really* nice if Scene had an iterator to simplify this
441+ scene->for_each_if(filter, match);
442+
443+ if (filter.fullscreen_on_top())
444+ {
445+ auto bypass_buf =
446+ match.topmost_fullscreen()->lock_compositor_buffer();
447+
448+ if (bypass_buf->can_bypass())
449+ {
450+ lock.unlock();
451+ display_buffer.post_update(bypass_buf);
452+ bypassed = true;
453+ }
454+ }
455+ }
456+
457+ if (!bypassed)
458+ mc::BasicDisplayBufferCompositor::composite();
459+}
460+
461 void mc::DefaultDisplayBufferCompositor::compose(
462 mir::geometry::Rectangle const& view_area,
463 std::function<void(std::shared_ptr<void> const&)> save_resource)
464
465=== modified file 'src/server/compositor/default_display_buffer_compositor.h'
466--- src/server/compositor/default_display_buffer_compositor.h 2013-07-23 16:59:10 +0000
467+++ src/server/compositor/default_display_buffer_compositor.h 2013-08-29 01:27:06 +0000
468@@ -43,6 +43,8 @@
469 std::shared_ptr<Renderer> const& renderer,
470 std::shared_ptr<OverlayRenderer> const& overlay_renderer);
471
472+ void composite() override;
473+
474 void compose(
475 mir::geometry::Rectangle const& view_area,
476 std::function<void(std::shared_ptr<void> const&)> save_resource);
477
478=== modified file 'src/server/compositor/temporary_buffers.cpp'
479--- src/server/compositor/temporary_buffers.cpp 2013-07-24 05:13:16 +0000
480+++ src/server/compositor/temporary_buffers.cpp 2013-08-29 01:27:06 +0000
481@@ -93,3 +93,8 @@
482 {
483 return buffer->native_buffer_handle();
484 }
485+
486+bool mc::TemporaryBuffer::can_bypass() const
487+{
488+ return buffer->can_bypass();
489+}
490
491=== modified file 'src/server/compositor/temporary_buffers.h'
492--- src/server/compositor/temporary_buffers.h 2013-07-22 10:28:12 +0000
493+++ src/server/compositor/temporary_buffers.h 2013-08-29 01:27:06 +0000
494@@ -41,6 +41,7 @@
495 mg::BufferID id() const;
496 void bind_to_texture();
497 std::shared_ptr<MirNativeBuffer> native_buffer_handle() const;
498+ bool can_bypass() const override;
499
500 protected:
501 explicit TemporaryBuffer(std::shared_ptr<mg::Buffer> const& real_buffer);
502
503=== modified file 'src/server/graphics/android/buffer.cpp'
504--- src/server/graphics/android/buffer.cpp 2013-07-24 05:22:03 +0000
505+++ src/server/graphics/android/buffer.cpp 2013-08-29 01:27:06 +0000
506@@ -94,6 +94,11 @@
507
508 egl_extensions->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
509 }
510+
511+bool mga::Buffer::can_bypass() const
512+{
513+ return false;
514+}
515
516 std::shared_ptr<ANativeWindowBuffer> mga::Buffer::native_buffer_handle() const
517 {
518
519=== modified file 'src/server/graphics/android/buffer.h'
520--- src/server/graphics/android/buffer.h 2013-07-24 05:22:03 +0000
521+++ src/server/graphics/android/buffer.h 2013-08-29 01:27:06 +0000
522@@ -49,6 +49,7 @@
523 geometry::Stride stride() const;
524 geometry::PixelFormat pixel_format() const;
525 void bind_to_texture();
526+ bool can_bypass() const override;
527 std::shared_ptr<ANativeWindowBuffer> native_buffer_handle() const;
528
529 private:
530
531=== modified file 'src/server/graphics/android/gpu_hwc_android_display_buffer_factory.cpp'
532--- src/server/graphics/android/gpu_hwc_android_display_buffer_factory.cpp 2013-07-15 12:28:23 +0000
533+++ src/server/graphics/android/gpu_hwc_android_display_buffer_factory.cpp 2013-08-29 01:27:06 +0000
534@@ -106,6 +106,11 @@
535 }
536 }
537
538+ bool can_bypass() const override
539+ {
540+ return false;
541+ }
542+
543 protected:
544 std::shared_ptr<mga::AndroidFramebufferWindowQuery> const native_window;
545 EGLDisplay const egl_display;
546
547=== modified file 'src/server/graphics/gbm/gbm_buffer.cpp'
548--- src/server/graphics/gbm/gbm_buffer.cpp 2013-08-08 17:23:29 +0000
549+++ src/server/graphics/gbm/gbm_buffer.cpp 2013-08-29 01:27:06 +0000
550@@ -75,8 +75,12 @@
551 }
552
553 mgg::GBMBuffer::GBMBuffer(std::shared_ptr<gbm_bo> const& handle,
554+ uint32_t bo_flags,
555 std::unique_ptr<BufferTextureBinder> texture_binder)
556- : gbm_handle{handle}, texture_binder{std::move(texture_binder)}, prime_fd{-1}
557+ : gbm_handle{handle},
558+ bo_flags{bo_flags},
559+ texture_binder{std::move(texture_binder)},
560+ prime_fd{-1}
561 {
562 auto device = gbm_bo_get_device(gbm_handle.get());
563 auto gem_handle = gbm_bo_get_handle(gbm_handle.get()).u32;
564@@ -121,10 +125,17 @@
565
566 std::shared_ptr<MirNativeBuffer> mgg::GBMBuffer::native_buffer_handle() const
567 {
568- auto temp = std::make_shared<MirNativeBuffer>();
569+ auto temp = std::make_shared<GBMNativeBuffer>();
570
571 temp->fd_items = 1;
572 temp->fd[0] = prime_fd;
573 temp->stride = stride().as_uint32_t();
574+ temp->bo = gbm_handle.get();
575+
576 return temp;
577 }
578+
579+bool mgg::GBMBuffer::can_bypass() const
580+{
581+ return bo_flags & GBM_BO_USE_SCANOUT;
582+}
583
584=== modified file 'src/server/graphics/gbm/gbm_buffer.h'
585--- src/server/graphics/gbm/gbm_buffer.h 2013-08-08 17:23:29 +0000
586+++ src/server/graphics/gbm/gbm_buffer.h 2013-08-29 01:27:06 +0000
587@@ -34,6 +34,11 @@
588 namespace gbm
589 {
590
591+struct GBMNativeBuffer : MirNativeBuffer
592+{
593+ struct gbm_bo *bo;
594+};
595+
596 geometry::PixelFormat gbm_format_to_mir_format(uint32_t format);
597 uint32_t mir_format_to_gbm_format(geometry::PixelFormat format);
598 enum : uint32_t { invalid_gbm_format = std::numeric_limits<uint32_t>::max() };
599@@ -44,6 +49,7 @@
600 {
601 public:
602 GBMBuffer(std::shared_ptr<gbm_bo> const& handle,
603+ uint32_t bo_flags,
604 std::unique_ptr<BufferTextureBinder> texture_binder);
605 GBMBuffer(const GBMBuffer&) = delete;
606 ~GBMBuffer();
607@@ -60,8 +66,11 @@
608
609 virtual void bind_to_texture();
610
611+ bool can_bypass() const override;
612+
613 private:
614 std::shared_ptr<gbm_bo> const gbm_handle;
615+ uint32_t bo_flags;
616 std::unique_ptr<BufferTextureBinder> const texture_binder;
617 int prime_fd;
618 };
619
620=== modified file 'src/server/graphics/gbm/gbm_buffer_allocator.cpp'
621--- src/server/graphics/gbm/gbm_buffer_allocator.cpp 2013-08-26 07:35:33 +0000
622+++ src/server/graphics/gbm/gbm_buffer_allocator.cpp 2013-08-29 01:27:06 +0000
623@@ -133,6 +133,25 @@
624 if (buffer_properties.usage == BufferUsage::software)
625 bo_flags |= GBM_BO_USE_WRITE;
626
627+ /*
628+ * Bypass is generally only beneficial to hardware buffers where the
629+ * blitting happens on the GPU. For software buffers it is slower to blit
630+ * individual pixels from CPU to GPU memory, so don't do it.
631+ * Also try to avoid allocating scanout buffers for small surfaces that
632+ * are unlikely to ever be fullscreen.
633+ *
634+ * TODO: Be more intelligent about when to apply GBM_BO_USE_SCANOUT. That
635+ * may have to come after buffer reallocation support (surface
636+ * resizing). We may also want to check for
637+ * mir_surface_state_fullscreen later when it's fully wired up.
638+ */
639+ if (buffer_properties.usage == BufferUsage::hardware &&
640+ buffer_properties.size.width.as_uint32_t() >= 800 &&
641+ buffer_properties.size.height.as_uint32_t() >= 600)
642+ {
643+ bo_flags |= GBM_BO_USE_SCANOUT;
644+ }
645+
646 gbm_bo *bo_raw = gbm_bo_create(
647 device,
648 buffer_properties.size.width.as_uint32_t(),
649@@ -149,7 +168,7 @@
650 new EGLImageBufferTextureBinder{bo, egl_extensions}};
651
652 /* Create the GBMBuffer */
653- std::shared_ptr<mg::Buffer> buffer{new GBMBuffer{bo, std::move(texture_binder)}};
654+ std::shared_ptr<mg::Buffer> buffer{new GBMBuffer{bo, bo_flags, std::move(texture_binder)}};
655
656 (*buffer_initializer)(*buffer);
657
658
659=== modified file 'src/server/graphics/gbm/gbm_display_buffer.cpp'
660--- src/server/graphics/gbm/gbm_display_buffer.cpp 2013-07-31 02:22:28 +0000
661+++ src/server/graphics/gbm/gbm_display_buffer.cpp 2013-08-29 01:27:06 +0000
662@@ -20,6 +20,7 @@
663 #include "gbm_platform.h"
664 #include "kms_output.h"
665 #include "mir/graphics/display_report.h"
666+#include "gbm_buffer.h"
667
668 #include <boost/throw_exception.hpp>
669 #include <GLES2/gl2.h>
670@@ -161,16 +162,38 @@
671 return area;
672 }
673
674+bool mgg::GBMDisplayBuffer::can_bypass() const
675+{
676+ return true;
677+}
678+
679 void mgg::GBMDisplayBuffer::post_update()
680 {
681+ post_update(nullptr);
682+}
683+
684+void mgg::GBMDisplayBuffer::post_update(
685+ std::shared_ptr<graphics::Buffer> bypass_buf)
686+{
687 /*
688 * Bring the back buffer to the front and get the buffer object
689 * corresponding to the front buffer.
690 */
691- if (!egl.swap_buffers())
692+ if (!bypass_buf && !egl.swap_buffers())
693 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to perform initial surface buffer swap"));
694
695- auto bufobj = get_front_buffer_object();
696+ mgg::BufferObject *bufobj;
697+ if (bypass_buf)
698+ {
699+ auto native = bypass_buf->native_buffer_handle();
700+ auto gbm_native = static_cast<mgg::GBMNativeBuffer*>(native.get());
701+ bufobj = get_buffer_object(gbm_native->bo);
702+ }
703+ else
704+ {
705+ bufobj = get_front_buffer_object();
706+ }
707+
708 if (!bufobj)
709 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get front buffer object"));
710
711@@ -183,7 +206,8 @@
712 */
713 if (!needs_set_crtc && !schedule_and_wait_for_page_flip(bufobj))
714 {
715- bufobj->release();
716+ if (!bypass_buf)
717+ bufobj->release();
718 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to schedule page flip"));
719 }
720 else if (needs_set_crtc)
721@@ -203,12 +227,31 @@
722 if (last_flipped_bufobj)
723 last_flipped_bufobj->release();
724
725- last_flipped_bufobj = bufobj;
726+ last_flipped_bufobj = bypass_buf ? nullptr : bufobj;
727+
728+ /*
729+ * Keep a reference to the buffer being bypassed for the entire duration
730+ * of the frame. This ensures the buffer doesn't get reused by the client
731+ * prematurely, which would be seen as tearing.
732+ * If not bypassing, then bypass_buf will be nullptr.
733+ */
734+ last_flipped_bypass_buf = bypass_buf;
735 }
736
737 mgg::BufferObject* mgg::GBMDisplayBuffer::get_front_buffer_object()
738 {
739- auto bo = gbm_surface_lock_front_buffer(surface_gbm.get());
740+ auto front = gbm_surface_lock_front_buffer(surface_gbm.get());
741+ auto ret = get_buffer_object(front);
742+
743+ if (!ret)
744+ gbm_surface_release_buffer(surface_gbm.get(), front);
745+
746+ return ret;
747+}
748+
749+mgg::BufferObject* mgg::GBMDisplayBuffer::get_buffer_object(
750+ struct gbm_bo *bo)
751+{
752 if (!bo)
753 return nullptr;
754
755@@ -228,10 +271,7 @@
756 auto ret = drmModeAddFB(drm.fd, area.size.width.as_uint32_t(), area.size.height.as_uint32_t(),
757 24, 32, stride, handle, &fb_id);
758 if (ret)
759- {
760- gbm_surface_release_buffer(surface_gbm.get(), bo);
761 return nullptr;
762- }
763
764 /* Create a BufferObject and associate it with the gbm_bo */
765 bufobj = new BufferObject{surface_gbm.get(), bo, fb_id};
766
767=== modified file 'src/server/graphics/gbm/gbm_display_buffer.h'
768--- src/server/graphics/gbm/gbm_display_buffer.h 2013-07-31 02:22:28 +0000
769+++ src/server/graphics/gbm/gbm_display_buffer.h 2013-08-29 01:27:06 +0000
770@@ -56,13 +56,18 @@
771 void release_current();
772 void post_update();
773
774+ bool can_bypass() const override;
775+ void post_update(std::shared_ptr<graphics::Buffer> bypass_buf) override;
776+
777 void schedule_set_crtc();
778
779 private:
780 BufferObject* get_front_buffer_object();
781+ BufferObject* get_buffer_object(struct gbm_bo *bo);
782 bool schedule_and_wait_for_page_flip(BufferObject* bufobj);
783
784 BufferObject* last_flipped_bufobj;
785+ std::shared_ptr<graphics::Buffer> last_flipped_bypass_buf;
786 std::shared_ptr<GBMPlatform> const platform;
787 std::shared_ptr<DisplayReport> const listener;
788 /* DRM helper from GBMPlatform */
789
790=== modified file 'tests/unit-tests/compositor/CMakeLists.txt'
791--- tests/unit-tests/compositor/CMakeLists.txt 2013-08-02 03:01:12 +0000
792+++ tests/unit-tests/compositor/CMakeLists.txt 2013-08-29 01:27:06 +0000
793@@ -6,6 +6,7 @@
794 ${CMAKE_CURRENT_SOURCE_DIR}/test_multi_threaded_compositor.cpp
795 ${CMAKE_CURRENT_SOURCE_DIR}/test_switching_bundle.cpp
796 ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_renderer.cpp
797+ ${CMAKE_CURRENT_SOURCE_DIR}/test_bypass.cpp
798 )
799
800 set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE)
801
802=== added file 'tests/unit-tests/compositor/test_bypass.cpp'
803--- tests/unit-tests/compositor/test_bypass.cpp 1970-01-01 00:00:00 +0000
804+++ tests/unit-tests/compositor/test_bypass.cpp 2013-08-29 01:27:06 +0000
805@@ -0,0 +1,258 @@
806+/*
807+ * Copyright © 2013 Canonical Ltd.
808+ *
809+ * This program is free software: you can redistribute it and/or modify
810+ * it under the terms of the GNU General Public License version 3 as
811+ * published by the Free Software Foundation.
812+ *
813+ * This program is distributed in the hope that it will be useful,
814+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
815+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
816+ * GNU General Public License for more details.
817+ *
818+ * You should have received a copy of the GNU General Public License
819+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
820+ *
821+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
822+ */
823+
824+#include "mir/compositor/compositing_criteria.h"
825+#include "src/server/compositor/bypass.h"
826+#include "mir_test_doubles/mock_display_buffer.h"
827+#include "mir_test_doubles/stub_buffer_stream.h"
828+#include "mir_test_doubles/stub_compositing_criteria.h"
829+
830+#include <gtest/gtest.h>
831+#include <memory>
832+
833+using namespace testing;
834+using namespace mir::geometry;
835+using namespace mir::compositor;
836+using namespace mir::test::doubles;
837+
838+struct BypassFilterTest : public Test
839+{
840+ BypassFilterTest()
841+ {
842+ monitor_rect[0].top_left = {0, 0};
843+ monitor_rect[0].size = {1920, 1200};
844+ EXPECT_CALL(display_buffer[0], view_area())
845+ .WillRepeatedly(Return(monitor_rect[0]));
846+
847+ monitor_rect[1].top_left = {1920, 0};
848+ monitor_rect[1].size = {1920, 1200};
849+ EXPECT_CALL(display_buffer[1], view_area())
850+ .WillRepeatedly(Return(monitor_rect[1]));
851+ }
852+
853+ Rectangle monitor_rect[2];
854+ MockDisplayBuffer display_buffer[2];
855+};
856+
857+TEST_F(BypassFilterTest, nothing_matches_nothing)
858+{
859+ BypassFilter filter(display_buffer[0]);
860+ BypassMatch match;
861+
862+ EXPECT_FALSE(filter.fullscreen_on_top());
863+ EXPECT_FALSE(match.topmost_fullscreen());
864+}
865+
866+TEST_F(BypassFilterTest, small_window_not_bypassed)
867+{
868+ BypassFilter filter(display_buffer[0]);
869+
870+ StubCompositingCriteria win(12, 34, 56, 78);
871+
872+ EXPECT_FALSE(filter(win));
873+ EXPECT_FALSE(filter.fullscreen_on_top());
874+}
875+
876+TEST_F(BypassFilterTest, single_fullscreen_window_bypassed)
877+{
878+ BypassFilter filter(display_buffer[0]);
879+
880+ StubCompositingCriteria win(0, 0, 1920, 1200);
881+
882+ EXPECT_TRUE(filter(win));
883+ EXPECT_TRUE(filter.fullscreen_on_top());
884+}
885+
886+TEST_F(BypassFilterTest, translucent_fullscreen_window_not_bypassed)
887+{
888+ BypassFilter filter(display_buffer[0]);
889+
890+ StubCompositingCriteria win(0, 0, 1920, 1200, 0.5f);
891+
892+ EXPECT_FALSE(filter(win));
893+ EXPECT_FALSE(filter.fullscreen_on_top());
894+}
895+
896+TEST_F(BypassFilterTest, offset_fullscreen_window_not_bypassed)
897+{
898+ BypassFilter filter(display_buffer[0]);
899+
900+ StubCompositingCriteria win(10, 50, 1920, 1200);
901+
902+ EXPECT_FALSE(filter(win));
903+ EXPECT_FALSE(filter.fullscreen_on_top());
904+}
905+
906+TEST_F(BypassFilterTest, obscured_fullscreen_window_not_bypassed)
907+{
908+ BypassFilter filter(display_buffer[0]);
909+
910+ StubCompositingCriteria fs(0, 0, 1920, 1200);
911+ StubCompositingCriteria small(20, 30, 40, 50);
912+
913+ EXPECT_TRUE(filter(fs));
914+ EXPECT_FALSE(filter(small));
915+ EXPECT_FALSE(filter.fullscreen_on_top());
916+}
917+
918+TEST_F(BypassFilterTest, unobscured_fullscreen_window_bypassed)
919+{
920+ BypassFilter filter(display_buffer[0]);
921+
922+ StubCompositingCriteria fs(0, 0, 1920, 1200);
923+ StubCompositingCriteria small(20, 30, 40, 50);
924+
925+ EXPECT_FALSE(filter(small));
926+ EXPECT_TRUE(filter(fs));
927+ EXPECT_TRUE(filter.fullscreen_on_top());
928+}
929+
930+TEST_F(BypassFilterTest, unobscured_fullscreen_alpha_window_not_bypassed)
931+{
932+ BypassFilter filter(display_buffer[0]);
933+
934+ StubCompositingCriteria fs(0, 0, 1920, 1200, 0.9f);
935+ StubCompositingCriteria small(20, 30, 40, 50);
936+
937+ EXPECT_FALSE(filter(small));
938+ EXPECT_FALSE(filter(fs));
939+ EXPECT_FALSE(filter.fullscreen_on_top());
940+}
941+
942+TEST_F(BypassFilterTest, many_fullscreen_windows_only_bypass_top)
943+{
944+ BypassFilter filter(display_buffer[0]);
945+
946+ StubCompositingCriteria a(0, 0, 1920, 1200);
947+ EXPECT_TRUE(filter(a));
948+ EXPECT_TRUE(filter.fullscreen_on_top());
949+
950+ StubCompositingCriteria b(1, 2, 3, 4);
951+ EXPECT_FALSE(filter(b));
952+ EXPECT_FALSE(filter.fullscreen_on_top());
953+
954+ StubCompositingCriteria c(0, 0, 1920, 1200);
955+ EXPECT_TRUE(filter(c));
956+ EXPECT_TRUE(filter.fullscreen_on_top());
957+
958+ StubCompositingCriteria d(5, 6, 7, 8);
959+ EXPECT_FALSE(filter(d));
960+ EXPECT_FALSE(filter.fullscreen_on_top());
961+
962+ StubCompositingCriteria e(0, 0, 1920, 1200);
963+ EXPECT_TRUE(filter(e));
964+ EXPECT_TRUE(filter.fullscreen_on_top());
965+
966+ StubCompositingCriteria f(9, 10, 11, 12);
967+ EXPECT_FALSE(filter(f));
968+ EXPECT_FALSE(filter.fullscreen_on_top());
969+
970+ StubCompositingCriteria g(0, 0, 1920, 1200);
971+ EXPECT_TRUE(filter(g));
972+ EXPECT_TRUE(filter.fullscreen_on_top());
973+}
974+
975+TEST_F(BypassFilterTest, multimonitor_one_bypassed)
976+{
977+ BypassFilter left(display_buffer[0]);
978+ BypassFilter right(display_buffer[1]);
979+
980+ StubCompositingCriteria fs(1920, 0, 1920, 1200);
981+ StubCompositingCriteria small(20, 30, 40, 50);
982+
983+ EXPECT_FALSE(left(small));
984+ EXPECT_FALSE(left.fullscreen_on_top());
985+ EXPECT_FALSE(left(fs));
986+ EXPECT_FALSE(left.fullscreen_on_top());
987+
988+ EXPECT_FALSE(right(small));
989+ EXPECT_FALSE(right.fullscreen_on_top());
990+ EXPECT_TRUE(right(fs));
991+ EXPECT_TRUE(right.fullscreen_on_top());
992+ EXPECT_FALSE(right(small));
993+ EXPECT_TRUE(right.fullscreen_on_top());
994+}
995+
996+TEST_F(BypassFilterTest, dual_bypass)
997+{
998+ BypassFilter left_filter(display_buffer[0]);
999+ BypassFilter right_filter(display_buffer[1]);
1000+
1001+ StubCompositingCriteria left_win(0, 0, 1920, 1200);
1002+ StubCompositingCriteria right_win(1920, 0, 1920, 1200);
1003+
1004+ EXPECT_TRUE(left_filter(left_win));
1005+ EXPECT_TRUE(left_filter.fullscreen_on_top());
1006+ EXPECT_FALSE(left_filter(right_win));
1007+ EXPECT_TRUE(left_filter.fullscreen_on_top());
1008+
1009+ EXPECT_FALSE(right_filter(left_win));
1010+ EXPECT_FALSE(right_filter.fullscreen_on_top());
1011+ EXPECT_TRUE(right_filter(right_win));
1012+ EXPECT_TRUE(right_filter.fullscreen_on_top());
1013+}
1014+
1015+TEST_F(BypassFilterTest, multimonitor_oversized_no_bypass)
1016+{
1017+ BypassFilter left_filter(display_buffer[0]);
1018+ BypassFilter right_filter(display_buffer[1]);
1019+
1020+ StubCompositingCriteria big_win(0, 0, 3840, 1200);
1021+
1022+ EXPECT_FALSE(left_filter(big_win));
1023+ EXPECT_FALSE(left_filter.fullscreen_on_top());
1024+
1025+ EXPECT_FALSE(right_filter(big_win));
1026+ EXPECT_FALSE(right_filter.fullscreen_on_top());
1027+}
1028+
1029+TEST(BypassMatchTest, defaults_to_null)
1030+{
1031+ BypassMatch match;
1032+
1033+ EXPECT_EQ(nullptr, match.topmost_fullscreen());
1034+}
1035+
1036+TEST(BypassMatchTest, returns_one)
1037+{
1038+ BypassMatch match;
1039+ StubCompositingCriteria win(1, 2, 3, 4);
1040+ StubBufferStream bufs;
1041+
1042+ EXPECT_EQ(nullptr, match.topmost_fullscreen());
1043+ match(win, bufs);
1044+ EXPECT_EQ(&bufs, match.topmost_fullscreen());
1045+}
1046+
1047+TEST(BypassMatchTest, returns_latest)
1048+{
1049+ BypassMatch match;
1050+ StubCompositingCriteria a(1, 2, 3, 4), b(5, 6, 7, 8), c(9, 10, 11, 12);
1051+ StubBufferStream abuf, bbuf, cbuf;
1052+
1053+ EXPECT_EQ(nullptr, match.topmost_fullscreen());
1054+ match(a, abuf);
1055+ EXPECT_EQ(&abuf, match.topmost_fullscreen());
1056+ match(b, bbuf);
1057+ EXPECT_EQ(&bbuf, match.topmost_fullscreen());
1058+ match(c, cbuf);
1059+ EXPECT_EQ(&cbuf, match.topmost_fullscreen());
1060+ EXPECT_EQ(&cbuf, match.topmost_fullscreen());
1061+ EXPECT_EQ(&cbuf, match.topmost_fullscreen());
1062+}
1063+
1064
1065=== modified file 'tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp'
1066--- tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2013-08-23 10:39:40 +0000
1067+++ tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2013-08-29 01:27:06 +0000
1068@@ -29,7 +29,9 @@
1069 #include "mir_test_doubles/mock_display_buffer.h"
1070 #include "mir_test_doubles/mock_buffer_stream.h"
1071 #include "mir_test_doubles/mock_compositing_criteria.h"
1072+#include "mir_test_doubles/stub_compositing_criteria.h"
1073 #include "mir_test_doubles/null_display_buffer.h"
1074+#include "mir_test_doubles/mock_buffer.h"
1075
1076 #include <gmock/gmock.h>
1077 #include <gtest/gtest.h>
1078@@ -76,6 +78,11 @@
1079
1080 void set_change_callback(std::function<void()> const&) {}
1081
1082+ void change(const std::vector<mc::CompositingCriteria*> &surfs)
1083+ {
1084+ surfaces = surfs;
1085+ }
1086+
1087 void lock() {}
1088 void unlock() {}
1089
1090@@ -210,3 +217,287 @@
1091
1092 comp->composite();
1093 }
1094+
1095+TEST(DefaultDisplayBufferCompositor, bypass_skips_composition)
1096+{
1097+ using namespace testing;
1098+
1099+ StubRendererFactory renderer_factory;
1100+ NiceMock<MockOverlayRenderer> overlay_renderer;
1101+
1102+ geom::Rectangle screen{{0, 0}, {1366, 768}};
1103+
1104+ mtd::MockDisplayBuffer display_buffer;
1105+ EXPECT_CALL(display_buffer, view_area())
1106+ .WillRepeatedly(Return(screen));
1107+ EXPECT_CALL(display_buffer, make_current())
1108+ .Times(0);
1109+ EXPECT_CALL(display_buffer, post_update())
1110+ .Times(0);
1111+ EXPECT_CALL(display_buffer, can_bypass())
1112+ .WillRepeatedly(Return(true));
1113+
1114+ mtd::StubCompositingCriteria small(10, 20, 30, 40);
1115+ mtd::StubCompositingCriteria fullscreen(0, 0, 1366, 768);
1116+
1117+ std::vector<mc::CompositingCriteria*> renderable_vec;
1118+ renderable_vec.push_back(&small);
1119+ renderable_vec.push_back(&fullscreen);
1120+
1121+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1122+ .Times(0);
1123+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1124+ .Times(0);
1125+
1126+ FakeScene scene(renderable_vec);
1127+
1128+ auto compositor_buffer = std::make_shared<mtd::MockBuffer>();
1129+ EXPECT_CALL(*compositor_buffer, can_bypass())
1130+ .WillOnce(Return(true));
1131+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1132+ .WillOnce(Return(compositor_buffer));
1133+
1134+ mc::DefaultDisplayBufferCompositorFactory factory(
1135+ mt::fake_shared(scene),
1136+ mt::fake_shared(renderer_factory),
1137+ mt::fake_shared(overlay_renderer));
1138+
1139+ auto comp = factory.create_compositor_for(display_buffer);
1140+
1141+ comp->composite();
1142+}
1143+
1144+TEST(DefaultDisplayBufferCompositor, obscured_fullscreen_does_not_bypass)
1145+{
1146+ using namespace testing;
1147+
1148+ StubRendererFactory renderer_factory;
1149+ NiceMock<MockOverlayRenderer> overlay_renderer;
1150+
1151+ geom::Rectangle screen{{0, 0}, {1366, 768}};
1152+
1153+ mtd::MockDisplayBuffer display_buffer;
1154+ EXPECT_CALL(display_buffer, view_area())
1155+ .WillRepeatedly(Return(screen));
1156+ EXPECT_CALL(display_buffer, make_current())
1157+ .Times(1);
1158+ EXPECT_CALL(display_buffer, post_update())
1159+ .Times(1);
1160+ EXPECT_CALL(display_buffer, can_bypass())
1161+ .WillRepeatedly(Return(true));
1162+
1163+ mtd::StubCompositingCriteria fullscreen(0, 0, 1366, 768);
1164+ mtd::StubCompositingCriteria small(10, 20, 30, 40);
1165+
1166+ std::vector<mc::CompositingCriteria*> renderable_vec;
1167+ renderable_vec.push_back(&fullscreen);
1168+ renderable_vec.push_back(&small);
1169+
1170+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1171+ .Times(1);
1172+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1173+ .Times(1);
1174+
1175+ FakeScene scene(renderable_vec);
1176+
1177+ auto compositor_buffer = std::make_shared<mtd::MockBuffer>();
1178+ EXPECT_CALL(*compositor_buffer, can_bypass())
1179+ .Times(0);
1180+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1181+ .Times(0);
1182+
1183+ mc::DefaultDisplayBufferCompositorFactory factory(
1184+ mt::fake_shared(scene),
1185+ mt::fake_shared(renderer_factory),
1186+ mt::fake_shared(overlay_renderer));
1187+
1188+ auto comp = factory.create_compositor_for(display_buffer);
1189+
1190+ comp->composite();
1191+}
1192+
1193+TEST(DefaultDisplayBufferCompositor, platform_does_not_support_bypass)
1194+{
1195+ using namespace testing;
1196+
1197+ StubRendererFactory renderer_factory;
1198+ NiceMock<MockOverlayRenderer> overlay_renderer;
1199+
1200+ geom::Rectangle screen{{0, 0}, {1366, 768}};
1201+
1202+ mtd::MockDisplayBuffer display_buffer;
1203+ EXPECT_CALL(display_buffer, view_area())
1204+ .WillRepeatedly(Return(screen));
1205+ EXPECT_CALL(display_buffer, make_current())
1206+ .Times(1);
1207+ EXPECT_CALL(display_buffer, post_update())
1208+ .Times(1);
1209+ EXPECT_CALL(display_buffer, can_bypass())
1210+ .WillRepeatedly(Return(false));
1211+
1212+ mtd::StubCompositingCriteria small(10, 20, 30, 40);
1213+ mtd::StubCompositingCriteria fullscreen(0, 0, 1366, 768);
1214+
1215+ std::vector<mc::CompositingCriteria*> renderable_vec;
1216+ renderable_vec.push_back(&small);
1217+ renderable_vec.push_back(&fullscreen);
1218+
1219+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1220+ .Times(1);
1221+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1222+ .Times(1);
1223+
1224+ FakeScene scene(renderable_vec);
1225+
1226+ auto compositor_buffer = std::make_shared<mtd::MockBuffer>();
1227+ EXPECT_CALL(*compositor_buffer, can_bypass())
1228+ .Times(0);
1229+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1230+ .Times(0);
1231+
1232+ mc::DefaultDisplayBufferCompositorFactory factory(
1233+ mt::fake_shared(scene),
1234+ mt::fake_shared(renderer_factory),
1235+ mt::fake_shared(overlay_renderer));
1236+
1237+ auto comp = factory.create_compositor_for(display_buffer);
1238+
1239+ comp->composite();
1240+}
1241+
1242+TEST(DefaultDisplayBufferCompositor, bypass_aborted_for_incompatible_buffers)
1243+{
1244+ using namespace testing;
1245+
1246+ StubRendererFactory renderer_factory;
1247+ NiceMock<MockOverlayRenderer> overlay_renderer;
1248+
1249+ geom::Rectangle screen{{0, 0}, {1366, 768}};
1250+
1251+ mtd::MockDisplayBuffer display_buffer;
1252+ EXPECT_CALL(display_buffer, view_area())
1253+ .WillRepeatedly(Return(screen));
1254+ EXPECT_CALL(display_buffer, make_current())
1255+ .Times(1);
1256+ EXPECT_CALL(display_buffer, post_update())
1257+ .Times(1);
1258+ EXPECT_CALL(display_buffer, can_bypass())
1259+ .WillRepeatedly(Return(true));
1260+
1261+ mtd::StubCompositingCriteria small(10, 20, 30, 40);
1262+ mtd::StubCompositingCriteria fullscreen(0, 0, 1366, 768);
1263+
1264+ std::vector<mc::CompositingCriteria*> renderable_vec;
1265+ renderable_vec.push_back(&small);
1266+ renderable_vec.push_back(&fullscreen);
1267+
1268+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1269+ .Times(1);
1270+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1271+ .Times(1);
1272+
1273+ FakeScene scene(renderable_vec);
1274+
1275+ auto compositor_buffer = std::make_shared<mtd::MockBuffer>();
1276+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1277+ .WillOnce(Return(compositor_buffer));
1278+ EXPECT_CALL(*compositor_buffer, can_bypass())
1279+ .WillRepeatedly(Return(false));
1280+
1281+ mc::DefaultDisplayBufferCompositorFactory factory(
1282+ mt::fake_shared(scene),
1283+ mt::fake_shared(renderer_factory),
1284+ mt::fake_shared(overlay_renderer));
1285+
1286+ auto comp = factory.create_compositor_for(display_buffer);
1287+
1288+ comp->composite();
1289+}
1290+
1291+TEST(DefaultDisplayBufferCompositor, bypass_toggles_seamlessly)
1292+{
1293+ using namespace testing;
1294+
1295+ StubRendererFactory renderer_factory;
1296+ NiceMock<MockOverlayRenderer> overlay_renderer;
1297+
1298+ geom::Rectangle screen{{0, 0}, {1366, 768}};
1299+
1300+ mtd::MockDisplayBuffer display_buffer;
1301+ EXPECT_CALL(display_buffer, view_area())
1302+ .WillRepeatedly(Return(screen));
1303+ EXPECT_CALL(display_buffer, make_current())
1304+ .Times(1);
1305+ EXPECT_CALL(display_buffer, post_update())
1306+ .Times(1);
1307+ EXPECT_CALL(display_buffer, can_bypass())
1308+ .WillRepeatedly(Return(true));
1309+
1310+ mtd::StubCompositingCriteria fullscreen(0, 0, 1366, 768);
1311+ mtd::StubCompositingCriteria small(10, 20, 30, 40);
1312+
1313+ std::vector<mc::CompositingCriteria*> renderable_vec;
1314+ renderable_vec.push_back(&fullscreen);
1315+ renderable_vec.push_back(&small);
1316+
1317+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1318+ .Times(1);
1319+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1320+ .Times(1);
1321+
1322+ FakeScene scene(renderable_vec);
1323+
1324+ auto compositor_buffer = std::make_shared<mtd::MockBuffer>();
1325+ EXPECT_CALL(*compositor_buffer, can_bypass())
1326+ .Times(0);
1327+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1328+ .Times(0);
1329+
1330+ mc::DefaultDisplayBufferCompositorFactory factory(
1331+ mt::fake_shared(scene),
1332+ mt::fake_shared(renderer_factory),
1333+ mt::fake_shared(overlay_renderer));
1334+
1335+ auto comp = factory.create_compositor_for(display_buffer);
1336+
1337+ // Frame 1: small window over fullscreen = no bypass
1338+ comp->composite();
1339+
1340+ // Frame 2: fullscreen over small window = bypass
1341+ renderable_vec.resize(0);
1342+ renderable_vec.push_back(&small);
1343+ renderable_vec.push_back(&fullscreen);
1344+ scene.change(renderable_vec);
1345+ EXPECT_CALL(display_buffer, make_current())
1346+ .Times(0);
1347+ EXPECT_CALL(display_buffer, post_update())
1348+ .Times(0);
1349+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1350+ .Times(0);
1351+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1352+ .Times(0);
1353+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1354+ .WillOnce(Return(compositor_buffer));
1355+ EXPECT_CALL(*compositor_buffer, can_bypass())
1356+ .WillOnce(Return(true));
1357+ comp->composite();
1358+
1359+ // Frame 3: only a small window = no bypass
1360+ renderable_vec.resize(0);
1361+ renderable_vec.push_back(&small);
1362+ scene.change(renderable_vec);
1363+ EXPECT_CALL(display_buffer, make_current())
1364+ .Times(1);
1365+ EXPECT_CALL(display_buffer, post_update())
1366+ .Times(1);
1367+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(small),_))
1368+ .Times(1);
1369+ EXPECT_CALL(renderer_factory.mock_renderer, render(_,Ref(fullscreen),_))
1370+ .Times(0);
1371+ EXPECT_CALL(scene.stub_stream, lock_compositor_buffer())
1372+ .Times(0);
1373+ EXPECT_CALL(*compositor_buffer, can_bypass())
1374+ .Times(0);
1375+ comp->composite();
1376+}
1377+

Subscribers

People subscribed via source and target branches