Mir

Merge lp:~kdub/mir/server-sent-buffer into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Chris Halse Rogers
Approved revision: no longer in the source branch.
Merged at revision: 2693
Proposed branch: lp:~kdub/mir/server-sent-buffer
Merge into: lp:mir
Prerequisite: lp:~kdub/mir/client-track-bufferstream-ids
Diff against target: 559 lines (+253/-10)
15 files modified
src/client/buffer_stream.cpp (+54/-1)
src/client/buffer_stream.h (+11/-0)
src/client/client_buffer_stream.h (+2/-1)
src/client/rpc/mir_protobuf_rpc_channel.cpp (+8/-0)
src/include/server/mir/frontend/event_sink.h (+3/-0)
src/protobuf/mir_protobuf.proto (+2/-0)
src/server/frontend/event_sender.cpp (+11/-0)
src/server/frontend/event_sender.h (+1/-0)
src/server/scene/global_event_sender.cpp (+4/-0)
src/server/scene/global_event_sender.h (+1/-0)
tests/include/mir_test_doubles/mock_client_buffer_stream.h (+1/-0)
tests/include/mir_test_doubles/mock_event_sink.h (+1/-0)
tests/include/mir_test_doubles/null_event_sink.h (+1/-0)
tests/integration-tests/test_exchange_buffer.cpp (+86/-3)
tests/unit-tests/client/test_client_buffer_stream.cpp (+67/-5)
To merge this branch: bzr merge lp:~kdub/mir/server-sent-buffer
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Chris Halse Rogers Approve
Andreas Pokorny (community) Approve
Alexandros Frantzis (community) Approve
Alan Griffiths Approve
Alberto Aguirre (community) Approve
Review via email: mp+261421@code.launchpad.net

Commit message

Give the server the ability to send a buffer to the client without a client request. (part of the buffer semantics changes). The buffer is sent as an event; as its the part of the system that's suited to sending unrequested messages to the server

Description of the change

Give the server the ability to send a buffer to the client without a client request. (part of the buffer semantics changes). The buffer is sent as an event; as its the part of the system that's suited to sending unrequested messages to the server.

Note: this can really only send the 'partial' buffer updates now, a bit more plumbing is needed to hook up the platform-specific packing operations for the full buffer sending.

The transition strategy here is that the client will use the exchange_buffer() call to submit its buffers, until it receives a buffer asynchronously. After receiving the first 'async' buffer, it submits its buffers via the submit_buffer() call.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

^^doesnt look related at first glance, but haven't ruled out that is yet (and cant reproduce locally)...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Is there a document with the proposed new buffer semantics anywhere? This doesn't match my understanding from Dallas. (Not that it's *wrong*, just that it's unexpected ☺)

Revision history for this message
Kevin DuBois (kdub) wrote :

Notes are in this card: https://trello.com/c/wGVrm86z

There were two ways that I could think of to do this, the buffer gets sent when its ready (the approach in the MP), or the client has to call some sort of acquire-buffer rpc call. The send-to-client-when-done approach seemed better, as the client gets notified sooner, without asking, and with less IPC.

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

Yeah I guess from the Dallas discussion I assumed this to be an event FD (buffer writable event) that would be get populated at the time of creation to avoid rpc.

My understanding was something akin to:

During buffer create, a couple of fence fds are created - one for the server to signal when the buffer is writable by the client again (i.e buffer is no longer being read) and another for the client to signal the server that the buffer is now readable (i.e. finished writing to the buffer).

An RPC call was needed for the client to "schedule" a buffer - as the fence fd did not have enough context for scheduling (i.e. timestamping)

An fd signaling a buffer is writable however, seems like there's enough context there to avoid explicit RPC calls.

review: Needs Information
Revision history for this message
Kevin DuBois (kdub) wrote :

There's also the problem that we'd have to send fences (in the android sense) with every signal of the fd pair, which is why I thought we shied away from wanting to do this.

Revision history for this message
Chris Halse Rogers (raof) wrote :

My understanding was that submit_buffer was going to take a {bufferid, when-readable-fence} and *return* a fence that becomes signalled when the buffer is client-writable again.

So, I guess the takeaway point is that we're not clear as a team how we expect this to work :)

Revision history for this message
Kevin DuBois (kdub) wrote :

Just returning one fence that signals (perhaps we would epoll on it?) still has the problem that we have to go and get the FD that indicates when the buffer is ready. (the 'standard' android fences). The android fences could be (and, without confirming, probably are) a different FD each time a buffer fence is returned from the driver, so we have to send that fd via ipc each time we get one.

There's two timelines going on... one is from the driver, and that fence protects the buffers content (when the GPU/display thinks the buffer is ready). The second timeline is when mir thinks its ready (perhaps there's screenshot going on, or we have to hold the buffer a bit longer for a convoluted multimonitor scenario), and both have to be clear before the gpu can go and write in the buffer on the client side.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

In the android platform I was envisioning returning the fence all the way to the driver not explicitly epolling.

ANW->dequeueBuffer would get any of the buffers that are not currently used by the driver along with its "this buffer is writable now" fence; that way no explicit waiting is done by the client code.

But I guess yeah the android fences could be different each time so an RPC would still be needed to update the fence on the client side.

Also the consumer side needs to be able to make the client wait until before retrieving a buffer (during dequeueBuffer) otherwise there would be a race getting the right sync fence fd.

Given that I guess we do need something like this MP.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

OK for transitioning from exchange buffer.

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

I guess this is OK as an intermediate form but...

21 + if (using_exchange_buffer)
22 + return exchange(done, std::move(lock));
23 + return submit(done, std::move(lock));
...
74 + using_exchange_buffer = false; //this stream uses async exchange from here on out
...
127 + bool using_exchange_buffer = true;

This feels like there's an abstraction missing. C.f.

    return state->send_buffer(done, std::move(lock));

Or perhaps a pointer to member function instead of a bool?

~~~~

35 + //in both cases, submit what we have

It would be helpful to explicitly state what the two cases are.

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

OK.

Nits:

432 + bool satisfied = false;

Clearer as 'stub_buffer_arrived' or similar.

429 + //spin-wait for the id to become the current one.

Could use mt::spin_wait_for_condition_or_timeout()

498 + bs->next_buffer([](){});

Could use just '[]{}'.

'[](){}' is a nice collection of brackets... C++ should introduce angle brackets in lambdas '[](){}⟨⟩' to become the first bracket-complete language! :)

review: Approve
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

ok looks good

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

@abstraction, There is one to be made, but we want to deprecate exchange_buffer soon anyways, so it didn't seem worth it to make this abstraction temporarily

Revision history for this message
Kevin DuBois (kdub) wrote :

@Alexandros, stopped shy of using mt::spin_wait_for_condition_or_timeout, as I have to call the swap buffers function in the loop, and didn't want to call code while waiting for a condition. Should have fixed the rest.

@Chris, still hesitant? :)

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

Ah, right. Yes. The problem with returning the GPU fence from submit_buffer is that server ownership of that buffer quite likely extends past the GPU read.

Although, hm.

Under the assumption that fences are represented as pollable fds *and* that pollable fds can be used as fences¹, we *could* do the fence-is-allocated-at-buffer-allocation-time thing, by adding a new fence on each submit_buffer.

This requires fences that have a signalled→unsignalled transition, though, which is unusual.

I'm mostly happy with this; if it turns out we can do better then we can do that extra funky stuff later.

¹: Or, alternatively, that we can do composite fences; the main requirement is that we can post-hoc combine fences and Mir can create and signal a fence itself.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Now, for the mostly happy bit:

submit_buffer() shouldn't block in that fashion. Why does it need to block at all?

I'm also not entirely sure why submit_buffer returns a MirWaitHandle, and even if it does I don't think that the MirWaitHandle it's returning should be the ‘there's a buffer available’ wait handle.

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

mir_buffer_stream_swap_buffers() currently will call exchange buffer, and return when that operation is done (ie, when BufferQueue signals the complete() function from SessionMediator). In this first pass, the intention is to maintain the same behavior, by ensuring that submit_buffer is done, and a new buffer has arrived ('asynchronously').

Once the protocol changes have completed, we'll have to have to have some exposition of the buffer semantics in the client api (so that nested and multimedia can use the feature), and we'll have to support mir_buffer_stream_swap_buffers by re-plumbing ClientBufferDepository to be more aware of many buffers.

For now, its working this way just in the name of compatibility, and because we haven't rooted out the uni-buffer concept from other client side classes.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Isn't that the purpose of the done callback? Why is there the additional submitting condition variable, and the wait on that?

-----Original Message-----
From: "Kevin DuBois" <email address hidden>
Sent: ‎16/‎06/‎2015 21:35
To: "Kevin DuBois" <email address hidden>
Subject: Re: [Merge] lp:~kdub/mir/server-sent-buffer into lp:mir

mir_buffer_stream_swap_buffers() currently will call exchange buffer, and return when that operation is done (ie, when BufferQueue signals the complete() function from SessionMediator). In this first pass, the intention is to maintain the same behavior, by ensuring that submit_buffer is done, and a new buffer has arrived ('asynchronously').

Once the protocol changes have completed, we'll have to have to have some exposition of the buffer semantics in the client api (so that nested and multimedia can use the feature), and we'll have to support mir_buffer_stream_swap_buffers by re-plumbing ClientBufferDepository to be more aware of many buffers.

For now, its working this way just in the name of compatibility, and because we haven't rooted out the uni-buffer concept from other client side classes.
--
https://code.launchpad.net/~kdub/mir/server-sent-buffer/+merge/261421
You are reviewing the proposed merge of lp:~kdub/mir/server-sent-buffer into lp:mir.

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

> Isn't that the purpose of the done callback? Why is there the additional
> submitting condition variable, and the wait on that?

For now (in the intermediate form), before mcl::BufferStream::next_buffer() returns, we have to wait for two things to happen... for submit buffer to finish and for the new buffer to come back over ipc. If we had a buffer, then we could just process things in the done callback. If the event-buffer arrives after, however, we can wait in the callback (blocking RPC processing). It seemed easiest to just wait for submit buffer to be done like in this MP.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Why do we have to wait for submit_buffer to finish?

We process RPC requests in order on the server, so as soon as it's submitted we know that it's going to be processed at the appropriate time.

As far as I can see, the client never needs to know when the server has acknowledged the submission; and for the transition it only needs to know when there's a buffer available to render into.

I think of any behaviour that would be incorrect if you treated submit_buffer as not having any response at all. If you tried it, what went wrong?

Revision history for this message
Kevin DuBois (kdub) wrote :

@Chris, I see now what you're saying, makes sense. We don't have to wait for the /completion/ of submit_buffer on the server side, we just have to accomplish the sending-off of the submit_buffer request, and then get another buffer in the client side before returning.

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
Kevin DuBois (kdub) wrote :

seems like CI just stopped before getting around to unit/integration?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

Err http://ports.ubuntu.com/ubuntu-ports/ wily/universe glmark2-data all 2014.03+git20150611.fa71af2d-0ubuntu1
  Temporary failure resolving 'ports.ubuntu.com'

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

LGTM now.

review: Approve
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
=== modified file 'src/client/buffer_stream.cpp'
--- src/client/buffer_stream.cpp 2015-06-17 18:24:42 +0000
+++ src/client/buffer_stream.cpp 2015-06-19 17:03:24 +0000
@@ -167,7 +167,11 @@
167void mcl::BufferStream::process_buffer(mp::Buffer const& buffer)167void mcl::BufferStream::process_buffer(mp::Buffer const& buffer)
168{168{
169 std::unique_lock<decltype(mutex)> lock(mutex);169 std::unique_lock<decltype(mutex)> lock(mutex);
170 170 process_buffer(buffer, lock);
171}
172
173void mcl::BufferStream::process_buffer(protobuf::Buffer const& buffer, std::unique_lock<std::mutex> const&)
174{
171 auto buffer_package = std::make_shared<MirBufferPackage>();175 auto buffer_package = std::make_shared<MirBufferPackage>();
172 populate_buffer_package(*buffer_package, buffer);176 populate_buffer_package(*buffer_package, buffer);
173 177
@@ -202,6 +206,37 @@
202206
203 secured_region.reset();207 secured_region.reset();
204208
209 if (using_exchange_buffer)
210 return exchange(done, std::move(lock));
211 return submit(done, std::move(lock));
212}
213
214MirWaitHandle* mcl::BufferStream::submit(std::function<void()> const& done, std::unique_lock<std::mutex> lock)
215{
216 //always submit what we have, whether we have a buffer, or will have to wait for an async reply
217 auto request = mcl::make_protobuf_object<mp::BufferRequest>();
218 request->mutable_id()->set_value(protobuf_bs->id().value());
219 request->mutable_buffer()->set_buffer_id(protobuf_bs->buffer().buffer_id());
220 display_server.submit_buffer(nullptr, request.get(), protobuf_void.get(),
221 google::protobuf::NewCallback(google::protobuf::DoNothing));
222
223 if (incoming_buffers.empty())
224 {
225 next_buffer_wait_handle.expect_result();
226 on_incoming_buffer = done;
227 }
228 else
229 {
230 process_buffer(incoming_buffers.front(), lock);
231 incoming_buffers.pop();
232 done();
233 }
234 return &next_buffer_wait_handle;
235}
236
237MirWaitHandle* mcl::BufferStream::exchange(
238 std::function<void()> const& done, std::unique_lock<std::mutex> lock)
239{
205 // TODO: We can fix the strange "ID casting" used below in the second phase240 // TODO: We can fix the strange "ID casting" used below in the second phase
206 // of buffer stream which generalizes and clarifies the server side logic.241 // of buffer stream which generalizes and clarifies the server side logic.
207 if (mode == mcl::BufferStreamMode::Producer)242 if (mode == mcl::BufferStreamMode::Producer)
@@ -395,3 +430,21 @@
395{430{
396 buffer_depository.set_max_buffers(cache_size);431 buffer_depository.set_max_buffers(cache_size);
397}432}
433
434void mcl::BufferStream::buffer_available(mir::protobuf::Buffer const& buffer)
435{
436 std::unique_lock<decltype(mutex)> lock(mutex);
437 using_exchange_buffer = false; //this stream uses async exchange from here on out
438
439 if (on_incoming_buffer)
440 {
441 process_buffer(buffer, lock);
442 next_buffer_wait_handle.result_received();
443 on_incoming_buffer();
444 on_incoming_buffer = std::function<void()>{};
445 }
446 else
447 {
448 incoming_buffers.push(buffer);
449 }
450}
398451
=== modified file 'src/client/buffer_stream.h'
--- src/client/buffer_stream.h 2015-06-17 18:24:42 +0000
+++ src/client/buffer_stream.h 2015-06-19 17:03:24 +0000
@@ -31,6 +31,7 @@
3131
32#include <EGL/eglplatform.h>32#include <EGL/eglplatform.h>
3333
34#include <queue>
34#include <memory>35#include <memory>
35#include <mutex>36#include <mutex>
3637
@@ -104,6 +105,9 @@
104105
105 frontend::BufferStreamId rpc_id() const override;106 frontend::BufferStreamId rpc_id() const override;
106 bool valid() const override;107 bool valid() const override;
108
109 void buffer_available(mir::protobuf::Buffer const& buffer) override;
110
107protected:111protected:
108 BufferStream(BufferStream const&) = delete;112 BufferStream(BufferStream const&) = delete;
109 BufferStream& operator=(BufferStream const&) = delete;113 BufferStream& operator=(BufferStream const&) = delete;
@@ -111,13 +115,20 @@
111private:115private:
112 void created(mir_buffer_stream_callback callback, void* context);116 void created(mir_buffer_stream_callback callback, void* context);
113 void process_buffer(protobuf::Buffer const& buffer);117 void process_buffer(protobuf::Buffer const& buffer);
118 void process_buffer(protobuf::Buffer const& buffer, std::unique_lock<std::mutex> const&);
114 void next_buffer_received(119 void next_buffer_received(
115 std::function<void()> done);120 std::function<void()> done);
116 void on_configured();121 void on_configured();
117 void release_cpu_region();122 void release_cpu_region();
123 MirWaitHandle* exchange(std::function<void()> const& done, std::unique_lock<std::mutex> lk);
124 MirWaitHandle* submit(std::function<void()> const& done, std::unique_lock<std::mutex> lk);
118125
119 mutable std::mutex mutex; // Protects all members of *this126 mutable std::mutex mutex; // Protects all members of *this
120127
128 bool using_exchange_buffer = true;
129 std::function<void()> on_incoming_buffer;
130 std::queue<mir::protobuf::Buffer> incoming_buffers;
131
121 MirConnection* connection;132 MirConnection* connection;
122 mir::protobuf::DisplayServer& display_server;133 mir::protobuf::DisplayServer& display_server;
123134
124135
=== modified file 'src/client/client_buffer_stream.h'
--- src/client/client_buffer_stream.h 2015-06-17 05:20:42 +0000
+++ src/client/client_buffer_stream.h 2015-06-19 17:03:24 +0000
@@ -63,7 +63,8 @@
63 virtual MirWaitHandle* release(mir_buffer_stream_callback callback, void* context) = 0;63 virtual MirWaitHandle* release(mir_buffer_stream_callback callback, void* context) = 0;
6464
65 virtual bool valid() const = 0;65 virtual bool valid() const = 0;
66 66 virtual void buffer_available(mir::protobuf::Buffer const& buffer) = 0;
67
67protected:68protected:
68 ClientBufferStream() = default;69 ClientBufferStream() = default;
69 ClientBufferStream(const ClientBufferStream&) = delete;70 ClientBufferStream(const ClientBufferStream&) = delete;
7071
=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp'
--- src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-06-17 18:24:42 +0000
+++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2015-06-19 17:03:24 +0000
@@ -251,6 +251,14 @@
251 lifecycle_control->call_lifecycle_event_handler(seq->lifecycle_event().new_state());251 lifecycle_control->call_lifecycle_event_handler(seq->lifecycle_event().new_state());
252 }252 }
253253
254 if (seq->has_buffer_request())
255 {
256 surface_map->with_stream_do(mf::BufferStreamId(seq->buffer_request().id().value()),
257 [&] (mcl::ClientBufferStream* stream) {
258 stream->buffer_available(seq->buffer_request().buffer());
259 });
260 }
261
254 int const nevents = seq->event_size();262 int const nevents = seq->event_size();
255 for (int i = 0; i != nevents; ++i)263 for (int i = 0; i != nevents; ++i)
256 {264 {
257265
=== modified file 'src/include/server/mir/frontend/event_sink.h'
--- src/include/server/mir/frontend/event_sink.h 2015-02-22 07:46:25 +0000
+++ src/include/server/mir/frontend/event_sink.h 2015-06-19 17:03:24 +0000
@@ -20,12 +20,14 @@
20#define MIR_EVENTS_EVENT_SINK_H_20#define MIR_EVENTS_EVENT_SINK_H_
2121
22#include "mir_toolkit/event.h"22#include "mir_toolkit/event.h"
23#include "mir/frontend/buffer_stream_id.h"
2324
24namespace mir25namespace mir
25{26{
26namespace graphics27namespace graphics
27{28{
28class DisplayConfiguration;29class DisplayConfiguration;
30class Buffer;
29}31}
30namespace frontend32namespace frontend
31{33{
@@ -37,6 +39,7 @@
37 virtual void handle_event(MirEvent const& e) = 0;39 virtual void handle_event(MirEvent const& e) = 0;
38 virtual void handle_lifecycle_event(MirLifecycleState state) = 0;40 virtual void handle_lifecycle_event(MirLifecycleState state) = 0;
39 virtual void handle_display_config_change(graphics::DisplayConfiguration const& config) = 0;41 virtual void handle_display_config_change(graphics::DisplayConfiguration const& config) = 0;
42 virtual void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer) = 0;
4043
41protected:44protected:
42 EventSink() = default;45 EventSink() = default;
4346
=== modified file 'src/protobuf/mir_protobuf.proto'
--- src/protobuf/mir_protobuf.proto 2015-06-17 19:16:03 +0000
+++ src/protobuf/mir_protobuf.proto 2015-06-19 17:03:24 +0000
@@ -245,6 +245,8 @@
245 repeated Event event = 1;245 repeated Event event = 1;
246 optional DisplayConfiguration display_configuration = 2;246 optional DisplayConfiguration display_configuration = 2;
247 optional LifecycleEvent lifecycle_event = 3;247 optional LifecycleEvent lifecycle_event = 3;
248 optional BufferRequest buffer_request = 4;
249
248 optional string error = 127;250 optional string error = 127;
249}251}
250252
251253
=== modified file 'src/server/frontend/event_sender.cpp'
--- src/server/frontend/event_sender.cpp 2015-06-17 05:20:42 +0000
+++ src/server/frontend/event_sender.cpp 2015-06-19 17:03:24 +0000
@@ -24,6 +24,8 @@
24#include "message_sender.h"24#include "message_sender.h"
25#include "protobuf_buffer_packer.h"25#include "protobuf_buffer_packer.h"
2626
27#include "mir/graphics/buffer.h"
28
27#include "mir_protobuf_wire.pb.h"29#include "mir_protobuf_wire.pb.h"
28#include "mir_protobuf.pb.h"30#include "mir_protobuf.pb.h"
2931
@@ -95,3 +97,12 @@
95 (void) error;97 (void) error;
96 }98 }
97}99}
100
101void mfd::EventSender::send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer)
102{
103 mp::EventSequence seq;
104 auto request = seq.mutable_buffer_request();
105 request->mutable_id()->set_value(id.as_value());
106 request->mutable_buffer()->set_buffer_id(buffer.id().as_value());
107 send_event_sequence(seq);
108}
98109
=== modified file 'src/server/frontend/event_sender.h'
--- src/server/frontend/event_sender.h 2015-02-22 07:46:25 +0000
+++ src/server/frontend/event_sender.h 2015-06-19 17:03:24 +0000
@@ -41,6 +41,7 @@
41 void handle_event(MirEvent const& e);41 void handle_event(MirEvent const& e);
42 void handle_lifecycle_event(MirLifecycleState state);42 void handle_lifecycle_event(MirLifecycleState state);
43 void handle_display_config_change(graphics::DisplayConfiguration const& config);43 void handle_display_config_change(graphics::DisplayConfiguration const& config);
44 void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer);
4445
45private:46private:
46 void send_event_sequence(protobuf::EventSequence&);47 void send_event_sequence(protobuf::EventSequence&);
4748
=== modified file 'src/server/scene/global_event_sender.cpp'
--- src/server/scene/global_event_sender.cpp 2015-02-22 07:46:25 +0000
+++ src/server/scene/global_event_sender.cpp 2015-06-19 17:03:24 +0000
@@ -45,3 +45,7 @@
45 session->send_display_config(config);45 session->send_display_config(config);
46 });46 });
47}47}
48
49void ms::GlobalEventSender::send_buffer(mir::frontend::BufferStreamId, mg::Buffer&)
50{
51}
4852
=== modified file 'src/server/scene/global_event_sender.h'
--- src/server/scene/global_event_sender.h 2015-02-22 07:46:25 +0000
+++ src/server/scene/global_event_sender.h 2015-06-19 17:03:24 +0000
@@ -36,6 +36,7 @@
36 void handle_event(MirEvent const& e);36 void handle_event(MirEvent const& e);
37 void handle_lifecycle_event(MirLifecycleState state);37 void handle_lifecycle_event(MirLifecycleState state);
38 void handle_display_config_change(graphics::DisplayConfiguration const& config);38 void handle_display_config_change(graphics::DisplayConfiguration const& config);
39 void send_buffer(frontend::BufferStreamId id, graphics::Buffer& buffer);
3940
40private:41private:
41 std::shared_ptr<SessionContainer> const sessions;42 std::shared_ptr<SessionContainer> const sessions;
4243
=== modified file 'tests/include/mir_test_doubles/mock_client_buffer_stream.h'
--- tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-06-17 05:20:42 +0000
+++ tests/include/mir_test_doubles/mock_client_buffer_stream.h 2015-06-19 17:03:24 +0000
@@ -45,6 +45,7 @@
45 MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void));45 MOCK_METHOD0(get_create_wait_handle, MirWaitHandle*(void));
46 MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void));46 MOCK_CONST_METHOD0(rpc_id, frontend::BufferStreamId(void));
47 MOCK_CONST_METHOD0(valid, bool(void));47 MOCK_CONST_METHOD0(valid, bool(void));
48 MOCK_METHOD1(buffer_available, void(mir::protobuf::Buffer const&));
48};49};
4950
50}51}
5152
=== modified file 'tests/include/mir_test_doubles/mock_event_sink.h'
--- tests/include/mir_test_doubles/mock_event_sink.h 2015-02-11 22:10:31 +0000
+++ tests/include/mir_test_doubles/mock_event_sink.h 2015-06-19 17:03:24 +0000
@@ -34,6 +34,7 @@
34 MOCK_METHOD1(handle_event, void(MirEvent const&));34 MOCK_METHOD1(handle_event, void(MirEvent const&));
35 MOCK_METHOD1(handle_lifecycle_event, void(MirLifecycleState));35 MOCK_METHOD1(handle_lifecycle_event, void(MirLifecycleState));
36 MOCK_METHOD1(handle_display_config_change, void(graphics::DisplayConfiguration const&));36 MOCK_METHOD1(handle_display_config_change, void(graphics::DisplayConfiguration const&));
37 MOCK_METHOD2(send_buffer, void(frontend::BufferStreamId, graphics::Buffer&));
37};38};
38}39}
39}40}
4041
=== modified file 'tests/include/mir_test_doubles/null_event_sink.h'
--- tests/include/mir_test_doubles/null_event_sink.h 2015-02-22 07:46:25 +0000
+++ tests/include/mir_test_doubles/null_event_sink.h 2015-06-19 17:03:24 +0000
@@ -32,6 +32,7 @@
32 void handle_event(MirEvent const&) {}32 void handle_event(MirEvent const&) {}
33 void handle_lifecycle_event(MirLifecycleState) {}33 void handle_lifecycle_event(MirLifecycleState) {}
34 void handle_display_config_change(graphics::DisplayConfiguration const&) {}34 void handle_display_config_change(graphics::DisplayConfiguration const&) {}
35 void send_buffer(frontend::BufferStreamId, graphics::Buffer&) {}
35};36};
36}37}
37}38}
3839
=== modified file 'tests/integration-tests/test_exchange_buffer.cpp'
--- tests/integration-tests/test_exchange_buffer.cpp 2015-06-17 19:16:03 +0000
+++ tests/integration-tests/test_exchange_buffer.cpp 2015-06-19 17:03:24 +0000
@@ -28,10 +28,13 @@
28#include "mir/graphics/buffer_ipc_message.h"28#include "mir/graphics/buffer_ipc_message.h"
29#include "mir/graphics/platform_operation_message.h"29#include "mir/graphics/platform_operation_message.h"
30#include "mir/scene/buffer_stream_factory.h"30#include "mir/scene/buffer_stream_factory.h"
31#include "mir/scene/session_coordinator.h"
32#include "mir/frontend/event_sink.h"
31#include "mir/compositor/buffer_stream.h"33#include "mir/compositor/buffer_stream.h"
32#include "src/server/compositor/buffer_bundle.h"34#include "src/server/compositor/buffer_bundle.h"
33#include "src/server/compositor/buffer_stream_surfaces.h"35#include "src/server/compositor/buffer_stream_surfaces.h"
34#include "mir_toolkit/mir_client_library.h"36#include "mir_toolkit/mir_client_library.h"
37#include "mir_toolkit/debug/surface.h"
35#include "src/client/mir_connection.h"38#include "src/client/mir_connection.h"
36#include <chrono>39#include <chrono>
37#include <mutex>40#include <mutex>
@@ -49,6 +52,7 @@
49namespace mc = mir::compositor;52namespace mc = mir::compositor;
50namespace geom = mir::geometry;53namespace geom = mir::geometry;
51namespace mp = mir::protobuf;54namespace mp = mir::protobuf;
55namespace mf = mir::frontend;
5256
53namespace57namespace
54{58{
@@ -176,6 +180,47 @@
176 std::shared_ptr<mg::PlatformIpcOperations> const ipc_ops;180 std::shared_ptr<mg::PlatformIpcOperations> const ipc_ops;
177};181};
178182
183class SinkSkimmingCoordinator : public msc::SessionCoordinator
184{
185public:
186 SinkSkimmingCoordinator(std::shared_ptr<msc::SessionCoordinator> const& wrapped) :
187 wrapped(wrapped)
188 {
189 }
190
191 void set_focus_to(std::shared_ptr<msc::Session> const& focus) override
192 {
193 wrapped->set_focus_to(focus);
194 }
195
196 void unset_focus() override
197 {
198 wrapped->unset_focus();
199 }
200
201 std::shared_ptr<msc::Session> open_session(
202 pid_t client_pid,
203 std::string const& name,
204 std::shared_ptr<mf::EventSink> const& sink) override
205 {
206 last_sink = sink;
207 return wrapped->open_session(client_pid, name, sink);
208 }
209
210 void close_session(std::shared_ptr<msc::Session> const& session) override
211 {
212 wrapped->close_session(session);
213 }
214
215 std::shared_ptr<msc::Session> successor_of(std::shared_ptr<msc::Session> const& session) const override
216 {
217 return wrapped->successor_of(session);
218 }
219
220 std::shared_ptr<msc::SessionCoordinator> const wrapped;
221 std::weak_ptr<mf::EventSink> last_sink;
222};
223
179struct ExchangeServerConfiguration : mtf::StubbedServerConfiguration224struct ExchangeServerConfiguration : mtf::StubbedServerConfiguration
180{225{
181 ExchangeServerConfiguration(226 ExchangeServerConfiguration(
@@ -186,6 +231,14 @@
186 {231 {
187 }232 }
188233
234 std::shared_ptr<msc::SessionCoordinator> the_session_coordinator() override
235 {
236 return session_coordinator([this]{
237 coordinator = std::make_shared<SinkSkimmingCoordinator>(
238 DefaultServerConfiguration::the_session_coordinator());
239 return coordinator;
240 });
241 }
189 std::shared_ptr<mg::Platform> the_graphics_platform() override242 std::shared_ptr<mg::Platform> the_graphics_platform() override
190 {243 {
191 return platform;244 return platform;
@@ -196,8 +249,9 @@
196 return stream_factory;249 return stream_factory;
197 }250 }
198251
199 std::shared_ptr<msc::BufferStreamFactory> const stream_factory;252 std::shared_ptr<StubBundleFactory> const stream_factory;
200 std::shared_ptr<mg::Platform> const platform;253 std::shared_ptr<mg::Platform> const platform;
254 std::shared_ptr<SinkSkimmingCoordinator> coordinator;
201};255};
202256
203struct ExchangeBufferTest : mir_test_framework::InProcessServer257struct ExchangeBufferTest : mir_test_framework::InProcessServer
@@ -217,8 +271,6 @@
217 cv.notify_all();271 cv.notify_all();
218 }272 }
219273
220 //TODO: once the next_buffer rpc is deprecated, change this code out for the
221 // mir_surface_next_buffer() api call
222 bool exchange_buffer(mp::DisplayServer& server)274 bool exchange_buffer(mp::DisplayServer& server)
223 {275 {
224 std::unique_lock<decltype(mutex)> lk(mutex);276 std::unique_lock<decltype(mutex)> lk(mutex);
@@ -359,6 +411,37 @@
359 mir_connection_release(connection);411 mir_connection_release(connection);
360}412}
361413
414TEST_F(ExchangeBufferTest, server_can_send_buffer)
415{
416 using namespace testing;
417 using namespace std::literals::chrono_literals;
418 mtd::StubBuffer stub_buffer;
419 auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
420 auto surface = mtf::make_any_surface(connection);
421 auto sink = server_configuration.coordinator->last_sink.lock();
422 sink->send_buffer(mf::BufferStreamId{0}, stub_buffer);
423
424 //spin-wait for the id to become the current one.
425 //The notification doesn't generate a client-facing callback on the stream yet
426 //(although probably should, seems something a media decoder would need
427 bool buffer_arrived = false;
428 auto timeout = std::chrono::steady_clock::now() + 5s;
429 while(!buffer_arrived && std::chrono::steady_clock::now() < timeout)
430 {
431 mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
432 if (mir_debug_surface_current_buffer_id(surface) == stub_buffer.id().as_value())
433 {
434 buffer_arrived = true;
435 break;
436 }
437 std::this_thread::yield();
438 }
439 EXPECT_THAT(buffer_arrived, Eq(true)) << "failed to see the sent buffer become the current one";
440
441 mir_surface_release_sync(surface);
442 mir_connection_release(connection);
443}
444
362//TODO: check that a buffer arrives asynchronously.445//TODO: check that a buffer arrives asynchronously.
363TEST_F(ExchangeBufferTest, allocate_buffers_doesnt_time_out)446TEST_F(ExchangeBufferTest, allocate_buffers_doesnt_time_out)
364{447{
365448
=== modified file 'tests/unit-tests/client/test_client_buffer_stream.cpp'
--- tests/unit-tests/client/test_client_buffer_stream.cpp 2015-06-17 05:20:42 +0000
+++ tests/unit-tests/client/test_client_buffer_stream.cpp 2015-06-19 17:03:24 +0000
@@ -50,6 +50,11 @@
50 mp::ScreencastId const* /*request*/,50 mp::ScreencastId const* /*request*/,
51 mp::Buffer* /*response*/,51 mp::Buffer* /*response*/,
52 google::protobuf::Closure* /*done*/));52 google::protobuf::Closure* /*done*/));
53 MOCK_METHOD4(submit_buffer,
54 void(google::protobuf::RpcController* /*controller*/,
55 mp::BufferRequest const* /*request*/,
56 mp::Void* /*response*/,
57 google::protobuf::Closure* /*done*/));
53 MOCK_METHOD4(exchange_buffer,58 MOCK_METHOD4(exchange_buffer,
54 void(google::protobuf::RpcController* /*controller*/,59 void(google::protobuf::RpcController* /*controller*/,
55 mp::BufferRequest const* /*request*/,60 mp::BufferRequest const* /*request*/,
@@ -113,7 +118,7 @@
113118
114struct ClientBufferStreamTest : public testing::Test119struct ClientBufferStreamTest : public testing::Test
115{120{
116 mtd::MockClientBufferFactory mock_client_buffer_factory;121 testing::NiceMock<mtd::MockClientBufferFactory> mock_client_buffer_factory;
117 mtd::StubClientBufferFactory stub_client_buffer_factory;122 mtd::StubClientBufferFactory stub_client_buffer_factory;
118123
119 MockProtobufServer mock_protobuf_server;124 MockProtobufServer mock_protobuf_server;
@@ -283,7 +288,7 @@
283288
284 auto bs = make_buffer_stream(protobuf_bs, mcl::BufferStreamMode::Producer);289 auto bs = make_buffer_stream(protobuf_bs, mcl::BufferStreamMode::Producer);
285 290
286 bs->next_buffer([](){});291 bs->next_buffer([]{});
287}292}
288293
289TEST_F(ClientBufferStreamTest, consumer_streams_call_screencast_buffer_on_next_buffer)294TEST_F(ClientBufferStreamTest, consumer_streams_call_screencast_buffer_on_next_buffer)
@@ -298,7 +303,7 @@
298303
299 auto bs = make_buffer_stream(protobuf_bs, mcl::BufferStreamMode::Consumer);304 auto bs = make_buffer_stream(protobuf_bs, mcl::BufferStreamMode::Consumer);
300 305
301 bs->next_buffer([](){});306 bs->next_buffer([]{});
302}307}
303308
304TEST_F(ClientBufferStreamTest, invokes_callback_on_next_buffer)309TEST_F(ClientBufferStreamTest, invokes_callback_on_next_buffer)
@@ -363,7 +368,7 @@
363 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);368 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);
364369
365 EXPECT_EQ(client_buffer_1, bs->get_current_buffer());370 EXPECT_EQ(client_buffer_1, bs->get_current_buffer());
366 bs->next_buffer([](){});371 bs->next_buffer([]{});
367 EXPECT_EQ(client_buffer_2, bs->get_current_buffer());372 EXPECT_EQ(client_buffer_2, bs->get_current_buffer());
368}373}
369374
@@ -397,7 +402,7 @@
397 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);402 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);
398403
399 EXPECT_EQ(client_buffer_1, bs->get_current_buffer());404 EXPECT_EQ(client_buffer_1, bs->get_current_buffer());
400 bs->next_buffer([](){});405 bs->next_buffer([]{});
401 EXPECT_EQ(client_buffer_2, bs->get_current_buffer());406 EXPECT_EQ(client_buffer_2, bs->get_current_buffer());
402}407}
403408
@@ -453,3 +458,60 @@
453 std::make_shared<StubClientPlatform>(mt::fake_shared(stub_client_buffer_factory)),458 std::make_shared<StubClientPlatform>(mt::fake_shared(stub_client_buffer_factory)),
454 protobuf_bs, mt::fake_shared(mock_perf_report), name);459 protobuf_bs, mt::fake_shared(mock_perf_report), name);
455}460}
461
462TEST_F(ClientBufferStreamTest, receives_unsolicited_buffer)
463{
464 using namespace ::testing;
465 int id = 88;
466 MockClientBuffer mock_client_buffer;
467 MockClientBuffer second_mock_client_buffer;
468 MirBufferPackage buffer_package = a_buffer_package();
469 auto protobuf_bs = a_protobuf_buffer_stream(default_pixel_format, default_buffer_usage, buffer_package);
470 EXPECT_CALL(mock_client_buffer_factory, create_buffer(BufferPackageMatches(buffer_package),_,_))
471 .WillOnce(Return(mt::fake_shared(mock_client_buffer)));
472 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);
473
474 mir::protobuf::Buffer another_buffer_package;
475 another_buffer_package.set_buffer_id(id);
476 EXPECT_CALL(mock_client_buffer_factory, create_buffer(_,_,_))
477 .WillOnce(Return(mt::fake_shared(second_mock_client_buffer)));
478 EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_,_))
479 .WillOnce(RunProtobufClosure());
480 bs->buffer_available(another_buffer_package);
481 bs->next_buffer([]{});
482
483 EXPECT_THAT(bs->get_current_buffer().get(), Eq(&second_mock_client_buffer));
484 EXPECT_THAT(bs->get_current_buffer_id(), Eq(id));
485}
486
487//useful only in transitioning from exchange to async
488TEST_F(ClientBufferStreamTest, after_receiving_an_unsolicited_buffer_exchange_buffer_return_are_ignored)
489{
490 using namespace ::testing;
491 int id = 88;
492 MockClientBuffer mock_client_buffer;
493 MockClientBuffer second_mock_client_buffer;
494 MirBufferPackage buffer_package = a_buffer_package();
495 mir::protobuf::Buffer another_buffer_package;
496 another_buffer_package.set_buffer_id(id);
497 auto protobuf_bs = a_protobuf_buffer_stream(default_pixel_format, default_buffer_usage, buffer_package);
498 ON_CALL(mock_client_buffer_factory, create_buffer(_,_,_))
499 .WillByDefault(Return(mt::fake_shared(mock_client_buffer)));
500 auto bs = make_buffer_stream(protobuf_bs, mock_client_buffer_factory);
501
502 int a_few_times = 11;
503 EXPECT_CALL(mock_protobuf_server, exchange_buffer(_,_,_,_))
504 .Times(a_few_times)
505 .WillRepeatedly(RunProtobufClosure());
506 EXPECT_CALL(mock_protobuf_server, submit_buffer(_,_,_,_))
507 .Times(a_few_times)
508 .WillRepeatedly(RunProtobufClosure());
509
510 for(auto i = 0; i < a_few_times; i++)
511 bs->next_buffer([]{});
512
513 bs->buffer_available(another_buffer_package);
514
515 for(auto i = 0; i < a_few_times; i++)
516 bs->next_buffer([]{});
517}

Subscribers

People subscribed via source and target branches