Merge lp:~vanvugt/mir/bypass into lp:~mir-team/mir/trunk
- bypass
- Merge into trunk
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 | ||||
Related bugs: |
|
||||
Related blueprints: |
Xorg on Mir
(High)
|
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.
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
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.
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.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:924
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
A quick first pass:
37 + virtual void post_update(
DisplayBuffer should be a pure abstract (interface) class.
701 + last_flipped_
The variable isn't used anywhere else.
673 + auto native = bypass_
674 + struct gbm_bo *bo = *(struct gbm_bo*
675 + bufobj = get_buffer_
Since we are making the assumption that the mg::Buffer is a GBMBuffer, why not cast to a GBMBuffer and provide a GBMBuffer:
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:
GBMBuffer:
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
Alexandros,
1. Purifying: virtual void post_update(
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_
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_
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal | # |
> 1. Purifying: virtual void post_update(
> 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_
> 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_
> 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:
235 +BypassFilter:
...and at other points
'T const&' is the preferred style.
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_
3. Ugly casting: Fixed as you suggested
4. Tconst&: Disagree
I've been using "const T&" ever since I raised the issue;
https:/
https:/
I hoped that others would follow, but the conversation went quiet.
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:/
> https:/
> 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://
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
50 -private:
51 +protected:
BasicDisplayBuf
Existing issue here: What on earth does "void BasicDisplayBuf
But this MP is probably not the place to address this (unless a better design makes other things simpler).
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:927
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:927
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal | # |
416 +void mc::DefaultDisp
417 +{
418 + bool bypassed = false;
419 +
420 + if (display_
421 + {
422 + mc::BypassFilter filter(
423 + mc::BypassMatch match;
424 +
425 + // It would be *really* nice if Scene had an iterator to simplify this
426 + scene->
427 +
428 + if (filter.
429 + {
430 + auto bypass_buf =
431 + match.topmost_
432 +
433 + if (bypass_
434 + {
435 + display_
436 + bypassed = true;
437 + }
438 + }
439 + }
440 +
441 + if (!bypassed)
442 + mc::BasicDispla
443 +}
On an initial reading this looks as though it violates "Tell, Don't Ask".
I'd rather it read:
void mc::DefaultDisp
{
if (!display_
}
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"
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.)
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.
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 BasicDisplayBuf
I dislike BypassFilter/
On first read I like your proposal of DisplayBuffer:
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().
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
-"it is"
+"in its"
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:930
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Robert Ancell (robert-ancell) wrote : Posted in a previous version of this proposal | # |
When testing with ppa:mir-
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-
[texture] texture-
[texture] texture-
[shading] shading=gouraud: FPS: 248 FrameTime: 4.032 ms
[shading] shading=
[shading] shading=phong: FPS: 169 FrameTime: 5.917 ms
[bump] bump-render=
[bump] bump-render=
[bump] bump-render=height: FPS: 251 FrameTime: 3.984 ms
[effect2d] kernel=
[effect2d] kernel=
[pulsar] light=false:
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-
[texture] texture-
[texture] texture-
[shading] shading=gouraud: FPS: 101 FrameTime: 9.901 ms
[shading] shading=
[shading] shading=phong: FPS: 105 FrameTime: 9.524 ms
[bump] bump-render=
[bump] bump-render=
[bump] bump-render=height: FPS: 117 FrameTime: 8.547 ms
[effect2d] kernel=
[effect2d] kernel=
[pulsar] light=false:
(both crash after pulsar due to bug 1203742)
Robert Ancell (robert-ancell) : | # |
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)
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
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.
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.
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
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/
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.
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...
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:932
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:934
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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_
(b) Run a fullscreen software client.
The failure is:
terminate called after throwing an instance of 'boost:
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.
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.
Daniel van Vugt (vanvugt) : | # |
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.
Robert Ancell (robert-ancell) : | # |
Robert Ancell (robert-ancell) wrote : | # |
Passed testing on the QA-8
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:936
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:938
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
There are still design issues around BasicDisplayBuf
But some of that is pre-existing and all of it is better sorted after landing this stuff.
Alan Griffiths (alan-griffiths) wrote : | # |
I've seen that test failure before this morning. I thought it a fluke: rebuild...
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:938
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Robert Ancell (robert-ancell) wrote : | # |
On ps-radeon-hd5750-le this branch is failing with screen corruption.
A standard build works fine:
http://
A build with just bypass fails (from ppa:mir-
http://
Robert Ancell (robert-ancell) : | # |
Robert Ancell (robert-ancell) wrote : | # |
Image showing this machine running openarena: http://
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...
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:943
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:946
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
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 | + |
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.