Mir

Merge lp:~raof/mir/use-dma-buf into lp:~mir-team/mir/trunk

Proposed by Chris Halse Rogers
Status: Merged
Approved by: Chris Halse Rogers
Approved revision: no longer in the source branch.
Merged at revision: 580
Proposed branch: lp:~raof/mir/use-dma-buf
Merge into: lp:~mir-team/mir/trunk
Diff against target: 1105 lines (+546/-107)
24 files modified
include/server/mir/frontend/session_mediator.h (+2/-0)
include/shared/mir/frontend/client_constants.h (+35/-0)
src/client/client_buffer_depository.cpp (+23/-33)
src/client/client_buffer_depository.h (+13/-5)
src/client/gbm/drm_fd_handler.h (+2/-0)
src/client/gbm/gbm_client_buffer.cpp (+19/-16)
src/client/gbm/gbm_client_buffer.h (+2/-0)
src/client/gbm/gbm_client_platform.cpp (+17/-0)
src/client/mir_surface.cpp (+2/-1)
src/server/frontend/CMakeLists.txt (+1/-0)
src/server/frontend/client_buffer_tracker.cpp (+53/-0)
src/server/frontend/client_buffer_tracker.h (+62/-0)
src/server/frontend/session_mediator.cpp (+36/-23)
src/server/graphics/gbm/gbm_buffer.cpp (+6/-8)
src/server/graphics/gbm/gbm_buffer.h (+1/-1)
tests/unit-tests/client/gbm/mock_drm_fd_handler.h (+2/-0)
tests/unit-tests/client/gbm/test_gbm_client_buffer.cpp (+38/-9)
tests/unit-tests/client/test_client_buffer_depository.cpp (+33/-0)
tests/unit-tests/frontend/CMakeLists.txt (+1/-0)
tests/unit-tests/frontend/test_client_buffer_tracker.cpp (+108/-0)
tests/unit-tests/frontend/test_session_mediator.cpp (+69/-3)
tests/unit-tests/graphics/gbm/mock_drm.cpp (+10/-0)
tests/unit-tests/graphics/gbm/mock_drm.h (+3/-0)
tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp (+8/-8)
To merge this branch: bzr merge lp:~raof/mir/use-dma-buf
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Needs Fixing
Robert Carr (community) Approve
Kevin DuBois (community) Approve
Alan Griffiths Approve
Chris Halse Rogers Approve
Review via email: mp+153267@code.launchpad.net

Commit message

Change from sending flink'd gem names over IPC to sending dma-buf fds.

This means that clients can no longer snoop on other client's buffers
by guessing (easily guessable) flink names.

Fixes LP: #1124631

Description of the change

Change from sending flink'd gem names over IPC to sending dma-buf fds.

This means that clients can no longer snoop on other client's buffers
by guessing (easily guessable) flink names.

Fixes LP: #1124631

To post a comment you must log in.
Revision history for this message
Chris Halse Rogers (raof) wrote :

Note that this should be not be merged until we're ready to roll out updated the XMir and Mesa code; otherwise it breaks them.

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

Changed to Work In Progress due to raof's comment above.

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

Should now be good to go. The mesa changes are ready for when this lands.

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

I'm Chris Halse Rogers, and I approve this MP

review: Approve
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 :

It's worth noting that this breaks existing accelerated GBM clients, like Mesa. A working mesa is available in the mir-ppa-dma-buf of my github tree - https://github.com/RAOF/mesa

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

> I'm Chris Halse Rogers, and I approve this MP

That's cheating ;)

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

src/server/frontend/client_buffer_tracker.cpp
src/server/frontend/client_buffer_tracker.h
... perhaps in other files too

Tabs instead of 4 spaces.

190 + // TODO: Do we need to GEM_CLOSE here? Do we need to close the fd?

Any update on this? I'd rather we ensure we don't leak handles and fds, before getting this in.

356 +class ClientBufferTracker

Delete copy constructor and assignment operator if not needed.

...and some nits:

139 + const unsigned int max_buffers;

unsigned int const ...

711 + mc::BufferID id{5};
... and others in tests

Could be const.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Cool!

Lots of tabs. i.e. 284.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

479 + std::list<uint32_t>::iterator find_buffer(compositor::BufferID const& id);
480 +
481 + std::list<uint32_t> ids;

Why not use std::list<BufferID> (BufferID has a comparison operator, and should be as efficient as using an uint32_t directly) and std::find() instead?

745 + // We don't map the buffer, so we don't need to take a GEM reference...
746 + EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
747 + .Times(0);
748 + // We haven't taken a GEM reference, so we shouldn't close it.
749 + EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
750 + .Times(0);

Do these add any value to the prime_fd_closed_on_buffer_destruction test? They are already checked for in the buffer_does_not_take_a_gem_reference_when_not_mapping test.

Also, as a general comment, expectations should be set before creating and using any objects that depend on the mock objects (unless there is a strong reason to do otherwise).

1004 + mediator.create_surface(nullptr, &surface_request, &surface_response, null_callback.get());
1005 +
1006 + mediator.next_buffer(nullptr, &buffer_request, &buffer_response[0], null_callback.get());

It's also worth testing that the mediator sends the expected buffer data the first time around.

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

22 === added file 'include/shared/mir/frontend/client_constants.h
...
52 +/// mir::client::ClientBufferDepository and mir::frontend::ClientBufferTracker need to use the same value
53 +unsigned int const client_buffer_cache_size = 3;

There's obviously conflicting forces to resolve in deciding where to put this, but I don't think namespace mir is the right answer. Especially when the file suggests namespace mir::frontend.

At the moment I'm struggling to understand why the server needs to be *compiled* with the size of a client-side cache - but maybe I'll grok that later. In any case, something here needs fixing.

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

--- Alexandros ---

> 479 + std::list<uint32_t>::iterator find_buffer(compositor::BufferID const& id);
> 480 +
> 481 + std::list<uint32_t> ids;

> Why not use std::list<BufferID> (BufferID has a comparison operator, and should be as efficient as using an uint32_t directly) and std::find() instead?

Hm, yeah, that works better. Thanks!

> 745 + // We don't map the buffer, so we don't need to take a GEM reference...
> 746 + EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
> 747 + .Times(0);
> 748 + // We haven't taken a GEM reference, so we shouldn't close it.
> 749 + EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
> 750 + .Times(0);

> Do these add any value to the prime_fd_closed_on_buffer_destruction test? They are already checked for in the buffer_does_not_take_a_gem_reference_when_not_mapping test.

Oh, no. I forgot to delete that bit when I decided to make buffer_does_not_take_a_gem_reference_when_not_mapping a separate test.

> 1004 + mediator.create_surface(nullptr, &surface_request, &surface_response, null_callback.get());
> 1005 +
> 1006 + mediator.next_buffer(nullptr, &buffer_request, &buffer_response[0], null_callback.get());

> It's also worth testing that the mediator sends the expected buffer data the first time around.

Done.

--- Alan ---
> At the moment I'm struggling to understand why the server needs to be *compiled* with the size of a client-side cache - but maybe I'll grok that later. In any case, something here needs fixing.

The server and the client need to negotiate the size of the cache, because the server will not send data for buffers it knows the client has cached.

The easiest way to do this is to compile in a fixed value; if need be we can move to explicit IPC negotiation later.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

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

106 + for (auto pair = buffers.begin(); pair != buffers.end(); ++pair)

you can write:

for (auto& pair : buffers)

(With a bit of s/->/./)

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

> 106 + for (auto pair = buffers.begin(); pair != buffers.end(); ++pair)
>
> you can write:
>
> for (auto& pair : buffers)
>
> (With a bit of s/->/./)

Forget that - I just realized you store the iterator.

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

looks good to me too. Side point: I recently rooted the ID out of the buffer swapper, i think we're getting pretty close to being able to isolate the ID to just the frontend... (a good thing)

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

LGTM

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

Please don't merge this yet, as the Mesa side for radeon and nouveau is not ready yet. Marking as "needs fixing" in order to block this until the mesa side is ready.

review: Needs Fixing
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 :

Marked as approved, as the mesa changes for radeon and nouveau are now available.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/frontend/session_mediator.h'
2--- include/server/mir/frontend/session_mediator.h 2013-03-21 03:32:59 +0000
3+++ include/server/mir/frontend/session_mediator.h 2013-04-10 08:47:23 +0000
4@@ -47,6 +47,7 @@
5 class Session;
6 class ResourceCache;
7 class SessionMediatorReport;
8+class ClientBufferTracker;
9
10 // SessionMediator relays requests from the client process into the server.
11 class SessionMediator : public mir::protobuf::DisplayServer
12@@ -116,6 +117,7 @@
13
14 std::shared_ptr<SessionMediatorReport> const report;
15 std::shared_ptr<ResourceCache> const resource_cache;
16+ std::shared_ptr<ClientBufferTracker> const client_tracker;
17
18 std::shared_ptr<Session> session;
19 };
20
21=== added directory 'include/shared/mir/frontend'
22=== added file 'include/shared/mir/frontend/client_constants.h'
23--- include/shared/mir/frontend/client_constants.h 1970-01-01 00:00:00 +0000
24+++ include/shared/mir/frontend/client_constants.h 2013-04-10 08:47:23 +0000
25@@ -0,0 +1,35 @@
26+/*
27+ * Copyright © 2013 Canonical Ltd.
28+ *
29+ * This program is free software: you can redistribute it and/or modify
30+ * it under the terms of the GNU Lesser General Public License version 3 as
31+ * published by the Free Software Foundation.
32+ *
33+ * This program is distributed in the hope that it will be useful,
34+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
35+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36+ * GNU General Public License for more details.
37+ *
38+ * You should have received a copy of the GNU Lesser General Public License
39+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
40+ *
41+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
42+ */
43+
44+#ifndef MIR_FRONTEND_CLIENT_CONSTANTS_H_
45+#define MIR_FRONTEND_CLIENT_CONSTANTS_H_
46+
47+namespace mir
48+{
49+
50+namespace frontend
51+{
52+/// Number of buffers the client library will keep.
53+
54+/// mir::client::ClientBufferDepository and mir::frontend::ClientBufferTracker need to use the same value
55+unsigned int const client_buffer_cache_size = 3;
56+
57+}
58+}
59+
60+#endif
61
62=== modified file 'src/client/client_buffer_depository.cpp'
63--- src/client/client_buffer_depository.cpp 2013-03-29 11:27:00 +0000
64+++ src/client/client_buffer_depository.cpp 2013-04-10 08:47:23 +0000
65@@ -29,49 +29,39 @@
66
67 mcl::ClientBufferDepository::ClientBufferDepository(std::shared_ptr<ClientBufferFactory> const& factory, int max_buffers)
68 : factory(factory),
69- current_buffer_iter(buffers.end()),
70- max_age(max_buffers - 1)
71+ max_buffers(max_buffers)
72 {
73 }
74
75 void mcl::ClientBufferDepository::deposit_package(std::shared_ptr<MirBufferPackage> const& package, int id, geometry::Size size, geometry::PixelFormat pf)
76 {
77- for (auto next = buffers.begin(); next != buffers.end();)
78- {
79- // C++ guarantees that buffers.erase() will only invalidate the iterator we
80- // erase. Move to the next buffer before we potentially invalidate our iterator.
81- auto current = next;
82- next++;
83-
84- if (current != current_buffer_iter &&
85- current->first != id &&
86- current->second->age() >= max_age)
87- {
88- buffers.erase(current);
89- }
90- }
91-
92- for (auto& current : buffers)
93- {
94- current.second->increment_age();
95- }
96-
97- if (current_buffer_iter != buffers.end())
98- {
99- current_buffer_iter->second->mark_as_submitted();
100- }
101-
102- current_buffer_iter = buffers.find(id);
103-
104- if (current_buffer_iter == buffers.end())
105+ auto existing_buffer_id_pair = buffers.end();
106+ for (auto pair = buffers.begin(); pair != buffers.end(); ++pair)
107+ {
108+ pair->second->increment_age();
109+ if (pair->first == id)
110+ existing_buffer_id_pair = pair;
111+ }
112+
113+ if (buffers.size() > 0)
114+ buffers.front().second->mark_as_submitted();
115+
116+ if (existing_buffer_id_pair == buffers.end())
117 {
118 auto new_buffer = factory->create_buffer(package, size, pf);
119+ buffers.push_front(std::make_pair(id, new_buffer));
120+ }
121+ else
122+ {
123+ buffers.push_front(*existing_buffer_id_pair);
124+ buffers.erase(existing_buffer_id_pair);
125+ }
126
127- current_buffer_iter = buffers.insert(current_buffer_iter, std::make_pair(id, new_buffer));
128- }
129+ if (buffers.size() > max_buffers)
130+ buffers.pop_back();
131 }
132
133 std::shared_ptr<mcl::ClientBuffer> mcl::ClientBufferDepository::current_buffer()
134 {
135- return current_buffer_iter->second;
136+ return buffers.front().second;
137 }
138
139=== modified file 'src/client/client_buffer_depository.h'
140--- src/client/client_buffer_depository.h 2013-03-29 17:42:12 +0000
141+++ src/client/client_buffer_depository.h 2013-04-10 08:47:23 +0000
142@@ -20,7 +20,7 @@
143 #ifndef MIR_CLIENT_PRIVATE_MIR_CLIENT_BUFFER_DEPOSITORY_H_
144 #define MIR_CLIENT_PRIVATE_MIR_CLIENT_BUFFER_DEPOSITORY_H_
145
146-#include <map>
147+#include <list>
148 #include <memory>
149
150 #include "mir/geometry/pixel_format.h"
151@@ -39,9 +39,19 @@
152 /// The ClientBufferDepository is responsible for taking the IPC buffer data and converting it into
153 /// the more digestible form of a ClientBuffer. It maintains the client-side state necessary to
154 /// construct a ClientBuffer.
155+/// \sa mir::frontend::ClientBufferTracker for the server-side analogue.
156+/// \note Changes to the tracking algorithm will need associated server-side changes to mir::frontend::ClientBufferTracker
157 class ClientBufferDepository
158 {
159 public:
160+ /// Constructor
161+
162+ /// \param factory is the ClientBufferFactory that will be used to convert IPC data to ClientBuffers
163+ /// \param max_buffers is the number of buffers the depository will cache. After the client has received
164+ /// its initial buffers the ClientBufferDepository will always have the last \ref max_buffers buffers
165+ /// cached.
166+ /// \note The server relies on the depository caching \ref max_buffers buffers to optimise away buffer
167+ /// passing. As such, this number needs to be shared between the server and client libraries.
168 ClientBufferDepository(std::shared_ptr<ClientBufferFactory> const& factory, int max_buffers);
169
170 /// Construct a ClientBuffer from the IPC data, and use it as the current buffer.
171@@ -54,10 +64,8 @@
172
173 private:
174 std::shared_ptr<ClientBufferFactory> const factory;
175- typedef std::map<int, std::shared_ptr<ClientBuffer>> BufferMap;
176- BufferMap buffers;
177- BufferMap::iterator current_buffer_iter;
178- const unsigned max_age;
179+ std::list<std::pair<int, std::shared_ptr<ClientBuffer>>> buffers;
180+ unsigned int const max_buffers;
181 };
182 }
183 }
184
185=== modified file 'src/client/gbm/drm_fd_handler.h'
186--- src/client/gbm/drm_fd_handler.h 2013-03-13 04:54:15 +0000
187+++ src/client/gbm/drm_fd_handler.h 2013-04-10 08:47:23 +0000
188@@ -34,6 +34,8 @@
189 virtual ~DRMFDHandler() {}
190
191 virtual int ioctl(unsigned long request, void* arg) = 0;
192+ virtual int primeFDToHandle(int prime_fd, uint32_t *handle) = 0;
193+ virtual int close(int fd) = 0;
194 virtual void* map(size_t size, off_t offset) = 0;
195 virtual void unmap(void* addr, size_t size) = 0;
196
197
198=== modified file 'src/client/gbm/gbm_client_buffer.cpp'
199--- src/client/gbm/gbm_client_buffer.cpp 2013-03-29 11:27:00 +0000
200+++ src/client/gbm/gbm_client_buffer.cpp 2013-04-10 08:47:23 +0000
201@@ -40,30 +40,26 @@
202 struct GEMHandle
203 {
204 GEMHandle(std::shared_ptr<mclg::DRMFDHandler> const& drm_fd_handler,
205- uint32_t gem_name)
206+ int prime_fd)
207 : drm_fd_handler{drm_fd_handler}
208 {
209- struct drm_gem_open gem_open;
210- memset(&gem_open, 0, sizeof(gem_open));
211- gem_open.name = gem_name;
212-
213- int ret = drm_fd_handler->ioctl(DRM_IOCTL_GEM_OPEN, &gem_open);
214+ int ret = drm_fd_handler->primeFDToHandle(prime_fd, &handle);
215 if (ret)
216 {
217- std::string msg("Failed to open GEM handle for DRM buffer");
218+ std::string msg("Failed to import PRIME fd for DRM buffer");
219 BOOST_THROW_EXCEPTION(
220 boost::enable_error_info(
221 std::runtime_error(msg)) << boost::errinfo_errno(errno));
222 }
223-
224- handle = gem_open.handle;
225 }
226
227 ~GEMHandle()
228 {
229- static const uint32_t pad{0};
230- struct drm_gem_close gem_close = {handle, pad};
231- drm_fd_handler->ioctl(DRM_IOCTL_GEM_CLOSE, &gem_close);
232+ struct drm_gem_close arg;
233+ arg.handle = handle;
234+ // TODO (@raof): Error reporting? I do not believe it should be possible for this to fail,
235+ // so if it does we should probably flag it.
236+ drm_fd_handler->ioctl(DRM_IOCTL_GEM_CLOSE, &arg);
237 }
238
239 std::shared_ptr<mclg::DRMFDHandler> const drm_fd_handler;
240@@ -78,10 +74,10 @@
241 struct GBMMemoryRegion : mcl::MemoryRegion
242 {
243 GBMMemoryRegion(std::shared_ptr<mclg::DRMFDHandler> const& drm_fd_handler,
244- uint32_t gem_name, geom::Size const& size_param,
245+ int prime_fd, geom::Size const& size_param,
246 geom::Stride stride_param, geom::PixelFormat format_param)
247 : drm_fd_handler{drm_fd_handler},
248- gem_handle{drm_fd_handler, gem_name},
249+ gem_handle{drm_fd_handler, prime_fd},
250 size_in_bytes{size_param.height.as_uint32_t() * stride_param.as_uint32_t()}
251 {
252 width = size_param.width;
253@@ -137,12 +133,19 @@
254 {
255 }
256
257+mclg::GBMClientBuffer::~GBMClientBuffer()
258+{
259+ // TODO (@raof): Error reporting? It should not be possible for this to fail; if it does,
260+ // something's seriously wrong.
261+ drm_fd_handler->close(creation_package->fd[0]);
262+}
263+
264 std::shared_ptr<mcl::MemoryRegion> mclg::GBMClientBuffer::secure_for_cpu_write()
265 {
266- const uint32_t gem_name = creation_package->data[0];
267+ const int prime_fd = creation_package->fd[0];
268
269 return std::make_shared<GBMMemoryRegion>(drm_fd_handler,
270- gem_name,
271+ prime_fd,
272 size(),
273 stride(),
274 pixel_format());
275
276=== modified file 'src/client/gbm/gbm_client_buffer.h'
277--- src/client/gbm/gbm_client_buffer.h 2013-03-29 11:27:00 +0000
278+++ src/client/gbm/gbm_client_buffer.h 2013-04-10 08:47:23 +0000
279@@ -43,6 +43,8 @@
280 geometry::Size size,
281 geometry::PixelFormat pf);
282
283+ virtual ~GBMClientBuffer();
284+
285 std::shared_ptr<MemoryRegion> secure_for_cpu_write();
286 geometry::Size size() const;
287 geometry::Stride stride() const;
288
289=== modified file 'src/client/gbm/gbm_client_platform.cpp'
290--- src/client/gbm/gbm_client_platform.cpp 2013-03-29 11:27:00 +0000
291+++ src/client/gbm/gbm_client_platform.cpp 2013-04-10 08:47:23 +0000
292@@ -27,6 +27,7 @@
293
294 #include <xf86drm.h>
295 #include <sys/mman.h>
296+#include <unistd.h>
297
298 namespace mcl=mir::client;
299 namespace mclg=mir::client::gbm;
300@@ -47,6 +48,22 @@
301 return drmIoctl(drm_fd, request, arg);
302 }
303
304+ int primeFDToHandle(int prime_fd, uint32_t *handle)
305+ {
306+ return drmPrimeFDToHandle(drm_fd, prime_fd, handle);
307+ }
308+
309+ int close(int fd)
310+ {
311+ while (::close(fd) == -1)
312+ {
313+ // Retry on EINTR, return error on anything else
314+ if (errno != EINTR)
315+ return errno;
316+ }
317+ return 0;
318+ }
319+
320 void* map(size_t size, off_t offset)
321 {
322 return mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
323
324=== modified file 'src/client/mir_surface.cpp'
325--- src/client/mir_surface.cpp 2013-04-08 05:28:44 +0000
326+++ src/client/mir_surface.cpp 2013-04-10 08:47:23 +0000
327@@ -17,6 +17,7 @@
328 */
329
330 #include "mir_toolkit/mir_client_library.h"
331+#include "mir/frontend/client_constants.h"
332 #include "mir_logger.h"
333 #include "client_buffer.h"
334 #include "mir_surface.h"
335@@ -42,7 +43,7 @@
336 mir_surface_lifecycle_callback callback, void * context)
337 : server(server),
338 connection(allocating_connection),
339- buffer_depository(std::make_shared<mcl::ClientBufferDepository>(factory, 3)),
340+ buffer_depository(std::make_shared<mcl::ClientBufferDepository>(factory, mir::frontend::client_buffer_cache_size)),
341 input_platform(input_platform),
342 logger(logger)
343 {
344
345=== modified file 'src/server/frontend/CMakeLists.txt'
346--- src/server/frontend/CMakeLists.txt 2013-03-28 09:50:09 +0000
347+++ src/server/frontend/CMakeLists.txt 2013-04-10 08:47:23 +0000
348@@ -1,6 +1,7 @@
349 set(
350 FRONTEND_SOURCES
351
352+ client_buffer_tracker.cpp
353 session_mediator.cpp
354 null_session_mediator_report.cpp
355 null_message_processor_report.cpp
356
357=== added file 'src/server/frontend/client_buffer_tracker.cpp'
358--- src/server/frontend/client_buffer_tracker.cpp 1970-01-01 00:00:00 +0000
359+++ src/server/frontend/client_buffer_tracker.cpp 2013-04-10 08:47:23 +0000
360@@ -0,0 +1,53 @@
361+/*
362+ * Copyright © 2013 Canonical Ltd.
363+ *
364+ * This program is free software: you can redistribute it and/or modify
365+ * it under the terms of the GNU Lesser General Public License version 3 as
366+ * published by the Free Software Foundation.
367+ *
368+ * This program is distributed in the hope that it will be useful,
369+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
370+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
371+ * GNU General Public License for more details.
372+ *
373+ * You should have received a copy of the GNU Lesser General Public License
374+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
375+ *
376+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
377+ */
378+
379+#include <algorithm>
380+
381+#include "client_buffer_tracker.h"
382+#include "mir/compositor/buffer_id.h"
383+
384+namespace mf = mir::frontend;
385+namespace mc = mir::compositor;
386+
387+mf::ClientBufferTracker::ClientBufferTracker(unsigned int client_cache_size)
388+ : ids(),
389+ cache_size{client_cache_size}
390+{
391+}
392+
393+void mf::ClientBufferTracker::add(mc::BufferID const& id)
394+{
395+ auto existing_id = std::find(ids.begin(), ids.end(), id);
396+
397+ if (existing_id != ids.end())
398+ {
399+ ids.push_front(*existing_id);
400+ ids.erase(existing_id);
401+ }
402+ else
403+ {
404+ ids.push_front(id);
405+ }
406+ if (ids.size() > cache_size)
407+ ids.pop_back();
408+}
409+
410+bool mf::ClientBufferTracker::client_has(mc::BufferID const& id) const
411+{
412+ return std::find(ids.begin(), ids.end(), id) != ids.end();
413+}
414
415=== added file 'src/server/frontend/client_buffer_tracker.h'
416--- src/server/frontend/client_buffer_tracker.h 1970-01-01 00:00:00 +0000
417+++ src/server/frontend/client_buffer_tracker.h 2013-04-10 08:47:23 +0000
418@@ -0,0 +1,62 @@
419+/*
420+ * Copyright © 2013 Canonical Ltd.
421+ *
422+ * This program is free software: you can redistribute it and/or modify
423+ * it under the terms of the GNU Lesser General Public License version 3 as
424+ * published by the Free Software Foundation.
425+ *
426+ * This program is distributed in the hope that it will be useful,
427+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
428+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429+ * GNU General Public License for more details.
430+ *
431+ * You should have received a copy of the GNU Lesser General Public License
432+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
433+ *
434+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
435+ */
436+
437+ #ifndef MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_
438+ #define MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_
439+
440+#include <stdint.h>
441+#include <list>
442+
443+namespace mir
444+{
445+
446+namespace compositor
447+{
448+class BufferID;
449+}
450+
451+namespace frontend
452+{
453+
454+/// Responsible for tracking what buffers the client library knows about for a surface.
455+
456+/// \sa mir::client::ClientBufferDepository for the client-side of this tracking
457+/// \note Changes to the tracking algorithm of mir::client::ClientBufferDepository will need to be mirrored here
458+class ClientBufferTracker
459+{
460+public:
461+ ClientBufferTracker(unsigned int client_cache_size);
462+
463+ ClientBufferTracker(ClientBufferTracker const&) = delete;
464+ ClientBufferTracker& operator=(ClientBufferTracker const&) = delete;
465+
466+ /// Add a BufferID to the list of buffers known by the client.
467+ ///
468+ /// Typically this should be done just prior to or just after sending the buffer information
469+ void add(compositor::BufferID const& id);
470+ bool client_has(compositor::BufferID const& id) const;
471+private:
472+
473+ std::list<compositor::BufferID> ids;
474+ unsigned int const cache_size;
475+};
476+
477+}
478+}
479+
480+ #endif // MIR_FRONTEND_CLIENT_BUFFER_TRACKER_H_
481
482=== modified file 'src/server/frontend/session_mediator.cpp'
483--- src/server/frontend/session_mediator.cpp 2013-03-21 03:32:59 +0000
484+++ src/server/frontend/session_mediator.cpp 2013-04-10 08:47:23 +0000
485@@ -33,6 +33,8 @@
486 #include "mir/graphics/platform.h"
487 #include "mir/graphics/viewable_area.h"
488 #include "mir/graphics/platform_ipc_package.h"
489+#include "mir/frontend/client_constants.h"
490+#include "client_buffer_tracker.h"
491
492 #include <boost/throw_exception.hpp>
493
494@@ -48,7 +50,8 @@
495 viewable_area(viewable_area),
496 buffer_allocator(buffer_allocator),
497 report(report),
498- resource_cache(resource_cache)
499+ resource_cache(resource_cache),
500+ client_tracker(std::make_shared<ClientBufferTracker>(frontend::client_buffer_cache_size))
501 {
502 }
503
504@@ -122,20 +125,25 @@
505 auto const& buffer_resource = surface->client_buffer();
506
507 auto const& id = buffer_resource->id();
508- auto ipc_package = buffer_resource->get_ipc_package();
509+
510 auto buffer = response->mutable_buffer();
511-
512 buffer->set_buffer_id(id.as_uint32_t());
513
514- for (auto& data : ipc_package->ipc_data)
515- buffer->add_data(data);
516-
517- for (auto& ipc_fds : ipc_package->ipc_fds)
518- buffer->add_fd(ipc_fds);
519-
520- buffer->set_stride(ipc_package->stride);
521-
522- resource_cache->save_resource(response, ipc_package);
523+ if (!client_tracker->client_has(id))
524+ {
525+ auto ipc_package = buffer_resource->get_ipc_package();
526+
527+ for (auto& data : ipc_package->ipc_data)
528+ buffer->add_data(data);
529+
530+ for (auto& ipc_fds : ipc_package->ipc_fds)
531+ buffer->add_fd(ipc_fds);
532+
533+ buffer->set_stride(ipc_package->stride);
534+
535+ resource_cache->save_resource(response, ipc_package);
536+ }
537+ client_tracker->add(id);
538 }
539
540 done->Run();
541@@ -157,18 +165,23 @@
542 surface->advance_client_buffer();
543 auto const& buffer_resource = surface->client_buffer();
544 auto const& id = buffer_resource->id();
545- auto ipc_package = buffer_resource->get_ipc_package();
546-
547 response->set_buffer_id(id.as_uint32_t());
548- for (auto& data : ipc_package->ipc_data)
549- response->add_data(data);
550-
551- for (auto& ipc_fds : ipc_package->ipc_fds)
552- response->add_fd(ipc_fds);
553-
554- response->set_stride(ipc_package->stride);
555-
556- resource_cache->save_resource(response, ipc_package);
557+
558+ if (!client_tracker->client_has(id))
559+ {
560+ auto ipc_package = buffer_resource->get_ipc_package();
561+
562+ for (auto& data : ipc_package->ipc_data)
563+ response->add_data(data);
564+
565+ for (auto& ipc_fds : ipc_package->ipc_fds)
566+ response->add_fd(ipc_fds);
567+
568+ response->set_stride(ipc_package->stride);
569+
570+ resource_cache->save_resource(response, ipc_package);
571+ }
572+ client_tracker->add(id);
573 done->Run();
574 }
575
576
577=== modified file 'src/server/graphics/gbm/gbm_buffer.cpp'
578--- src/server/graphics/gbm/gbm_buffer.cpp 2013-03-13 04:54:15 +0000
579+++ src/server/graphics/gbm/gbm_buffer.cpp 2013-04-10 08:47:23 +0000
580@@ -22,6 +22,7 @@
581 #include "buffer_texture_binder.h"
582 #include "mir/compositor/buffer_ipc_package.h"
583
584+#include <fcntl.h>
585 #include <xf86drm.h>
586
587 #include <boost/exception/errinfo_errno.hpp>
588@@ -84,19 +85,16 @@
589 auto device = gbm_bo_get_device(gbm_handle.get());
590 auto gem_handle = gbm_bo_get_handle(gbm_handle.get()).u32;
591 auto drm_fd = gbm_device_get_fd(device);
592- struct drm_gem_flink flink;
593- flink.handle = gem_handle;
594-
595- auto ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_FLINK, &flink);
596+
597+ auto ret = drmPrimeHandleToFD(drm_fd, gem_handle, DRM_CLOEXEC, &prime_fd);
598+
599 if (ret)
600 {
601- std::string const msg("Failed to get GEM flink name from gbm bo");
602+ std::string const msg("Failed to get PRIME fd from gbm bo");
603 BOOST_THROW_EXCEPTION(
604 boost::enable_error_info(
605 std::runtime_error(msg)) << boost::errinfo_errno(errno));
606 }
607-
608- gem_flink_name = flink.name;
609 }
610
611 geom::Size mgg::GBMBuffer::size() const
612@@ -119,7 +117,7 @@
613 {
614 auto temp = std::make_shared<mc::BufferIPCPackage>();
615
616- temp->ipc_data.push_back(gem_flink_name);
617+ temp->ipc_fds.push_back(prime_fd);
618 temp->stride = stride().as_uint32_t();
619
620 return temp;
621
622=== modified file 'src/server/graphics/gbm/gbm_buffer.h'
623--- src/server/graphics/gbm/gbm_buffer.h 2013-03-13 04:54:15 +0000
624+++ src/server/graphics/gbm/gbm_buffer.h 2013-04-10 08:47:23 +0000
625@@ -60,7 +60,7 @@
626 private:
627 std::shared_ptr<gbm_bo> const gbm_handle;
628 std::unique_ptr<BufferTextureBinder> const texture_binder;
629- uint32_t gem_flink_name;
630+ int prime_fd;
631 };
632
633 }
634
635=== modified file 'tests/unit-tests/client/gbm/mock_drm_fd_handler.h'
636--- tests/unit-tests/client/gbm/mock_drm_fd_handler.h 2013-02-24 19:16:46 +0000
637+++ tests/unit-tests/client/gbm/mock_drm_fd_handler.h 2013-04-10 08:47:23 +0000
638@@ -34,6 +34,8 @@
639 {
640 public:
641 MOCK_METHOD2(ioctl, int(unsigned long request, void* arg));
642+ MOCK_METHOD2(primeFDToHandle, int(int prime_fd, uint32_t *handle));
643+ MOCK_METHOD1(close, int(int fd));
644 MOCK_METHOD2(map, void*(size_t size, off_t offset));
645 MOCK_METHOD2(unmap, void(void* addr, size_t size));
646 };
647
648=== modified file 'tests/unit-tests/client/gbm/test_gbm_client_buffer.cpp'
649--- tests/unit-tests/client/gbm/test_gbm_client_buffer.cpp 2013-03-24 23:33:49 +0000
650+++ tests/unit-tests/client/gbm/test_gbm_client_buffer.cpp 2013-04-10 08:47:23 +0000
651@@ -97,7 +97,7 @@
652 using namespace testing;
653 void *map_addr{reinterpret_cast<void*>(0xabcdef)};
654
655- EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_OPEN,_))
656+ EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
657 .WillOnce(Return(0));
658 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_MODE_MAP_DUMB,_))
659 .WillOnce(Return(0));
660@@ -106,7 +106,7 @@
661 EXPECT_CALL(*drm_fd_handler, unmap(_,_))
662 .Times(1);
663 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
664- .WillOnce(Return(0));
665+ .Times(1);
666
667 mclg::GBMClientBuffer buffer(drm_fd_handler, std::move(package), size, pf);
668
669@@ -118,12 +118,12 @@
670 ASSERT_EQ(pf, mem_region->format);
671 }
672
673-TEST_F(MirGBMBufferTest, secure_for_cpu_write_throws_on_gem_open_failure)
674+TEST_F(MirGBMBufferTest, secure_for_cpu_write_throws_on_prime_handle_failure)
675 {
676 using namespace testing;
677
678- EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_OPEN,_))
679- .WillOnce(Return(1));
680+ EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
681+ .WillOnce(Return(-1));
682 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_MODE_MAP_DUMB,_))
683 .Times(0);
684 EXPECT_CALL(*drm_fd_handler, map(_,_))
685@@ -144,7 +144,7 @@
686 {
687 using namespace testing;
688
689- EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_OPEN,_))
690+ EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
691 .WillOnce(Return(0));
692 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_MODE_MAP_DUMB,_))
693 .WillOnce(Return(1));
694@@ -153,7 +153,7 @@
695 EXPECT_CALL(*drm_fd_handler, unmap(_,_))
696 .Times(0);
697 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
698- .WillOnce(Return(0));
699+ .Times(1);
700
701 mclg::GBMClientBuffer buffer(drm_fd_handler, std::move(package), size, pf);
702
703@@ -166,7 +166,7 @@
704 {
705 using namespace testing;
706
707- EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_OPEN,_))
708+ EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
709 .WillOnce(Return(0));
710 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_MODE_MAP_DUMB,_))
711 .WillOnce(Return(0));
712@@ -175,7 +175,7 @@
713 EXPECT_CALL(*drm_fd_handler, unmap(_,_))
714 .Times(0);
715 EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
716- .WillOnce(Return(0));
717+ .Times(1);
718
719 mclg::GBMClientBuffer buffer(drm_fd_handler, std::move(package), size, pf);
720
721@@ -183,3 +183,32 @@
722 auto mem_region = buffer.secure_for_cpu_write();
723 }, std::runtime_error);
724 }
725+
726+TEST_F(MirGBMBufferTest, prime_fd_closed_on_buffer_destruction)
727+{
728+ using namespace testing;
729+
730+ int const prime_fd{42};
731+
732+ package->fd[0] = prime_fd;
733+ package->fd_items = 1;
734+
735+ EXPECT_CALL(*drm_fd_handler, close(prime_fd))
736+ .Times(1);
737+
738+ mclg::GBMClientBuffer buffer(drm_fd_handler, package, size, pf);
739+}
740+
741+TEST_F(MirGBMBufferTest, buffer_does_not_take_a_gem_reference_when_not_mapping)
742+{
743+ using namespace testing;
744+
745+ // We don't map the buffer, so we don't need to take a GEM reference...
746+ EXPECT_CALL(*drm_fd_handler, primeFDToHandle(_,_))
747+ .Times(0);
748+ // We haven't taken a GEM reference, so we shouldn't close it.
749+ EXPECT_CALL(*drm_fd_handler, ioctl(DRM_IOCTL_GEM_CLOSE,_))
750+ .Times(0);
751+
752+ mclg::GBMClientBuffer buffer(drm_fd_handler, package, size, pf);
753+}
754
755=== modified file 'tests/unit-tests/client/test_client_buffer_depository.cpp'
756--- tests/unit-tests/client/test_client_buffer_depository.cpp 2013-03-29 16:51:35 +0000
757+++ tests/unit-tests/client/test_client_buffer_depository.cpp 2013-04-10 08:47:23 +0000
758@@ -369,3 +369,36 @@
759 depository.deposit_package(package1, 8, size, pf);
760 depository.deposit_package(package2, 9, size, pf);
761 }
762+
763+TEST_F(MirBufferDepositoryTest, depository_keeps_last_2_buffers_regardless_of_age)
764+{
765+ using namespace testing;
766+
767+ mcl::ClientBufferDepository depository{mock_factory, 2};
768+
769+ EXPECT_CALL(*mock_factory, create_buffer(_,_,_)).Times(2);
770+
771+ depository.deposit_package(package, 8, size, pf);
772+ depository.deposit_package(package, 9, size, pf);
773+ depository.deposit_package(package, 9, size, pf);
774+ depository.deposit_package(package, 9, size, pf);
775+ depository.deposit_package(package, 8, size, pf);
776+}
777+
778+TEST_F(MirBufferDepositoryTest, depository_keeps_last_3_buffers_regardless_of_age)
779+{
780+ using namespace testing;
781+
782+ mcl::ClientBufferDepository depository{mock_factory, 3};
783+
784+ EXPECT_CALL(*mock_factory, create_buffer(_,_,_)).Times(3);
785+
786+ depository.deposit_package(package, 8, size, pf);
787+ depository.deposit_package(package, 9, size, pf);
788+ depository.deposit_package(package, 10, size, pf);
789+ depository.deposit_package(package, 9, size, pf);
790+ depository.deposit_package(package, 10, size, pf);
791+ depository.deposit_package(package, 9, size, pf);
792+ depository.deposit_package(package, 10, size, pf);
793+ depository.deposit_package(package, 8, size, pf);
794+}
795
796=== modified file 'tests/unit-tests/frontend/CMakeLists.txt'
797--- tests/unit-tests/frontend/CMakeLists.txt 2013-04-09 11:33:19 +0000
798+++ tests/unit-tests/frontend/CMakeLists.txt 2013-04-10 08:47:23 +0000
799@@ -1,4 +1,5 @@
800 list(APPEND UNIT_TEST_SOURCES
801+ ${CMAKE_CURRENT_SOURCE_DIR}/test_client_buffer_tracker.cpp
802 ${CMAKE_CURRENT_SOURCE_DIR}/stress_protobuf_communicator.cpp
803 ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_communicator.cpp
804 ${CMAKE_CURRENT_SOURCE_DIR}/test_protobuf_surface_apis.cpp
805
806=== added file 'tests/unit-tests/frontend/test_client_buffer_tracker.cpp'
807--- tests/unit-tests/frontend/test_client_buffer_tracker.cpp 1970-01-01 00:00:00 +0000
808+++ tests/unit-tests/frontend/test_client_buffer_tracker.cpp 2013-04-10 08:47:23 +0000
809@@ -0,0 +1,108 @@
810+/*
811+ * Copyright © 2013 Canonical Ltd.
812+ *
813+ * This program is free software: you can redistribute it and/or modify
814+ * it under the terms of the GNU Lesser General Public License version 3 as
815+ * published by the Free Software Foundation.
816+ *
817+ * This program is distributed in the hope that it will be useful,
818+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
819+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
820+ * GNU General Public License for more details.
821+ *
822+ * You should have received a copy of the GNU Lesser General Public License
823+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
824+ *
825+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
826+ */
827+
828+#include "../../src/server/frontend/client_buffer_tracker.h"
829+#include "mir/compositor/buffer_id.h"
830+
831+#include <gtest/gtest.h>
832+
833+namespace mf = mir::frontend;
834+namespace mc = mir::compositor;
835+
836+TEST(ClientBufferTracker, just_added_buffer_is_known_by_client)
837+{
838+ mf::ClientBufferTracker tracker(3);
839+ mc::BufferID const id{5};
840+
841+ tracker.add(id);
842+ EXPECT_TRUE(tracker.client_has(id));
843+}
844+
845+TEST(ClientBufferTracker, unadded_buffer_is_unknown_by_client)
846+{
847+ mf::ClientBufferTracker tracker(3);
848+
849+ tracker.add(mc::BufferID{5});
850+ EXPECT_FALSE(tracker.client_has(mc::BufferID{6}));
851+}
852+
853+TEST(ClientBufferTracker, tracks_sequence_of_buffers)
854+{
855+ mf::ClientBufferTracker tracker(3);
856+ mc::BufferID const one{1};
857+ mc::BufferID const two{2};
858+ mc::BufferID const three{3};
859+ mc::BufferID const four{4};
860+
861+ tracker.add(one);
862+ tracker.add(two);
863+ tracker.add(three);
864+
865+ EXPECT_TRUE(tracker.client_has(one));
866+ EXPECT_TRUE(tracker.client_has(two));
867+ EXPECT_TRUE(tracker.client_has(three));
868+ EXPECT_FALSE(tracker.client_has(four));
869+}
870+
871+TEST(ClientBufferTracker, old_buffers_expire_from_tracker)
872+{
873+ mf::ClientBufferTracker tracker(3);
874+
875+ mc::BufferID const one{1};
876+ mc::BufferID const two{2};
877+ mc::BufferID const three{3};
878+ mc::BufferID const four{4};
879+
880+ tracker.add(one);
881+ tracker.add(two);
882+ tracker.add(three);
883+
884+ ASSERT_TRUE(tracker.client_has(one));
885+ ASSERT_TRUE(tracker.client_has(two));
886+ ASSERT_TRUE(tracker.client_has(three));
887+
888+ tracker.add(two);
889+ tracker.add(three);
890+ tracker.add(four);
891+
892+ EXPECT_FALSE(tracker.client_has(one));
893+ EXPECT_TRUE(tracker.client_has(two));
894+ EXPECT_TRUE(tracker.client_has(three));
895+ EXPECT_TRUE(tracker.client_has(four));
896+}
897+
898+TEST(ClientBufferTracker, tracks_correct_number_of_buffers)
899+{
900+ mc::BufferID ids[10];
901+ for (unsigned int i = 0; i < 10; ++i)
902+ ids[i] = mc::BufferID{i};
903+
904+ for (unsigned int tracker_size = 2; tracker_size < 10; ++tracker_size)
905+ {
906+ mf::ClientBufferTracker tracker{tracker_size};
907+
908+ for (unsigned int i = 0; i < tracker_size; ++i)
909+ tracker.add(ids[i]);
910+
911+ tracker.add(ids[tracker_size]);
912+
913+ EXPECT_FALSE(tracker.client_has(ids[0]));
914+ for (unsigned int i = 1; i <= tracker_size; ++i)
915+ EXPECT_TRUE(tracker.client_has(ids[i]));
916+ }
917+}
918
919=== modified file 'tests/unit-tests/frontend/test_session_mediator.cpp'
920--- tests/unit-tests/frontend/test_session_mediator.cpp 2013-03-29 16:51:35 +0000
921+++ tests/unit-tests/frontend/test_session_mediator.cpp 2013-04-10 08:47:23 +0000
922@@ -30,7 +30,7 @@
923 #include "mir_test_doubles/null_display.h"
924 #include "mir_test_doubles/mock_shell.h"
925 #include "mir_test_doubles/mock_surface.h"
926-#include "mir_test_doubles/stub_buffer.h"
927+#include "mir_test_doubles/mock_buffer.h"
928 #include "mir_test_doubles/stub_session.h"
929 #include "mir_test_doubles/stub_surface_builder.h"
930 #include "mir_test/fake_shared.h"
931@@ -63,10 +63,11 @@
932 using namespace ::testing;
933
934 mock_surface = std::make_shared<mtd::MockSurface>(mt::fake_shared(surface_builder));
935+ mock_buffer = std::make_shared<NiceMock<mtd::MockBuffer>>(geom::Size(), geom::Stride(), geom::PixelFormat());
936
937 EXPECT_CALL(*mock_surface, size()).Times(AnyNumber()).WillRepeatedly(Return(geom::Size()));
938 EXPECT_CALL(*mock_surface, pixel_format()).Times(AnyNumber()).WillRepeatedly(Return(geom::PixelFormat()));
939- EXPECT_CALL(*mock_surface, client_buffer()).Times(AnyNumber()).WillRepeatedly(Return(mt::fake_shared(stub_buffer)));
940+ EXPECT_CALL(*mock_surface, client_buffer()).Times(AnyNumber()).WillRepeatedly(Return(mock_buffer));
941 EXPECT_CALL(*mock_surface, advance_client_buffer()).Times(AnyNumber());
942
943 EXPECT_CALL(*mock_surface, supports_input()).Times(AnyNumber()).WillRepeatedly(Return(true));
944@@ -80,7 +81,7 @@
945
946 mtd::StubSurfaceBuilder surface_builder;
947 std::shared_ptr<mtd::MockSurface> mock_surface;
948- mtd::StubBuffer stub_buffer;
949+ std::shared_ptr<mtd::MockBuffer> mock_buffer;
950 static int const testing_client_input_fd;
951 };
952
953@@ -354,3 +355,68 @@
954
955 mediator.disconnect(nullptr, nullptr, nullptr, null_callback.get());
956 }
957+
958+TEST_F(SessionMediatorTest, session_only_sends_needed_buffers)
959+{
960+ using namespace testing;
961+
962+ mp::ConnectParameters connect_parameters;
963+ mp::Connection connection;
964+
965+ mediator.connect(nullptr, &connect_parameters, &connection, null_callback.get());
966+
967+ {
968+ auto package = std::make_shared<mc::BufferIPCPackage>();
969+ package->ipc_data = std::vector<int32_t>{5, 23, 1};
970+ package->ipc_fds = std::vector<int32_t>{2, 1};
971+
972+ ON_CALL(*stubbed_session->mock_buffer, get_ipc_package())
973+ .WillByDefault(Return(package));
974+
975+ EXPECT_CALL(*stubbed_session->mock_buffer, id())
976+ .WillOnce(Return(mc::BufferID{4}))
977+ .WillOnce(Return(mc::BufferID{5}))
978+ .WillOnce(Return(mc::BufferID{4}))
979+ .WillOnce(Return(mc::BufferID{5}));
980+
981+ EXPECT_CALL(*stubbed_session->mock_buffer, get_ipc_package())
982+ .Times(2);
983+
984+ mp::SurfaceParameters surface_request;
985+ mp::Surface surface_response;
986+ mp::SurfaceId buffer_request;
987+ mp::Buffer buffer_response[3];
988+
989+ mediator.create_surface(nullptr, &surface_request, &surface_response, null_callback.get());
990+ ASSERT_EQ(mc::BufferID{4}.as_uint32_t(), static_cast<uint32_t>(surface_response.buffer().buffer_id()));
991+ EXPECT_EQ(2, surface_response.buffer().fd_size());
992+ EXPECT_EQ(3, surface_response.buffer().data_size());
993+ for (int i = 0; i < surface_response.buffer().fd_size(); ++i)
994+ EXPECT_EQ(package->ipc_fds[i], surface_response.buffer().fd(i));
995+ for (int i = 0; i < surface_response.buffer().data_size(); ++i)
996+ EXPECT_EQ(package->ipc_data[i], surface_response.buffer().data(i));
997+
998+
999+ mediator.next_buffer(nullptr, &buffer_request, &buffer_response[0], null_callback.get());
1000+ ASSERT_EQ(mc::BufferID{5}.as_uint32_t(), static_cast<uint32_t>(buffer_response[0].buffer_id()));
1001+ EXPECT_EQ(2, buffer_response[0].fd_size());
1002+ EXPECT_EQ(3, buffer_response[0].data_size());
1003+ for (int i = 0; i < buffer_response[0].fd_size(); ++i)
1004+ EXPECT_EQ(package->ipc_fds[i], buffer_response[0].fd(i));
1005+ for (int i = 0; i < buffer_response[0].data_size(); ++i)
1006+ EXPECT_EQ(package->ipc_data[i], buffer_response[0].data(i));
1007+
1008+ mediator.next_buffer(nullptr, &buffer_request, &buffer_response[1], null_callback.get());
1009+ ASSERT_EQ(mc::BufferID{4}.as_uint32_t(), static_cast<uint32_t>(buffer_response[1].buffer_id()));
1010+ EXPECT_EQ(0, buffer_response[1].fd_size());
1011+ EXPECT_EQ(0, buffer_response[1].data_size());
1012+
1013+ mediator.next_buffer(nullptr, &buffer_request, &buffer_response[2], null_callback.get());
1014+
1015+ ASSERT_EQ(mc::BufferID{5}.as_uint32_t(), static_cast<uint32_t>(buffer_response[2].buffer_id()));
1016+ EXPECT_EQ(0, buffer_response[2].fd_size());
1017+ EXPECT_EQ(0, buffer_response[2].data_size());
1018+ }
1019+
1020+ mediator.disconnect(nullptr, nullptr, nullptr, null_callback.get());
1021+}
1022
1023=== modified file 'tests/unit-tests/graphics/gbm/mock_drm.cpp'
1024--- tests/unit-tests/graphics/gbm/mock_drm.cpp 2013-02-05 16:18:10 +0000
1025+++ tests/unit-tests/graphics/gbm/mock_drm.cpp 2013-04-10 08:47:23 +0000
1026@@ -341,3 +341,13 @@
1027 {
1028 return global_mock->drmAuthMagic(fd, magic);
1029 }
1030+
1031+int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd)
1032+{
1033+ return global_mock->drmPrimeHandleToFD(fd, handle, flags, prime_fd);
1034+}
1035+
1036+int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle)
1037+{
1038+ return global_mock->drmPrimeFDToHandle(fd, prime_fd, handle);
1039+}
1040
1041=== modified file 'tests/unit-tests/graphics/gbm/mock_drm.h'
1042--- tests/unit-tests/graphics/gbm/mock_drm.h 2013-02-05 16:18:10 +0000
1043+++ tests/unit-tests/graphics/gbm/mock_drm.h 2013-04-10 08:47:23 +0000
1044@@ -114,6 +114,9 @@
1045 MOCK_METHOD2(drmGetMagic, int(int fd, drm_magic_t *magic));
1046 MOCK_METHOD2(drmAuthMagic, int(int fd, drm_magic_t magic));
1047
1048+ MOCK_METHOD4(drmPrimeHandleToFD, int(int fd, uint32_t handle, uint32_t flags, int *prime_fd));
1049+ MOCK_METHOD3(drmPrimeFDToHandle, int(int fd, int prime_fd, uint32_t *handle));
1050+
1051 FakeDRMResources fake_drm;
1052 };
1053
1054
1055=== modified file 'tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp'
1056--- tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp 2013-03-13 08:09:52 +0000
1057+++ tests/unit-tests/graphics/gbm/test_gbm_buffer.cpp 2013-04-10 08:47:23 +0000
1058@@ -142,8 +142,8 @@
1059
1060 auto buffer = allocator->alloc_buffer(buffer_properties);
1061 auto ipc_package = buffer->get_ipc_package();
1062- ASSERT_TRUE(ipc_package->ipc_fds.empty());
1063- ASSERT_EQ(size_t(1), ipc_package->ipc_data.size());
1064+ ASSERT_EQ(size_t(1), ipc_package->ipc_fds.size());
1065+ ASSERT_TRUE(ipc_package->ipc_data.empty());
1066 }
1067
1068 MATCHER_P(GEMFlinkHandleIs, value, "")
1069@@ -162,7 +162,7 @@
1070 {
1071 using namespace testing;
1072
1073- uint32_t gem_flink_name{0x77};
1074+ uint32_t prime_fd{0x77};
1075 gbm_bo_handle mock_handle;
1076 mock_handle.u32 = 0xdeadbeef;
1077
1078@@ -170,23 +170,23 @@
1079 .Times(Exactly(1))
1080 .WillOnce(Return(mock_handle));
1081
1082- EXPECT_CALL(mock_drm, drmIoctl(_,DRM_IOCTL_GEM_FLINK, GEMFlinkHandleIs(mock_handle.u32)))
1083+ EXPECT_CALL(mock_drm, drmPrimeHandleToFD(_,mock_handle.u32,_,_))
1084 .Times(Exactly(1))
1085- .WillOnce(DoAll(SetGEMFlinkName(gem_flink_name), Return(0)));
1086+ .WillOnce(DoAll(SetArgPointee<3>(prime_fd), Return(0)));
1087
1088 EXPECT_NO_THROW({
1089 auto buffer = allocator->alloc_buffer(buffer_properties);
1090 auto ipc_package = buffer->get_ipc_package();
1091- ASSERT_EQ(gem_flink_name, static_cast<uint32_t>(ipc_package->ipc_data[0]));
1092+ ASSERT_EQ(prime_fd, static_cast<uint32_t>(ipc_package->ipc_fds[0]));
1093 ASSERT_EQ(stride.as_uint32_t(), static_cast<uint32_t>(ipc_package->stride));
1094 });
1095 }
1096
1097-TEST_F(GBMGraphicBufferBasic, buffer_ipc_package_throws_on_gem_flink_failure)
1098+TEST_F(GBMGraphicBufferBasic, buffer_ipc_package_throws_on_prime_fd_failure)
1099 {
1100 using namespace testing;
1101
1102- EXPECT_CALL(mock_drm, drmIoctl(_,DRM_IOCTL_GEM_FLINK,_))
1103+ EXPECT_CALL(mock_drm, drmPrimeHandleToFD(_,_,_,_))
1104 .Times(Exactly(1))
1105 .WillOnce(Return(-1));
1106

Subscribers

People subscribed via source and target branches