Merge lp:~raof/mir/use-dma-buf into lp:~mir-team/mir/trunk
- use-dma-buf
- Merge into trunk
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 |
Related bugs: |
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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:475
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
Changed to Work In Progress due to raof's comment above.
Chris Halse Rogers (raof) wrote : | # |
Should now be good to go. The mesa changes are ready for when this lands.
Chris Halse Rogers (raof) wrote : | # |
I'm Chris Halse Rogers, and I approve this MP
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:488
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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:/
Alexandros Frantzis (afrantzis) wrote : | # |
> I'm Chris Halse Rogers, and I approve this MP
That's cheating ;)
Alexandros Frantzis (afrantzis) wrote : | # |
src/server/
src/server/
... 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.
Robert Carr (robertcarr) wrote : | # |
Cool!
Lots of tabs. i.e. 284.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:497
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
479 + std::list<
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_
747 + .Times(0);
748 + // We haven't taken a GEM reference, so we shouldn't close it.
749 + EXPECT_
750 + .Times(0);
Do these add any value to the prime_fd_
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.
1005 +
1006 + mediator.
It's also worth testing that the mediator sends the expected buffer data the first time around.
Alan Griffiths (alan-griffiths) wrote : | # |
22 === added file 'include/
...
52 +/// mir::client:
53 +unsigned int const client_
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.
Chris Halse Rogers (raof) wrote : | # |
--- Alexandros ---
> 479 + std::list<
> 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_
> 747 + .Times(0);
> 748 + // We haven't taken a GEM reference, so we shouldn't close it.
> 749 + EXPECT_
> 750 + .Times(0);
> Do these add any value to the prime_fd_
Oh, no. I forgot to delete that bit when I decided to make buffer_
> 1004 + mediator.
> 1005 +
> 1006 + mediator.
> 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:501
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
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/->/./)
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.
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)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:501
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:502
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:503
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Chris Halse Rogers (raof) wrote : | # |
Marked as approved, as the mesa changes for radeon and nouveau are now available.
Preview Diff
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 |
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.