Mir

Merge lp:~alan-griffiths/mir/fix-1516670 into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 3131
Proposed branch: lp:~alan-griffiths/mir/fix-1516670
Merge into: lp:mir
Diff against target: 192 lines (+78/-37)
3 files modified
src/server/graphics/nested/display.cpp (+67/-30)
src/server/graphics/nested/display.h (+2/-0)
tests/acceptance-tests/test_nested_mir.cpp (+9/-7)
To merge this branch: bzr merge lp:~alan-griffiths/mir/fix-1516670
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Needs Information
Andreas Pokorny (community) Approve
Alberto Aguirre Pending
Review via email: mp+278184@code.launchpad.net

This proposal supersedes a proposal from 2015-11-17.

Commit message

nested: For clone mode use a fullscreen surface for the output with the largest intersection with the bounding rectangle

Description of the change

nested: For clone mode use a fullscreen surface for the output with the largest intersection with the bounding rectangle

The previous implementation attempted to cover the bounding rectangle of all outputs with a surface that was fullscreen on the first - which led to inconsistent states.

The surface creation logic has also been reworked to reuse existing surfaces where possible, which makes the server interaction less racy (I suspect there are some races still, but this makes plugging and replugging a lot more stable on the phone).

I also added some information to the surface name (which can be helpful when debugging window management in the host server).

To post a comment you must log in.
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
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Having a stack of overlapping fullscreen surfaces in the host server introduces some visual artifacts (and inefficiencies).

So this is clearly not the ideal solution - just less broken than what we currently have.

There are a number of approaches for a better solution:

1. use non-fullscreen surfaces in nested mode and either:
1.1. display only the intersection of all outputs
1.2. display the bounding rectangle of all outputs

2. use only fullscreen surfaces and either:
2.1. select the "smallest" display
2.2. select the "largest" display

I'm not yet convinced by any of the above options, but favour of sticking with "fullscreen only" when nesting.

Revision history for this message
Alberto Aguirre (albaguirre) wrote : Posted in a previous version of this proposal

Are the visual artifacts on phone/desktop/both?

And what are the cases where outputs overlap?

We could also potentially run into max texture size issues for high res displays if we attempt to do 1.2.

In any case, a fullscreen surface per output makes sense to me.

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

> Are the visual artifacts on phone/desktop/both?

Both, but easier to see on phone.

> And what are the cases where outputs overlap?

When displays of different sizes are "cloned".

So, with an external monitor attached to a mako:

$ sudo stop lightdm
$ bin/mir_demo_server --window-manager system-compositor&
$ bin/mir_demo_standalone_render_surfaces --no-file --host $XDG_RUNTIME_DIR/mir_socket --display-config clone

On the external monitor the fullscreen for the phone output is overlayed[1] on top of the fullscreen for the external monitor and the edge is visible as the flying surfaces it (as the rendering is not quite in sync.)

> We could also potentially run into max texture size issues for high res
> displays if we attempt to do 1.2.

Yep.

> In any case, a fullscreen surface per output makes sense to me.

[1] I'm not sure if this ordering is guaranteed, but was consistent in my limited testing.

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
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

hm I think this is an improvement but in clone mode we should use the smallest screen size for each surface and have the output scale.

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

Ok overall after looking and testing (on kms).. our default display configuration for cloned seems to be wrong. It should figure out a suitable intersection and try to select suitable modes on each output, instead of just aligning top_left. If that would be in place it would not matter if we pick the maximum overlap or intersection. (The former might be a better solution then).

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

Note that following scenario is still not working for me (but I am not sure if this MP was supposed to fix this):

Internal monitor 1280x800
External monitor 1920x1080

In different shells:
$ sudo bin/mir_demo_server --arw-file -f /tmp/mir_host
$ bin/mir_demo_server --host-socket /tmp/mir_host -f /tmp/mir_nested --vt 1
$ bin/mir_demo_client_display_config -m /tmp/mir_nested

Should start in clone mode, press 'h', press 'c' again. After last 'c' we should expect to go back to the original setup but we get into a strange setup instead. There seem to be two "framebuffer" surfaces, the first sized for the internal screen and the second for the external. On the external screen the two FB surfaces overlap, with the internal one being in front and hiding the top left part of the external. The client surface is only drawn on the external FB surface.

Also note that both the old and the new code don't handle properly cases where one output doesn't fully contain the other (e.g. 100x100+0+0 100x100+50+50). It's not a common case but we should (eventually) either support it or explicitly prohibit it.

> area.intersection_with(output.extents()))

Since area is the bounding rectangle of all outputs, isn't this expression equivalent to just output.extents()?

@Andreas
> hm I think this is an improvement but in clone mode we should use the smallest screen size
> for each surface and have the output scale.
...
> It should figure out a suitable intersection and try to select suitable modes on each output,
> instead of just aligning top_left.

Fair enough, we should probably rename the current clone policy to '(max)overlap' and implement a new 'clone' one. In any case there are both valid policies and should be properly supported. Plus there are cases where we can't 'clone' because there aren't any matching modes, so we need to fall back to 'overlap' anyway.

review: Needs Information
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 'src/server/graphics/nested/display.cpp'
2--- src/server/graphics/nested/display.cpp 2015-11-18 12:38:30 +0000
3+++ src/server/graphics/nested/display.cpp 2015-11-25 23:12:50 +0000
4@@ -34,6 +34,7 @@
5
6 #include <boost/throw_exception.hpp>
7 #include <stdexcept>
8+#include <sstream>
9
10 namespace mi = mir::input;
11 namespace mg = mir::graphics;
12@@ -147,6 +148,11 @@
13 return std::chrono::milliseconds::zero();
14 }
15
16+geom::Rectangle mgn::detail::DisplaySyncGroup::view_area() const
17+{
18+ return output->view_area();
19+}
20+
21 mgn::Display::Display(
22 std::shared_ptr<mg::Platform> const& platform,
23 std::shared_ptr<HostConnection> const& connection,
24@@ -203,11 +209,16 @@
25 void mgn::Display::configure(mg::DisplayConfiguration const& configuration)
26 {
27 std::lock_guard<std::mutex> lock(configuration_mutex);
28- if (*current_configuration != configuration)
29- {
30- apply_to_connection(configuration);
31- create_surfaces(configuration);
32- }
33+ apply_to_connection(configuration);
34+ create_surfaces(configuration);
35+}
36+
37+namespace
38+{
39+long area_of(geom::Rectangle const& rect)
40+{
41+ return static_cast<long>(rect.size.width.as_int())*rect.size.height.as_int();
42+}
43 }
44
45 void mgn::Display::create_surfaces(mg::DisplayConfiguration const& configuration)
46@@ -223,35 +234,61 @@
47 unique_outputs.for_each_group(
48 [&](mg::OverlappingOutputGroup const& group)
49 {
50- bool have_output_for_group = false;
51- geometry::Rectangle const& area = group.bounding_rectangle();
52- group.for_each_output([&](mg::DisplayConfigurationOutput output)
53+ geometry::Rectangle const area = group.bounding_rectangle();
54+
55+ long max_overlap_area = 0;
56+ mg::DisplayConfigurationOutput best_output;
57+
58+ group.for_each_output([&](mg::DisplayConfigurationOutput const& output)
59 {
60- if (!have_output_for_group)
61+ if (area_of(area.intersection_with(output.extents())) > max_overlap_area)
62 {
63- auto const& egl_config_format = output.current_format;
64-
65- complete_display_initialization(egl_config_format);
66-
67- auto const host_surface = connection->create_surface(
68- area.size.width.as_int(),
69- area.size.height.as_int(),
70- egl_config_format,
71- "Mir nested display",
72- mir_buffer_usage_hardware,
73- static_cast<uint32_t>(output.id.as_value()));
74-
75- result[output.id] = std::make_shared<mgn::detail::DisplaySyncGroup>(
76- std::make_shared<mgn::detail::DisplayBuffer>(
77- egl_display,
78- host_surface,
79- area,
80- dispatcher,
81- cursor_listener,
82- output.current_format));
83- have_output_for_group = true;
84+ max_overlap_area = area_of(area.intersection_with(output.extents()));
85+ best_output = output;
86 }
87 });
88+
89+ auto const& egl_config_format = best_output.current_format;
90+ geometry::Rectangle const& extents = best_output.extents();
91+
92+ auto& display_buffer = result[best_output.id];
93+
94+ {
95+ std::unique_lock<std::mutex> lock(outputs_mutex);
96+ display_buffer = outputs[best_output.id];
97+ }
98+
99+ if (display_buffer)
100+ {
101+ if (display_buffer->view_area() != extents)
102+ display_buffer.reset();
103+ }
104+
105+ if (!display_buffer)
106+ {
107+ complete_display_initialization(egl_config_format);
108+
109+ std::ostringstream surface_title;
110+
111+ surface_title << "Mir nested display for output #" << best_output.id.as_value();
112+
113+ auto const host_surface = connection->create_surface(
114+ extents.size.width.as_int(),
115+ extents.size.height.as_int(),
116+ egl_config_format,
117+ surface_title.str().c_str(),
118+ mir_buffer_usage_hardware,
119+ static_cast<uint32_t>(best_output.id.as_value()));
120+
121+ display_buffer = std::make_shared<mgn::detail::DisplaySyncGroup>(
122+ std::make_shared<mgn::detail::DisplayBuffer>(
123+ egl_display,
124+ host_surface,
125+ extents,
126+ dispatcher,
127+ cursor_listener,
128+ best_output.current_format));
129+ }
130 });
131
132 {
133
134=== modified file 'src/server/graphics/nested/display.h'
135--- src/server/graphics/nested/display.h 2015-10-29 02:15:05 +0000
136+++ src/server/graphics/nested/display.h 2015-11-25 23:12:50 +0000
137@@ -100,6 +100,8 @@
138 void for_each_display_buffer(std::function<void(graphics::DisplayBuffer&)> const&) override;
139 void post() override;
140 std::chrono::milliseconds recommended_sleep() const override;
141+
142+ geometry::Rectangle view_area() const;
143 private:
144 std::shared_ptr<detail::DisplayBuffer> const output;
145 };
146
147=== modified file 'tests/acceptance-tests/test_nested_mir.cpp'
148--- tests/acceptance-tests/test_nested_mir.cpp 2015-11-20 10:46:36 +0000
149+++ tests/acceptance-tests/test_nested_mir.cpp 2015-11-25 23:12:50 +0000
150@@ -111,7 +111,7 @@
151 std::vector<geom::Rectangle> const display_geometry
152 {
153 {{ 0, 0}, { 640, 480}},
154- {{480, 0}, {1920, 1080}}
155+ {{640, 0}, {1920, 1080}}
156 };
157
158 std::chrono::seconds const timeout{10};
159@@ -410,12 +410,13 @@
160 // would be included in one of the later counts and cause a test failure.
161 Mock::VerifyAndClearExpectations(mock_session_mediator_report.get());
162
163- // One post when surface drawn
164+ // One post on each output when surface drawn
165 {
166 mt::WaitCondition wait;
167
168- EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(1)
169- .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); }));
170+ EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(2)
171+ .WillOnce(InvokeWithoutArgs([]{}))
172+ .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); }));
173
174 mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
175
176@@ -423,12 +424,13 @@
177 Mock::VerifyAndClearExpectations(mock_session_mediator_report.get());
178 }
179
180- // One post when surface released
181+ // One post on each output when surface released
182 {
183 mt::WaitCondition wait;
184
185- EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(1)
186- .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); }));
187+ EXPECT_CALL(*mock_session_mediator_report, session_submit_buffer_called(_)).Times(2)
188+ .WillOnce(InvokeWithoutArgs([]{}))
189+ .WillOnce(InvokeWithoutArgs([&] { wait.wake_up_everyone(); }));
190
191 mir_surface_release_sync(surface);
192 mir_connection_release(connection);

Subscribers

People subscribed via source and target branches