Mir

Merge lp:~kdub/mir/add-streams-to-surface into lp:mir

Proposed by Kevin DuBois on 2015-05-21
Status: Merged
Approved by: Daniel van Vugt on 2015-06-05
Approved revision: 2575
Merged at revision: 2629
Proposed branch: lp:~kdub/mir/add-streams-to-surface
Merge into: lp:mir
Diff against target: 924 lines (+426/-109)
10 files modified
include/server/mir/scene/surface.h (+11/-2)
src/server/scene/basic_surface.cpp (+50/-21)
src/server/scene/basic_surface.h (+4/-1)
src/server/scene/surface_stack.cpp (+13/-8)
tests/acceptance-tests/test_surfaces_with_output_id.cpp (+6/-1)
tests/include/mir_test_doubles/stub_scene_surface.h (+2/-1)
tests/integration-tests/surface_composition.cpp (+1/-1)
tests/unit-tests/scene/test_basic_surface.cpp (+252/-31)
tests/unit-tests/scene/test_surface.cpp (+6/-2)
tests/unit-tests/scene/test_surface_stack.cpp (+81/-41)
To merge this branch: bzr merge lp:~kdub/mir/add-streams-to-surface
Reviewer Review Type Date Requested Status
Daniel van Vugt 2015-05-21 Abstain on 2015-06-05
PS Jenkins bot continuous-integration Approve on 2015-06-04
Alberto Aguirre Approve on 2015-06-03
Alexandros Frantzis (community) Needs Fixing on 2015-06-02
Chris Halse Rogers Approve on 2015-06-01
Cemil Azizoglu (community) Approve on 2015-05-28
Robert Carr (community) Approve on 2015-05-27
Alan Griffiths Approve on 2015-05-27
Review via email: mp+259776@code.launchpad.net

Commit Message

provide a way to add multiple streams to ms::Surface. ms::Surface now generates a list-of-renderables instead of a single renderable.

Description of the Change

provide a way to add multiple streams to ms::Surface. ms::Surface now generates a list-of-renderables instead of a single renderable.

Most of the 'primary buffer stream' concept is rooted out, although I address the surface-buffer-access in another branch, and resize currently resizes the primary buffer stream, which should be rooted out soon. In the meantime, disallow removing the primary stream.

To post a comment you must log in.
Daniel van Vugt (vanvugt) wrote :

Architecturally I find this confusing. When does a surface have multiple buffer streams?... As opposed to sub-surfaces? Or is this a replacement for the sub-surface concept?

review: Needs Information
Kevin DuBois (kdub) wrote :

It has multiple buffer streams when the client adds them. Also, the server (via the window manager) would be able to add additional server-side buffer streams to the surface (in the same sort of way that the demo-shell canonical window manager does)

It is mostly useful in the case where a client has on Surface (in the concept of the "spec document"), but finds it convenient to have multiple streams/multiple contexts that are composed together by the servers compositor. (eg, the nested server passthrough, video, and things that do some sort of client-side decoration, maybe browsers).

Its a bit clearer to think about ms::Surface as "Window" (with inputs, and relation to other "Windows" per the surface spec), and the mc::BufferStreams as "Surfaces", but our renaming hasn't caught up yet.

Alan Griffiths (alan-griffiths) wrote :

23 +struct StreamInfo
24 +{
25 + std::shared_ptr<compositor::BufferStream> stream;
26 + geometry::Displacement position;
27 +};

I'm still thinking about whether the position should be an attribute of the stream.

Kevin DuBois (kdub) wrote :

> 23 +struct StreamInfo
> 24 +{
> 25 + std::shared_ptr<compositor::BufferStream> stream;
> 26 + geometry::Displacement position;
> 27 +};
>
> I'm still thinking about whether the position should be an attribute of the
> stream.

Could this be because I (poorly) named the displacement "position"?

Alan Griffiths (alan-griffiths) wrote :

23 +struct StreamInfo
24 +{
25 + std::shared_ptr<compositor::BufferStream> stream;
26 + geometry::Displacement displacement;
27 +};

I'm still thinking about whether the displacement should be an attribute of the stream.

Alan Griffiths (alan-griffiths) wrote :

OK

review: Approve
Robert Carr (robertcarr) wrote :

LGTM

review: Approve
Daniel van Vugt (vanvugt) wrote :

I'm not sure this design is sensible, as demonstrated in:

23 +struct StreamInfo
24 +{
25 + std::shared_ptr<compositor::BufferStream> stream;
26 + geometry::Displacement displacement;
27 +};

"displacement" is not enough for non-trivial cases because most streams won't be the primary client area so won't respond to resize() in the same way as the client stream.

Rather than specifying displacement I think a generic rectangle (size and displacement) makes more sense. Also each stream needs some indicator of its purpose so it gets handled differently during resizing. e.g. the client area matches the window dimensions but a titlebar's height never changes. Also the titlebar stream is ideally sized in _physical_ units (mir::geometry::Length) while the client stream in pixel units. To cover these cases you need resize() to be overridable because the alternative of hard-coding all the types and their behaviours is worse.

It looks like we're re-inventing the sub-surface concept in a less elegant way here and it's going to rapidly become very painful to manage. Better would be to relate surfaces together (as planned already) rather than introduce two completely different parent/child/sibling systems. Let each surface own one stream and just add new surface types/relationships as required.

review: Disapprove
Daniel van Vugt (vanvugt) wrote :

We should also continue refining the Surface classes so that BasicSurface becomes truly basic. Then there will be very little difference between BufferStream and a BasicSurface and it's less tempting to make BasicSurface more complex.

Alan Griffiths (alan-griffiths) wrote :

> Rather than specifying displacement I think a generic rectangle (size and
> displacement) makes more sense. Also each stream needs some indicator of its
> purpose so it gets handled differently during resizing...

That is another step down the slope towards a generic layout manager. We either do the minimum that could possibly work or have to consider the existing solution domain of layout managers (allowing the specification of anchors, insets, fills, weights, flow, etc.).

I thought we'd already agreed to start with the minimal support needed for nested and get that landed - and that's what this MP does.

Kevin DuBois (kdub) wrote :

I had hoped the disapprovals and suggestions for sweeping improvements would have come in the first branch in this line of work (back here: https://code.launchpad.net/~kdub/mir/RFC-surface-arrangements/+merge/254447)...

So for resize, this is a snag that's left to work out before the work is completed. A position+size probably makes sense as a per-stream attribute.

A stream's purpose is to show content on the screen. The server doesn't know about the relations between the client streams in the same surface on purpose, this is for the client to work out the best way to present the content on the Surface. Multiple streams aren't "subsurfaces" (although admittedly I don't remember that conversation in detail); they are ways for a surface to present more than one stream in their surface out of convenience or necessity.

The relationships between surfaces are captured in the spec document. The idea here is that the client can arrange its own drawing.

Kevin DuBois (kdub) wrote :

> We should also continue refining the Surface classes so that BasicSurface
> becomes truly basic. Then there will be very little difference between
> BufferStream and a BasicSurface and it's less tempting to make BasicSurface
> more complex.

Separating the Surface/BufferStream responsibilites has already gone a long way to reduce quite a few pass-through functions that Surface need not have had. It has seemed a good distinction to make just in the cleanups that it has unlocked. A surface has input, graphics content, and window management concerns, and a BufferStream is just concerned with the graphics content.

Kevin DuBois (kdub) wrote :

@Alan, right, the primary goal here is to support the plan for nested passthrough. I hope that I didn't muddy the waters too much by talking about other potential (future) uses of allowing one surface to have multiple drawing contexts.

Cemil Azizoglu (cemil-azizoglu) wrote :

Nested passthrough is (obviously) very important future to have. And we want to get there as fast as we can. I'm ok concentrating on that use-case and letting this through even if things have to change (slightly) in the future for other use-cases (as we agreed in Dallas, we should prevent paralysis - this is software and we can always change it). As Kevin mentioned there was already agreement on the RFC so changes should not be all that drastic.

Kevin, perhaps 'position' can be added (hopefully it'll suffice to remove the 'Disapprove').

review: Approve
Daniel van Vugt (vanvugt) wrote :

The resizing and placement issue is a big one. We don't want Mir to be a generic layout manager, so position should go away. Once it goes away you have an immediate need for the shell to provide the placement logic instead (e.g. a titlebar if displayed at all is usually at the top with fixed height and variable width). If you provide so much as a "displacement" you also have to provide all the logic describing what gets painted between and around the buffer streams, how they're blended etc. So not a problem Mir should solve in its core.

Using the word "paralysis" is an excuse for making short term progress at the expense of Mir's longer term. Sometimes large charges are too large and interwoven to be feasibly undone. So you need to catch architectural issues early and stop them taking hold.

"Needs fixing" in the least. Mir should never express any layout information for the inter-stream structure of a window ("displacement"). That's the shell's job. The shell defines how streams are combined. Mir's default should be just to display the client area / main stream.

(footnotes)
I again have to remind myself that when you say "surface" it means window. And what we call "BufferStream" is what was originally a simple surface. Transpose the terminology mentally and it's OK, but I hope we can change it in reality eventually too.

From a high-level perspective this feels dirty to me. We're adding piles of complexity to implement nested bypass which itself may only be a short-term problem. Today it would save us two frames, tomorrow after 'ddouble' lands nested bypass will only save us one frame. In the longer term if people build other shells with a focus on performance and power management then nesting is likely to be skipped completely. So the value of solving this problem and justification in adding complexity is shrinking.

review: Needs Fixing
Daniel van Vugt (vanvugt) wrote :

"displacement" should be replaced by an enum of the stream purpose/type for the shell to interpret as it sees fit:

   enum StreamType {client, titlebar, icon, ...}

Then your collection of streams is just a map between type and stream:

   std::unordered_map<StreamType, std::shared_ptr<compositor::BufferStream>> stream;

So primarily you will do most things with "stream[client]". The other types are optional.

Kevin DuBois (kdub) wrote :

> The resizing and placement issue is a big one. We don't want Mir to be a
> generic layout manager, so position should go away. Once it goes away you have
> an immediate need for the shell to provide the placement logic instead (e.g. a
> titlebar if displayed at all is usually at the top with fixed height and
> variable width). If you provide so much as a "displacement" you also have to
> provide all the logic describing what gets painted between and around the
> buffer streams, how they're blended etc. So not a problem Mir should solve in
> its core.

I agree that the resizing and placement is a big issue; and having streams within a surface without absolute coordinates allows the client to have a guarantee that its contexts will be composited in a certain way, and allows the server to still manage the windowing (ms::Surface) positions.

If the client is to offload simple compositing work, it needs to guarantee that its multiple graphics contexts (streams) get painted within its Surface in a certain way. It took a bit of tinkering to get to this point, but displacement (as opposed to an absolute position) keeps the window management contained, as the positioning is always relative to the decision of the window manager. The structure of the buffer streams is more of an internal way that the client would like to offload work to the server; as opposed to influencing window management policy.

Ideally, the shell could crop the surfaces that overhang, but in the meantime (in the upcoming branches), the window managers have the option to just reject requests that exceed the bounds of the surface. Crop-related tasks are in the backlog and would mean less rejected relative placement requests.

So, part of this line-of-MPs goal is explicitly NOT to give the clients a backdoor that they can use to breech the WM policy. (which hopefully addresses the architectural concerns)

Chris Halse Rogers (raof) wrote :

69 + for(auto info : streams)
70 + visible |= info.stream->has_submitted_buffer();

I don't think this is the right logic; a surface still has a “main” buffer stream, and I think that should determine visibility. (And, later, should gate a whole bunch of state changes).

178 + return std::move(list);

Pessimization: this breaks copy elision. Clang 3.7 generates a warning for this.

Needs fixing for the visibility calculation.

review: Needs Fixing
Chris Halse Rogers (raof) wrote :

Additionally: I'm not sure what's difficult about resize. With the current behaviour when resizing clients the subsurfaces will jump around a bit because there's no synchronisation for those updates, but this synchronisation is a conceptually simple problem to solve that we've deliberately punted on for the first iteration.

Of course, that's a lie; *currently* there's no way for a client to currently specify >1 buffer stream for its surface ☺.

Hm. Now that I've reread the RFC branches your visibility calculation is correct for some use-cases. I don't like that, but I don't have a better suggestion at the moment.

review: Approve
Daniel van Vugt (vanvugt) wrote :

(0) If you think back to Brussels we realized there are three modes of decoration and the two dominant modes are not the one being addressed here (not that they need to be):

 * "Client" decorates itself (e.g. GTK 3)
 * "Server" decorates clients (using simple data like a title string and button mask, e.g. X window managers and Microsoft Windows).
 * "Hybrid": Server combines multiple client surface/streams into a single window (e.g. menu blended into the titlebar). Server still needs to add some decoration around the combined streams (e.g. titlebar background/rounding, window frame).

This proposal addresses the hybrid mode. And while it is a real use case I want to make sure we don't go designing something that gets too much in the way of the pure client or server modes.

(1) X apps (and possibly others) would like to have a stream/image for storing their icons. In fact it's the only kind of secondary image/stream that actually exists as a real use case already, so we should try to cover that one. It needs to support streams that do not have any displacement relative to the surface. How should we represent such a null displacement? Just add a type enum and ignore displacement for certain types?

review: Needs Information
Chris Halse Rogers (raof) wrote :

You're thinking of a fundamentally different problem. This isn't about
decoration at all, and this interface won't be used to provide our
funky decoration options.

This is about a client delegating some of it's *internal* compositing
needs to Mir. The motivating use-case is nested-bypass (where the
nested server delegates its compositing to the host Mir for simple
cases). This sort of interface is also necessary for proper use of
hardware overlays by clients.

This is why the window management code doesn't have, or need, any
influence on the client decisions here - this is purely an optimisation
for something the app could have done itself.

Daniel van Vugt (vanvugt) wrote :

You mean this is for a video overlay (YUV) sitting above some client-rendered controls (RGB)? That sounds reasonable then.

But if we're not thinking about decorations yet then we should. We don't want to re-invent the wheel and an entirely separate API when what's proposed here is roughly what we've been talking about for hybrid decorations all along.

I guess what I'm asking for is a future enhancement to this then. And displacement is all we care for right now.

review: Abstain
Daniel van Vugt (vanvugt) wrote :

(2) Just noticed the same visible() regression Chris pointed out:

65 bool ms::BasicSurface::visible(std::unique_lock<std::mutex>&) const
66 {
67 - return !hidden && surface_buffer_stream->has_submitted_buffer();
68 + bool visible{false};
69 + for(auto info : streams)
70 + visible |= info.stream->has_submitted_buffer();
71 + return !hidden && visible;
72 }

This allows a surface to get rendered even if the client hasn't painted its main layer yet. Sounds like a user-visible bug waiting to happen.

I think the right answer is that a surface only becomes visible when all of its layers are ready:

bool visible = true;
for (auto s : streams)
{
    if (!s->has_submitted_buffer())
    {
        visible = false;
        break;
    }
}

P.S. Did anyone suggest the word "layer" yet, as in "BasicSurface::layers"?

review: Needs Fixing
Alexandros Frantzis (afrantzis) wrote :

69+ for(auto info : streams)
140+ for(auto info : streams)
... and elsewhere

No need to copy, 'for (auto const& info : streams)'.

Formatting nits:

55+ if(
248+ for(
... and elsewhere

Space before opening parenthesis.

> This isn't about decoration at all, and this interface won't be used to provide our
> funky decoration options.

I was under the impression that the buffer stream mechanism could eventually be used for some decoration use cases (but I haven't followed the discussions closely).

> I think the right answer is that a surface only becomes visible when all of its layers are ready:

It really depends on the use case. For the nested bypass use case, where only one buffer stream (i.e. one client surface of the nested server) should be displayed, I would say that the surface can be considered visible if the "active" buffer stream has a valid buffer. I guess what Kevin is proposing will also work, as long as the nested server is smart enough not to "activate" a client buffer stream that doesn't have a valid buffer.

review: Needs Fixing
Kevin DuBois (kdub) wrote :

> You mean this is for a video overlay (YUV) sitting above some client-rendered
> controls (RGB)? That sounds reasonable then.
>
Right, this is one compelling use. The one I'm presently interested in is a nested server handing off its clients buffers to the host, and the host using bypass/overlay to composite using those methods on behalf of the nested server.

> But if we're not thinking about decorations yet then we should. We don't want
> to re-invent the wheel and an entirely separate API when what's proposed here
> is roughly what we've been talking about for hybrid decorations all along.
>
> I guess what I'm asking for is a future enhancement to this then. And
> displacement is all we care for right now.

Right, I agree that this is a good avenue to having hybrid decorations. I think its reasonable that this can be expanded in certain ways that help out decorations, but its a bit off-topic of the immediate goal of helping the client out with its composition.

Kevin DuBois (kdub) wrote :

@Visibility topic
If the user is specifying its surfaces, it doesn't make sense to assume that the created-with stream is somehow more important than the other surfaces. 'main stream' is a concept that I'm trying to root out (just one or two places to root this out).

So, thinking from the a nested composition topic, it doesn't make sense for the surface to become invisible briefly because the nested server adds an additional stream to its surface. Like, if the nested server has 3 clients, and adds a 4th, which takes a long time to gets its first frame out, the 3 existing streams should be shown in the host server while we wait for all the streams to be visible. The plan is to count on the nested server to be smart enough... we're giving it more power, so it has to have more responsibility. We might also have to give it better synchronization guarantees, but even having multiple buffer streams seems the first bridge to cross.

Kevin DuBois (kdub) wrote :

> I was under the impression that the buffer stream mechanism could eventually
> be used for some decoration use cases (but I haven't followed the discussions
> closely).

I think that Daniel and I see that this could be the mechanism to build on for client-side decorations, but for the nested-passthrough topic (or the video player, other more advanced compositors), its not necessary to build this idea up now.

Daniel van Vugt (vanvugt) wrote :

OK, no more comments for now...

review: Abstain
Alberto Aguirre (albaguirre) wrote :

LGTM.

review: Approve
Daniel van Vugt (vanvugt) wrote :

(2) Hmm, no I think BasicSurface::visible still needs fixing;
If streams are stored in the stacking order then "visible" must gate on the primary (background) stream being visible, and only the primary stream. Otherwise your window could have transparent holes in it (e.g. video playing but your browser window isn't rendered yet).

(3) Terminology: "layer" as the variable name I think would explain the intent better than "streams".

review: Needs Fixing
lp:~kdub/mir/add-streams-to-surface updated on 2015-06-04
2574. By Kevin DuBois on 2015-06-04

merge in mir

2575. By Kevin DuBois on 2015-06-04

use layers instead of streams in BasicSurface member variable

Kevin DuBois (kdub) wrote :

1) With the current setup, the client can arrange the alpha channel to have transparent holes, too. We have to rely on the client to assemble the content that it wants within its surface region (even with only the created-with stream, we have to rely on the client).

As Chris has pointed out here and there (there being the RFC branch for the client api), some sort of 'rearrange and swap all at once' will probably be needed to have polished stream changes. In the meantime though, just going for the first cut to just have the swaps and the arrangements be 'async'

There is still a concept of a 'primary' buffer stream (or layer), but this is vestigial, and is being removed in the branches stacked upon this one. The buffer stream that is automatically created with the surface doesn't have to have a topmost or bottommost position in the layering.

2) Changed to use 'layers' as the name.

Kevin DuBois (kdub) wrote :

1) With the current setup, the client can arrange the alpha channel to have transparent holes. We have to rely on the client to assemble the content that it wants within its surface region (even with only the created-with stream, we have to rely on the client).

There is still a concept of a 'primary' buffer stream (or layer), but this is vestigial, and is being removed in the branches stacked upon this one. The buffer stream that is automatically created with the surface doesn't have to have a topmost or bottommost position in the layering.

2) Changed to use 'layers' as the name.

Daniel van Vugt (vanvugt) :
review: Abstain
Daniel van Vugt (vanvugt) wrote :

OK, my concerns have been aired sufficiently. And I can see the last remaining "Needs Fixing" was fixed already. Top approving...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/scene/surface.h'
2--- include/server/mir/scene/surface.h 2015-05-29 17:36:46 +0000
3+++ include/server/mir/scene/surface.h 2015-06-04 11:33:13 +0000
4@@ -22,8 +22,10 @@
5 #include "mir/graphics/renderable.h"
6 #include "mir/input/surface.h"
7 #include "mir/frontend/surface.h"
8+#include "mir/compositor/compositor_id.h"
9
10 #include <vector>
11+#include <list>
12
13 namespace mir
14 {
15@@ -31,9 +33,15 @@
16 namespace shell { class InputTargeter; }
17 namespace geometry { struct Rectangle; }
18 namespace graphics { class CursorImage; }
19-
20+namespace compositor { class BufferStream; }
21 namespace scene
22 {
23+struct StreamInfo
24+{
25+ std::shared_ptr<compositor::BufferStream> stream;
26+ geometry::Displacement displacement;
27+};
28+
29 class SurfaceObserver;
30
31 class Surface :
32@@ -54,7 +62,7 @@
33 /// Size of the surface including window frame (if any)
34 virtual geometry::Size size() const = 0;
35
36- virtual std::unique_ptr<graphics::Renderable> compositor_snapshot(void const* compositor_id) const = 0;
37+ virtual graphics::RenderableList generate_renderables(compositor::CompositorID id) const = 0;
38 virtual int buffers_ready_for_compositor(void const* compositor_id) const = 0;
39
40 virtual float alpha() const = 0; //only used in examples/
41@@ -103,6 +111,7 @@
42
43 virtual void set_keymap(xkb_rule_names const& rules) = 0;
44 virtual void rename(std::string const& title) = 0;
45+ virtual void set_streams(std::list<StreamInfo> const& streams) = 0;
46 };
47 }
48 }
49
50=== modified file 'src/server/scene/basic_surface.cpp'
51--- src/server/scene/basic_surface.cpp 2015-05-29 17:36:46 +0000
52+++ src/server/scene/basic_surface.cpp 2015-06-04 11:33:13 +0000
53@@ -147,7 +147,8 @@
54 input_sender(input_sender),
55 cursor_image_(cursor_image),
56 report(report),
57- parent_(parent)
58+ parent_(parent),
59+ layers({StreamInfo{buffer_stream, {0,0}}})
60 {
61 report->surface_created(this, surface_name);
62 }
63@@ -344,7 +345,10 @@
64
65 bool ms::BasicSurface::visible(std::unique_lock<std::mutex>&) const
66 {
67- return !hidden && surface_buffer_stream->has_submitted_buffer();
68+ bool visible{false};
69+ for (auto const& info : layers)
70+ visible |= info.stream->has_submitted_buffer();
71+ return !hidden && visible;
72 }
73
74 mi::InputReceptionMode ms::BasicSurface::reception_mode() const
75@@ -424,7 +428,8 @@
76 {
77 swapinterval_ = interval;
78 bool allow_dropping = (interval == 0);
79- surface_buffer_stream->allow_framedropping(allow_dropping);
80+ for (auto& info : layers)
81+ info.stream->allow_framedropping(allow_dropping);
82
83 lg.unlock();
84 observers.attrib_changed(mir_surface_attrib_swapinterval, interval);
85@@ -714,7 +719,10 @@
86 visibility_ = new_visibility;
87 lg.unlock();
88 if (new_visibility == mir_surface_visibility_exposed)
89- surface_buffer_stream->drop_old_buffers();
90+ {
91+ for (auto& info : layers)
92+ info.stream->drop_old_buffers();
93+ }
94 observers.attrib_changed(mir_surface_attrib_visibility, visibility_);
95 }
96
97@@ -724,7 +732,8 @@
98 void ms::BasicSurface::add_observer(std::shared_ptr<SurfaceObserver> const& observer)
99 {
100 observers.add(observer);
101- surface_buffer_stream->add_observer(observer);
102+ for (auto& info : layers)
103+ info.stream->add_observer(observer);
104 }
105
106 void ms::BasicSurface::remove_observer(std::weak_ptr<SurfaceObserver> const& observer)
107@@ -733,7 +742,8 @@
108 if (!o)
109 BOOST_THROW_EXCEPTION(std::runtime_error("Invalid observer (previously destroyed)"));
110 observers.remove(o);
111- surface_buffer_stream->remove_observer(o);
112+ for (auto& info : layers)
113+ info.stream->remove_observer(observer);
114 }
115
116 std::shared_ptr<ms::Surface> ms::BasicSurface::parent() const
117@@ -804,24 +814,13 @@
118 };
119 }
120
121-std::unique_ptr<mg::Renderable> ms::BasicSurface::compositor_snapshot(void const* compositor_id) const
122-{
123- std::unique_lock<std::mutex> lk(guard);
124-
125- return std::make_unique<SurfaceSnapshot>(
126- surface_buffer_stream,
127- compositor_id,
128- surface_rect,
129- transformation_matrix,
130- surface_alpha,
131- nonrectangular,
132- this);
133-}
134-
135 int ms::BasicSurface::buffers_ready_for_compositor(void const* id) const
136 {
137 std::unique_lock<std::mutex> lk(guard);
138- return surface_buffer_stream->buffers_ready_for_compositor(id);
139+ auto max_buf = 0;
140+ for (auto const& info : layers)
141+ max_buf = std::max(max_buf, info.stream->buffers_ready_for_compositor(id));
142+ return max_buf;
143 }
144
145 void ms::BasicSurface::consume(MirEvent const& event)
146@@ -842,3 +841,33 @@
147 observers.renamed(surface_name.c_str());
148 }
149 }
150+
151+void ms::BasicSurface::set_streams(std::list<scene::StreamInfo> const& s)
152+{
153+ std::unique_lock<std::mutex> lk(guard);
154+
155+ if (s.end() == std::find_if(s.begin(), s.end(),
156+ [this] (ms::StreamInfo const& info) { return info.stream == surface_buffer_stream; }))
157+ {
158+ BOOST_THROW_EXCEPTION(std::logic_error("cannot remove the created-with buffer stream yet"));
159+ }
160+
161+ layers = s;
162+}
163+
164+mg::RenderableList ms::BasicSurface::generate_renderables(mc::CompositorID id) const
165+{
166+ std::unique_lock<std::mutex> lk(guard);
167+ mg::RenderableList list;
168+ for (auto const& info : layers)
169+ {
170+ if (info.stream->has_submitted_buffer())
171+ {
172+ list.emplace_back(std::make_shared<SurfaceSnapshot>(
173+ info.stream, id,
174+ geom::Rectangle{surface_rect.top_left + info.displacement, surface_rect.size},
175+ transformation_matrix, surface_alpha, nonrectangular, info.stream.get()));
176+ }
177+ }
178+ return list;
179+}
180
181=== modified file 'src/server/scene/basic_surface.h'
182--- src/server/scene/basic_surface.h 2015-05-29 17:36:46 +0000
183+++ src/server/scene/basic_surface.h 2015-06-04 11:33:13 +0000
184@@ -29,6 +29,7 @@
185
186 #include <glm/glm.hpp>
187 #include <vector>
188+#include <list>
189 #include <memory>
190 #include <mutex>
191 #include <string>
192@@ -91,6 +92,7 @@
193 geometry::Size client_size() const override;
194
195 std::shared_ptr<frontend::BufferStream> primary_buffer_stream() const override;
196+ void set_streams(std::list<scene::StreamInfo> const& streams) override;
197
198 bool supports_input() const override;
199 int client_input_fd() const override;
200@@ -113,7 +115,7 @@
201
202 bool visible() const override;
203
204- std::unique_ptr<graphics::Renderable> compositor_snapshot(void const* compositor_id) const override;
205+ graphics::RenderableList generate_renderables(compositor::CompositorID id) const override;
206 int buffers_ready_for_compositor(void const* compositor_id) const override;
207
208 MirSurfaceType type() const override;
209@@ -171,6 +173,7 @@
210 std::shared_ptr<SceneReport> const report;
211 std::weak_ptr<Surface> const parent_;
212
213+ std::list<StreamInfo> layers;
214 // Surface attributes:
215 MirSurfaceType type_ = mir_surface_type_normal;
216 MirSurfaceState state_ = mir_surface_state_restored;
217
218=== modified file 'src/server/scene/surface_stack.cpp'
219--- src/server/scene/surface_stack.cpp 2015-04-09 08:57:24 +0000
220+++ src/server/scene/surface_stack.cpp 2015-06-04 11:33:13 +0000
221@@ -47,13 +47,14 @@
222 {
223 public:
224 SurfaceSceneElement(
225- std::shared_ptr<ms::Surface> surface,
226+ std::string name,
227+ std::shared_ptr<mg::Renderable> const& renderable,
228 std::shared_ptr<ms::RenderingTracker> const& tracker,
229 mc::CompositorID id)
230- : renderable_{surface->compositor_snapshot(id)},
231+ : renderable_{renderable},
232 tracker{tracker},
233 cid{id},
234- surface_name(surface->name())
235+ surface_name(name)
236 {
237 }
238
239@@ -136,11 +137,15 @@
240 {
241 if (surface->visible())
242 {
243- auto element = std::make_shared<SurfaceSceneElement>(
244- surface,
245- rendering_trackers[surface.get()],
246- id);
247- elements.emplace_back(element);
248+ for (auto& renderable : surface->generate_renderables(id))
249+ {
250+ elements.emplace_back(
251+ std::make_shared<SurfaceSceneElement>(
252+ surface->name(),
253+ renderable,
254+ rendering_trackers[surface.get()],
255+ id));
256+ }
257 }
258 }
259 }
260
261=== modified file 'tests/acceptance-tests/test_surfaces_with_output_id.cpp'
262--- tests/acceptance-tests/test_surfaces_with_output_id.cpp 2015-03-31 02:35:42 +0000
263+++ tests/acceptance-tests/test_surfaces_with_output_id.cpp 2015-06-04 11:33:13 +0000
264@@ -81,7 +81,10 @@
265 for (auto const& surface : surfaces)
266 {
267 if (auto const ss = surface.lock())
268- rects.push_back(ss->compositor_snapshot(this)->screen_position());
269+ {
270+ for (auto& renderable: ss->generate_renderables(this))
271+ rects.push_back(renderable->screen_position());
272+ }
273 }
274 return rects;
275 }
276@@ -128,6 +131,7 @@
277 mir_surface_spec_set_fullscreen_on_output(spec, output.output_id);
278
279 auto surface_raw = mir_surface_create_sync(spec);
280+ mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface_raw));
281 mir_surface_spec_release(spec);
282
283 return shared_ptr_surface(surface_raw);
284@@ -144,6 +148,7 @@
285 mir_surface_spec_set_fullscreen_on_output(spec, output.output_id);
286
287 auto surface_raw = mir_surface_create_sync(spec);
288+ mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface_raw));
289 mir_surface_spec_release(spec);
290
291 return shared_ptr_surface(surface_raw);
292
293=== modified file 'tests/include/mir_test_doubles/stub_scene_surface.h'
294--- tests/include/mir_test_doubles/stub_scene_surface.h 2015-05-19 21:34:34 +0000
295+++ tests/include/mir_test_doubles/stub_scene_surface.h 2015-06-04 11:33:13 +0000
296@@ -62,7 +62,8 @@
297 geometry::Rectangle input_bounds() const override { return {{},{}}; }
298 bool input_area_contains(mir::geometry::Point const&) const override { return false; }
299
300- virtual std::unique_ptr<graphics::Renderable> compositor_snapshot(void const*) const override { return nullptr; }
301+ void set_streams(std::list<scene::StreamInfo> const&) override {}
302+ graphics::RenderableList generate_renderables(compositor::CompositorID) const override { return {}; }
303 int buffers_ready_for_compositor(void const*) const override { return 0; }
304
305 float alpha() const override { return 0.0f;}
306
307=== modified file 'tests/integration-tests/surface_composition.cpp'
308--- tests/integration-tests/surface_composition.cpp 2015-05-19 21:34:34 +0000
309+++ tests/integration-tests/surface_composition.cpp 2015-06-04 11:33:13 +0000
310@@ -120,7 +120,7 @@
311 surface->primary_buffer_stream()->swap_buffers(old_buffer, callback);
312 }
313
314- auto const renderable = surface->compositor_snapshot(this);
315+ auto const renderables = surface->generate_renderables(this);
316
317 surface.reset();
318 }
319
320=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
321--- tests/unit-tests/scene/test_basic_surface.cpp 2015-06-01 16:19:11 +0000
322+++ tests/unit-tests/scene/test_basic_surface.cpp 2015-06-04 11:33:13 +0000
323@@ -120,7 +120,8 @@
324 EXPECT_EQ(name, surface.name());
325 EXPECT_EQ(rect.size, surface.size());
326 EXPECT_EQ(rect.top_left, surface.top_left());
327- EXPECT_FALSE(surface.compositor_snapshot(compositor_id)->shaped());
328+ for (auto& renderable : surface.generate_renderables(this))
329+ EXPECT_FALSE(renderable->shaped());
330 }
331
332 TEST_F(BasicSurfaceTest, primary_buffer_stream)
333@@ -128,40 +129,40 @@
334 EXPECT_THAT(surface.primary_buffer_stream(), Eq(mock_buffer_stream));
335 }
336
337-TEST_F(BasicSurfaceTest, id_always_unique)
338+TEST_F(BasicSurfaceTest, buffer_stream_ids_always_unique)
339 {
340- int const N = 10;
341- std::unique_ptr<ms::BasicSurface> surfaces[N];
342+ int const n = 10;
343+ std::array<std::unique_ptr<ms::BasicSurface>, n> surfaces;
344
345- for (int i = 0; i < N; ++i)
346+ std::multiset<mg::Renderable::ID> ids;
347+ for (auto& surface : surfaces)
348 {
349- surfaces[i].reset(new ms::BasicSurface(
350- name, rect, false, mock_buffer_stream,
351+ surface = std::make_unique<ms::BasicSurface>(
352+ name, rect, false, std::make_shared<testing::NiceMock<mtd::MockBufferStream>>(),
353 std::shared_ptr<mi::InputChannel>(), stub_input_sender,
354- std::shared_ptr<mg::CursorImage>(), report)
355- );
356-
357- for (int j = 0; j < i; ++j)
358- {
359- ASSERT_NE(surfaces[j]->compositor_snapshot(compositor_id)->id(), surfaces[i]->compositor_snapshot(compositor_id)->id());
360- }
361+ std::shared_ptr<mg::CursorImage>(), report);
362+ for (auto& renderable : surface->generate_renderables(this))
363+ ids.insert(renderable->id());
364 }
365+ for (auto& it : ids)
366+ EXPECT_THAT(ids.count(it), testing::Eq(1));
367 }
368
369 TEST_F(BasicSurfaceTest, id_never_invalid)
370 {
371- int const N = 10;
372- std::unique_ptr<ms::BasicSurface> surfaces[N];
373+ int const n = 10;
374+ std::array<std::unique_ptr<ms::BasicSurface>, n> surfaces;
375
376- for (int i = 0; i < N; ++i)
377+ std::multiset<mg::Renderable::ID> ids;
378+ for (auto& surface : surfaces)
379 {
380- surfaces[i].reset(new ms::BasicSurface(
381+ surface = std::make_unique<ms::BasicSurface>(
382 name, rect, false, mock_buffer_stream,
383 std::shared_ptr<mi::InputChannel>(), stub_input_sender,
384- std::shared_ptr<mg::CursorImage>(), report)
385- );
386+ std::shared_ptr<mg::CursorImage>(), report);
387
388- ASSERT_TRUE(surfaces[i]->compositor_snapshot(compositor_id)->id());
389+ for (auto& renderable : surface->generate_renderables(this))
390+ EXPECT_THAT(renderable->id(), testing::Ne(nullptr));
391 }
392 }
393
394@@ -193,12 +194,16 @@
395 EXPECT_EQ(rect.size, surface.size());
396 EXPECT_NE(new_size, surface.size());
397
398- auto old_transformation = surface.compositor_snapshot(compositor_id)->transformation();
399+ auto renderables = surface.generate_renderables(compositor_id);
400+ ASSERT_THAT(renderables.size(), testing::Eq(1));
401+ auto old_transformation = renderables[0]->transformation();
402
403 surface.resize(new_size);
404 EXPECT_EQ(new_size, surface.size());
405 // Size no longer affects transformation:
406- EXPECT_EQ(old_transformation, surface.compositor_snapshot(compositor_id)->transformation());
407+ renderables = surface.generate_renderables(compositor_id);
408+ ASSERT_THAT(renderables.size(), testing::Eq(1));
409+ EXPECT_THAT(renderables[0]->transformation(), testing::Eq(old_transformation));
410 }
411
412 /*
413@@ -227,14 +232,18 @@
414 surface.add_observer(observer);
415 post_a_frame(surface);
416
417- auto original_transformation = surface.compositor_snapshot(compositor_id)->transformation();
418+ auto renderables = surface.generate_renderables(compositor_id);
419+ ASSERT_THAT(renderables.size(), testing::Eq(1));
420+ auto original_transformation = renderables[0]->transformation();
421 glm::mat4 trans{0.1f, 0.5f, 0.9f, 1.3f,
422 0.2f, 0.6f, 1.0f, 1.4f,
423 0.3f, 0.7f, 1.1f, 1.5f,
424 0.4f, 0.8f, 1.2f, 1.6f};
425
426 surface.set_transformation(trans);
427- auto got = surface.compositor_snapshot(compositor_id)->transformation();
428+ renderables = surface.generate_renderables(compositor_id);
429+ ASSERT_THAT(renderables.size(), testing::Eq(1));
430+ auto got = renderables[0]->transformation();
431 EXPECT_NE(original_transformation, got);
432 EXPECT_EQ(trans, got);
433 }
434@@ -258,18 +267,18 @@
435 using namespace testing;
436
437 EXPECT_THAT(1.0f, FloatEq(surface.alpha()));
438- EXPECT_FALSE(surface.compositor_snapshot(compositor_id)->shaped());
439+ auto renderables = surface.generate_renderables(compositor_id);
440+ ASSERT_THAT(renderables.size(), testing::Eq(1));
441+ EXPECT_FALSE(renderables[0]->shaped());
442 }
443
444 TEST_F(BasicSurfaceTest, test_surface_visibility)
445 {
446 using namespace testing;
447 mtd::StubBuffer mock_buffer;
448- EXPECT_CALL(*mock_buffer_stream, has_submitted_buffer())
449- .Times(3)
450- .WillOnce(Return(false))
451- .WillOnce(Return(false))
452- .WillOnce(Return(true));
453+ auto submitted_buffer = false;
454+ ON_CALL(*mock_buffer_stream, has_submitted_buffer())
455+ .WillByDefault(Invoke([&submitted_buffer] { return submitted_buffer; }));
456
457 // Must be a fresh surface to guarantee no frames posted yet...
458 ms::BasicSurface surface{
459@@ -292,6 +301,7 @@
460 EXPECT_FALSE(surface.visible());
461
462 surface.set_hidden(false);
463+ submitted_buffer = true;
464 EXPECT_TRUE(surface.visible());
465
466 surface.configure(mir_surface_attrib_state, mir_surface_state_hidden);
467@@ -747,3 +757,214 @@
468
469 surface.rename("Steve");
470 }
471+
472+MATCHER_P(IsRenderableOfPosition, pos, "is renderable with position")
473+{
474+ return (pos == arg->screen_position().top_left);
475+}
476+
477+MATCHER_P(IsRenderableOfAlpha, alpha, "is renderable with alpha")
478+{
479+ EXPECT_THAT(static_cast<float>(alpha), testing::FloatEq(arg->alpha()));
480+ return !(::testing::Test::HasFailure());
481+}
482+
483+TEST_F(BasicSurfaceTest, adds_buffer_streams)
484+{
485+ using namespace testing;
486+ geom::Displacement d0{19,99};
487+ geom::Displacement d1{21,101};
488+ geom::Displacement d2{20,9};
489+ auto buffer_stream0 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
490+ auto buffer_stream1 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
491+ auto buffer_stream2 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
492+
493+ std::list<ms::StreamInfo> streams = {
494+ { mock_buffer_stream, {0,0}},
495+ { buffer_stream0, d0 },
496+ { buffer_stream1, d1 },
497+ { buffer_stream2, d2 }
498+ };
499+ surface.set_streams(streams);
500+
501+ auto renderables = surface.generate_renderables(this);
502+ ASSERT_THAT(renderables.size(), Eq(4));
503+ EXPECT_THAT(renderables[0], IsRenderableOfPosition(rect.top_left));
504+ EXPECT_THAT(renderables[1], IsRenderableOfPosition(rect.top_left + d0));
505+ EXPECT_THAT(renderables[2], IsRenderableOfPosition(rect.top_left + d1));
506+ EXPECT_THAT(renderables[3], IsRenderableOfPosition(rect.top_left + d2));
507+
508+}
509+
510+TEST_F(BasicSurfaceTest, moving_surface_repositions_all_associated_streams)
511+{
512+ using namespace testing;
513+ geom::Point pt{10, 20};
514+ geom::Displacement d{19,99};
515+ auto buffer_stream = std::make_shared<NiceMock<mtd::MockBufferStream>>();
516+
517+ std::list<ms::StreamInfo> streams = {
518+ { mock_buffer_stream, {0,0}},
519+ { buffer_stream, d }
520+ };
521+
522+ surface.set_streams(streams);
523+ auto renderables = surface.generate_renderables(this);
524+ ASSERT_THAT(renderables.size(), Eq(2));
525+ EXPECT_THAT(renderables[0], IsRenderableOfPosition(rect.top_left));
526+ EXPECT_THAT(renderables[1], IsRenderableOfPosition(rect.top_left + d));
527+
528+ surface.move_to(pt);
529+
530+ renderables = surface.generate_renderables(this);
531+ ASSERT_THAT(renderables.size(), Eq(2));
532+ EXPECT_THAT(renderables[0], IsRenderableOfPosition(pt));
533+ EXPECT_THAT(renderables[1], IsRenderableOfPosition(pt + d));
534+}
535+
536+//TODO: (kdub) This should be a temporary behavior while the buffer stream the surface was created
537+//with is still more important than the rest of the streams. One will soon be able to
538+//remove the created-with bufferstream.
539+TEST_F(BasicSurfaceTest, cannot_remove_primary_buffer_stream_for_now)
540+{
541+ using namespace testing;
542+ geom::Displacement d0{19,99};
543+ geom::Displacement d1{21,101};
544+ geom::Displacement d2{20,9};
545+ auto buffer_stream0 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
546+ auto buffer_stream1 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
547+ auto buffer_stream2 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
548+
549+ std::list<ms::StreamInfo> streams = {
550+ { mock_buffer_stream, {0,0} },
551+ };
552+ surface.set_streams(streams);
553+ auto renderables = surface.generate_renderables(this);
554+ ASSERT_THAT(renderables.size(), Eq(1));
555+ EXPECT_THAT(renderables[0], IsRenderableOfPosition(rect.top_left));
556+
557+ EXPECT_THROW({
558+ surface.set_streams({});
559+ }, std::logic_error);
560+}
561+
562+TEST_F(BasicSurfaceTest, showing_brings_all_streams_up_to_date)
563+{
564+ using namespace testing;
565+ auto buffer_stream = std::make_shared<NiceMock<mtd::MockBufferStream>>();
566+ std::list<ms::StreamInfo> streams = {
567+ { mock_buffer_stream, {0,0} },
568+ { buffer_stream, {0,0} }
569+ };
570+ surface.set_streams(streams);
571+
572+ EXPECT_CALL(*buffer_stream, drop_old_buffers()).Times(Exactly(1));
573+ EXPECT_CALL(*mock_buffer_stream, drop_old_buffers()).Times(Exactly(1));
574+
575+ surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_occluded);
576+ surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed);
577+ surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed);
578+}
579+
580+//TODO: per-stream alpha and swapinterval seems useful
581+TEST_F(BasicSurfaceTest, changing_alpha_effects_all_streams)
582+{
583+ using namespace testing;
584+ auto alpha = 0.3;
585+
586+ auto buffer_stream = std::make_shared<NiceMock<mtd::MockBufferStream>>();
587+ std::list<ms::StreamInfo> streams = {
588+ { mock_buffer_stream, {0,0} },
589+ { buffer_stream, {0,0} }
590+ };
591+
592+ surface.set_streams(streams);
593+ auto renderables = surface.generate_renderables(this);
594+ ASSERT_THAT(renderables.size(), Eq(2));
595+ EXPECT_THAT(renderables[0], IsRenderableOfAlpha(1.0f));
596+ EXPECT_THAT(renderables[1], IsRenderableOfAlpha(1.0f));
597+
598+ surface.set_alpha(alpha);
599+ renderables = surface.generate_renderables(this);
600+ ASSERT_THAT(renderables.size(), Eq(2));
601+ EXPECT_THAT(renderables[0], IsRenderableOfAlpha(alpha));
602+ EXPECT_THAT(renderables[1], IsRenderableOfAlpha(alpha));
603+}
604+
605+TEST_F(BasicSurfaceTest, changing_inverval_effects_all_streams)
606+{
607+ using namespace testing;
608+
609+ auto buffer_stream = std::make_shared<NiceMock<mtd::MockBufferStream>>();
610+ std::list<ms::StreamInfo> streams = {
611+ { mock_buffer_stream, {0,0} },
612+ { buffer_stream, {0,0} }
613+ };
614+
615+ EXPECT_CALL(*mock_buffer_stream, allow_framedropping(true));
616+ EXPECT_CALL(*buffer_stream, allow_framedropping(true));
617+
618+ surface.set_streams(streams);
619+ surface.configure(mir_surface_attrib_swapinterval, 0);
620+}
621+
622+TEST_F(BasicSurfaceTest, visibility_matches_produced_list)
623+{
624+ using namespace testing;
625+ auto stream1_visible = false;
626+ auto stream2_visible = false;
627+ geom::Displacement displacement{3,-2};
628+ auto mock_buffer_stream1 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
629+ ON_CALL(*mock_buffer_stream, has_submitted_buffer())
630+ .WillByDefault(Invoke([&stream1_visible] { return stream1_visible; }));
631+ ON_CALL(*mock_buffer_stream1, has_submitted_buffer())
632+ .WillByDefault(Invoke([&stream2_visible] { return stream2_visible; }));
633+ std::list<ms::StreamInfo> streams = {
634+ { mock_buffer_stream, {0,0} },
635+ { mock_buffer_stream1, displacement },
636+ };
637+ surface.set_streams(streams);
638+
639+ auto renderables = surface.generate_renderables(this);
640+ EXPECT_FALSE(surface.visible());
641+ EXPECT_THAT(renderables.size(), Eq(0));
642+
643+ stream2_visible = true;
644+ renderables = surface.generate_renderables(this);
645+ EXPECT_TRUE(surface.visible());
646+ EXPECT_THAT(renderables.size(), Eq(1));
647+
648+ stream1_visible = true;
649+ renderables = surface.generate_renderables(this);
650+ EXPECT_TRUE(surface.visible());
651+ EXPECT_THAT(renderables.size(), Eq(2));
652+}
653+
654+TEST_F(BasicSurfaceTest, buffers_ready_correctly_reported)
655+{
656+ using namespace testing;
657+ auto buffer_stream0 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
658+ auto buffer_stream1 = std::make_shared<NiceMock<mtd::MockBufferStream>>();
659+ EXPECT_CALL(*mock_buffer_stream, buffers_ready_for_compositor(_))
660+ .WillOnce(Return(0))
661+ .WillOnce(Return(0))
662+ .WillOnce(Return(2));
663+ EXPECT_CALL(*buffer_stream0, buffers_ready_for_compositor(_))
664+ .WillOnce(Return(1))
665+ .WillOnce(Return(0))
666+ .WillOnce(Return(1));
667+ EXPECT_CALL(*buffer_stream1, buffers_ready_for_compositor(_))
668+ .WillOnce(Return(3))
669+ .WillOnce(Return(0))
670+ .WillOnce(Return(1));
671+
672+ std::list<ms::StreamInfo> streams = {
673+ { mock_buffer_stream, {0,0}},
674+ { buffer_stream0, {0,0} },
675+ { buffer_stream1, {0,0} },
676+ };
677+ surface.set_streams(streams);
678+ EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(3));
679+ EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(0));
680+ EXPECT_THAT(surface.buffers_ready_for_compositor(this), Eq(2));
681+}
682
683=== modified file 'tests/unit-tests/scene/test_surface.cpp'
684--- tests/unit-tests/scene/test_surface.cpp 2015-05-21 19:25:51 +0000
685+++ tests/unit-tests/scene/test_surface.cpp 2015-06-04 11:33:13 +0000
686@@ -355,13 +355,17 @@
687
688 surface.set_alpha(alpha);
689 EXPECT_FLOAT_EQ(alpha, surface.alpha());
690- EXPECT_FLOAT_EQ(alpha, surface.compositor_snapshot(nullptr)->alpha());
691+ auto renderables = surface.generate_renderables(nullptr);
692+ ASSERT_THAT(renderables.size(), Ge(1));
693+ EXPECT_FLOAT_EQ(alpha, renderables[0]->alpha());
694
695 alpha = 0.1;
696
697 surface.set_alpha(alpha);
698 EXPECT_FLOAT_EQ(alpha, surface.alpha());
699- EXPECT_FLOAT_EQ(alpha, surface.compositor_snapshot(nullptr)->alpha());
700+ renderables = surface.generate_renderables(nullptr);
701+ ASSERT_THAT(renderables.size(), Ge(1));
702+ EXPECT_FLOAT_EQ(alpha, renderables[0]->alpha());
703 }
704
705 TEST_F(SurfaceCreation, input_fds)
706
707=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
708--- tests/unit-tests/scene/test_surface_stack.cpp 2015-05-19 21:34:34 +0000
709+++ tests/unit-tests/scene/test_surface_stack.cpp 2015-06-04 11:33:13 +0000
710@@ -67,9 +67,14 @@
711 return arg->reception_mode() == mode;
712 }
713
714-MATCHER_P(SceneElementFor, surface, "")
715-{
716- return arg->renderable()->id() == surface.get();
717+MATCHER_P(SceneElementForSurface, surface, "")
718+{
719+ return arg->renderable()->id() == surface->primary_buffer_stream().get();
720+}
721+
722+MATCHER_P(SceneElementForStream, stream, "")
723+{
724+ return arg->renderable()->id() == stream.get();
725 }
726
727 struct StubInputChannelFactory : public mi::InputChannelFactory
728@@ -211,9 +216,45 @@
729 EXPECT_THAT(
730 stack.scene_elements_for(compositor_id),
731 ElementsAre(
732- SceneElementFor(stub_surface1),
733- SceneElementFor(stub_surface2),
734- SceneElementFor(stub_surface3)));
735+ SceneElementForSurface(stub_surface1),
736+ SceneElementForSurface(stub_surface2),
737+ SceneElementForSurface(stub_surface3)));
738+}
739+
740+TEST_F(SurfaceStack, stacking_order_with_multiple_buffer_streams)
741+{
742+ using namespace testing;
743+ auto stub_stream0 = std::make_shared<mtd::StubBufferStream>();
744+ auto stub_stream1 = std::make_shared<mtd::StubBufferStream>();
745+ auto stub_stream2 = std::make_shared<mtd::StubBufferStream>();
746+ std::list<ms::StreamInfo> streams = {
747+ { stub_surface1->buffer_stream(), {0,0} },
748+ { stub_stream0, {2,2} },
749+ { stub_stream1, {2,3} },
750+ };
751+ stub_surface1->set_streams(streams);
752+
753+ streams = {
754+ { stub_stream2, {2,4} },
755+ { stub_surface3->buffer_stream(), {0,0} }
756+ };
757+ stub_surface3->set_streams(streams);
758+
759+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
760+ stack.add_surface(stub_surface2, default_params.depth, default_params.input_mode);
761+ stack.add_surface(stub_surface3, default_params.depth, default_params.input_mode);
762+
763+
764+ EXPECT_THAT(
765+ stack.scene_elements_for(compositor_id),
766+ ElementsAre(
767+ SceneElementForSurface(stub_surface1),
768+ SceneElementForStream(stub_stream0),
769+ SceneElementForStream(stub_stream1),
770+ SceneElementForSurface(stub_surface2),
771+ SceneElementForStream(stub_stream2),
772+ SceneElementForSurface(stub_surface3)
773+ ));
774 }
775
776 TEST_F(SurfaceStack, scene_snapshot_omits_invisible_surfaces)
777@@ -227,8 +268,8 @@
778 EXPECT_THAT(
779 stack.scene_elements_for(compositor_id),
780 ElementsAre(
781- SceneElementFor(stub_surface1),
782- SceneElementFor(stub_surface2)));
783+ SceneElementForSurface(stub_surface1),
784+ SceneElementForSurface(stub_surface2)));
785 }
786
787 TEST_F(SurfaceStack, decor_name_is_surface_name)
788@@ -375,9 +416,9 @@
789 EXPECT_THAT(
790 stack.scene_elements_for(compositor_id),
791 ElementsAre(
792- SceneElementFor(stub_surface1),
793- SceneElementFor(stub_surface3),
794- SceneElementFor(stub_surface2)));
795+ SceneElementForSurface(stub_surface1),
796+ SceneElementForSurface(stub_surface3),
797+ SceneElementForSurface(stub_surface2)));
798 }
799
800
801@@ -411,18 +452,18 @@
802 EXPECT_THAT(
803 stack.scene_elements_for(compositor_id),
804 ElementsAre(
805- SceneElementFor(stub_surface1),
806- SceneElementFor(stub_surface2),
807- SceneElementFor(stub_surface3)));
808+ SceneElementForSurface(stub_surface1),
809+ SceneElementForSurface(stub_surface2),
810+ SceneElementForSurface(stub_surface3)));
811
812 stack.raise(stub_surface1);
813
814 EXPECT_THAT(
815 stack.scene_elements_for(compositor_id),
816 ElementsAre(
817- SceneElementFor(stub_surface2),
818- SceneElementFor(stub_surface3),
819- SceneElementFor(stub_surface1)));
820+ SceneElementForSurface(stub_surface2),
821+ SceneElementForSurface(stub_surface3),
822+ SceneElementForSurface(stub_surface1)));
823 }
824
825 TEST_F(SurfaceStack, depth_id_trumps_raise)
826@@ -436,18 +477,18 @@
827 EXPECT_THAT(
828 stack.scene_elements_for(compositor_id),
829 ElementsAre(
830- SceneElementFor(stub_surface1),
831- SceneElementFor(stub_surface2),
832- SceneElementFor(stub_surface3)));
833+ SceneElementForSurface(stub_surface1),
834+ SceneElementForSurface(stub_surface2),
835+ SceneElementForSurface(stub_surface3)));
836
837 stack.raise(stub_surface1);
838
839 EXPECT_THAT(
840 stack.scene_elements_for(compositor_id),
841 ElementsAre(
842- SceneElementFor(stub_surface2),
843- SceneElementFor(stub_surface1),
844- SceneElementFor(stub_surface3)));
845+ SceneElementForSurface(stub_surface2),
846+ SceneElementForSurface(stub_surface1),
847+ SceneElementForSurface(stub_surface3)));
848 }
849
850 TEST_F(SurfaceStack, raise_throw_behavior)
851@@ -894,9 +935,9 @@
852 EXPECT_THAT(
853 stack.scene_elements_for(compositor_id),
854 ElementsAre(
855- SceneElementFor(stub_surface1),
856- SceneElementFor(stub_surface2),
857- SceneElementFor(mt::fake_shared(r))));
858+ SceneElementForSurface(stub_surface1),
859+ SceneElementForSurface(stub_surface2),
860+ SceneElementForStream(mt::fake_shared(r))));
861 }
862
863 TEST_F(SurfaceStack, removed_overlays_are_removed)
864@@ -912,17 +953,17 @@
865 EXPECT_THAT(
866 stack.scene_elements_for(compositor_id),
867 ElementsAre(
868- SceneElementFor(stub_surface1),
869- SceneElementFor(stub_surface2),
870- SceneElementFor(mt::fake_shared(r))));
871+ SceneElementForSurface(stub_surface1),
872+ SceneElementForSurface(stub_surface2),
873+ SceneElementForStream(mt::fake_shared(r))));
874
875 stack.remove_input_visualization(mt::fake_shared(r));
876
877 EXPECT_THAT(
878 stack.scene_elements_for(compositor_id),
879 ElementsAre(
880- SceneElementFor(stub_surface1),
881- SceneElementFor(stub_surface2)));
882+ SceneElementForSurface(stub_surface1),
883+ SceneElementForSurface(stub_surface2)));
884 }
885
886 TEST_F(SurfaceStack, scene_observers_notified_of_generic_scene_change)
887@@ -1020,9 +1061,9 @@
888 EXPECT_THAT(
889 stack.scene_elements_for(compositor_id),
890 ElementsAre(
891- SceneElementFor(stub_surface2),
892- SceneElementFor(stub_surface1),
893- SceneElementFor(stub_surface3)));
894+ SceneElementForSurface(stub_surface2),
895+ SceneElementForSurface(stub_surface1),
896+ SceneElementForSurface(stub_surface3)));
897
898 Mock::VerifyAndClearExpectations(&observer);
899 EXPECT_CALL(observer, surfaces_reordered()).Times(1);
900@@ -1031,9 +1072,9 @@
901 EXPECT_THAT(
902 stack.scene_elements_for(compositor_id),
903 ElementsAre(
904- SceneElementFor(stub_surface1),
905- SceneElementFor(stub_surface2),
906- SceneElementFor(stub_surface3)));
907+ SceneElementForSurface(stub_surface1),
908+ SceneElementForSurface(stub_surface2),
909+ SceneElementForSurface(stub_surface3)));
910
911 Mock::VerifyAndClearExpectations(&observer);
912 EXPECT_CALL(observer, surfaces_reordered()).Times(0);
913@@ -1042,8 +1083,7 @@
914 EXPECT_THAT(
915 stack.scene_elements_for(compositor_id),
916 ElementsAre(
917- SceneElementFor(stub_surface1),
918- SceneElementFor(stub_surface2),
919- SceneElementFor(stub_surface3)));
920+ SceneElementForSurface(stub_surface1),
921+ SceneElementForSurface(stub_surface2),
922+ SceneElementForSurface(stub_surface3)));
923 }
924-

Subscribers

People subscribed via source and target branches