diff options
author | Mike Blumenkrantz <zmike@osg.samsung.com> | 2017-06-30 14:59:21 -0400 |
---|---|---|
committer | Mike Blumenkrantz <zmike@osg.samsung.com> | 2017-06-30 14:59:55 -0400 |
commit | c2fde93c9ef1108c0809a538cf2ec482ed8369a9 (patch) | |
tree | d9879e4ebea4d25cda1a6cc1268461ad0d669c3b /src/lib/efl_wl/dmabuf.c | |
parent | 3775a9645da7e92599babccfe8454304cec367b7 (diff) |
efl_wl: a multiseat wayland compositor in an evas smart object
build when wayland support is enabled and provide two test/demo cases
beta api
@feature
Reviewed-By: Cedric BAIL <cedric@osg.samsung.com>
Diffstat (limited to 'src/lib/efl_wl/dmabuf.c')
-rw-r--r-- | src/lib/efl_wl/dmabuf.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/src/lib/efl_wl/dmabuf.c b/src/lib/efl_wl/dmabuf.c new file mode 100644 index 0000000000..a451179d53 --- /dev/null +++ b/src/lib/efl_wl/dmabuf.c | |||
@@ -0,0 +1,513 @@ | |||
1 | /* Shamelessly stolen from weston and modified, original license boiler plate | ||
2 | * follows. | ||
3 | */ | ||
4 | |||
5 | #if defined(__clang__) | ||
6 | # pragma clang diagnostic ignored "-Wunused-parameter" | ||
7 | #elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 | ||
8 | # pragma GCC diagnostic ignored "-Wunused-parameter" | ||
9 | #endif | ||
10 | |||
11 | |||
12 | /* | ||
13 | * Copyright © 2014, 2015 Collabora, Ltd. | ||
14 | * | ||
15 | * Permission to use, copy, modify, distribute, and sell this software and | ||
16 | * its documentation for any purpose is hereby granted without fee, provided | ||
17 | * that the above copyright notice appear in all copies and that both that | ||
18 | * copyright notice and this permission notice appear in supporting | ||
19 | * documentation, and that the name of the copyright holders not be used in | ||
20 | * advertising or publicity pertaining to distribution of the software | ||
21 | * without specific, written prior permission. The copyright holders make | ||
22 | * no representations about the suitability of this software for any | ||
23 | * purpose. It is provided "as is" without express or implied warranty. | ||
24 | * | ||
25 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||
26 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
27 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
28 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | ||
29 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | ||
30 | * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
31 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | #include <sys/mman.h> | ||
35 | |||
36 | #include <assert.h> | ||
37 | |||
38 | #include "linux-dmabuf-unstable-v1-server-protocol.h" | ||
39 | #include <stdlib.h> | ||
40 | #include <stdio.h> | ||
41 | #include <unistd.h> | ||
42 | #include "dmabuf.h" | ||
43 | #include <Eina.h> | ||
44 | |||
45 | __attribute__ ((visibility("hidden"))) Eina_Bool comp_dmabuf_test(struct linux_dmabuf_buffer *dmabuf); | ||
46 | |||
47 | static void | ||
48 | linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer) | ||
49 | { | ||
50 | int i; | ||
51 | |||
52 | for (i = 0; i < buffer->attributes.n_planes; i++) { | ||
53 | close(buffer->attributes.fd[i]); | ||
54 | buffer->attributes.fd[i] = -1; | ||
55 | } | ||
56 | |||
57 | buffer->attributes.n_planes = 0; | ||
58 | |||
59 | free(buffer); | ||
60 | } | ||
61 | |||
62 | static void | ||
63 | destroy_params(struct wl_resource *params_resource) | ||
64 | { | ||
65 | struct linux_dmabuf_buffer *buffer; | ||
66 | |||
67 | buffer = wl_resource_get_user_data(params_resource); | ||
68 | |||
69 | if (!buffer) | ||
70 | return; | ||
71 | |||
72 | linux_dmabuf_buffer_destroy(buffer); | ||
73 | } | ||
74 | |||
75 | static void | ||
76 | params_destroy(struct wl_client *client, struct wl_resource *resource) | ||
77 | { | ||
78 | wl_resource_destroy(resource); | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | params_add(struct wl_client *client, | ||
83 | struct wl_resource *params_resource, | ||
84 | int32_t name_fd, | ||
85 | uint32_t plane_idx, | ||
86 | uint32_t offset, | ||
87 | uint32_t stride, | ||
88 | uint32_t modifier_hi, | ||
89 | uint32_t modifier_lo) | ||
90 | { | ||
91 | struct linux_dmabuf_buffer *buffer; | ||
92 | |||
93 | buffer = wl_resource_get_user_data(params_resource); | ||
94 | if (!buffer) { | ||
95 | wl_resource_post_error(params_resource, | ||
96 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, | ||
97 | "params was already used to create a wl_buffer"); | ||
98 | close(name_fd); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | assert(buffer->params_resource == params_resource); | ||
103 | assert(!buffer->buffer_resource); | ||
104 | |||
105 | if (plane_idx >= MAX_DMABUF_PLANES) { | ||
106 | wl_resource_post_error(params_resource, | ||
107 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, | ||
108 | "plane index %u is too high", plane_idx); | ||
109 | close(name_fd); | ||
110 | return; | ||
111 | } | ||
112 | |||
113 | if (buffer->attributes.fd[plane_idx] != -1) { | ||
114 | wl_resource_post_error(params_resource, | ||
115 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, | ||
116 | "a dmabuf has already been added for plane %u", | ||
117 | plane_idx); | ||
118 | close(name_fd); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | buffer->attributes.fd[plane_idx] = name_fd; | ||
123 | buffer->attributes.offset[plane_idx] = offset; | ||
124 | buffer->attributes.stride[plane_idx] = stride; | ||
125 | buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) | | ||
126 | modifier_lo; | ||
127 | buffer->attributes.n_planes++; | ||
128 | } | ||
129 | |||
130 | static void | ||
131 | linux_dmabuf_wl_buffer_destroy(struct wl_client *client, | ||
132 | struct wl_resource *resource) | ||
133 | { | ||
134 | wl_resource_destroy(resource); | ||
135 | } | ||
136 | |||
137 | static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = { | ||
138 | linux_dmabuf_wl_buffer_destroy | ||
139 | }; | ||
140 | |||
141 | static void | ||
142 | destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource) | ||
143 | { | ||
144 | struct linux_dmabuf_buffer *buffer; | ||
145 | |||
146 | buffer = wl_resource_get_user_data(resource); | ||
147 | assert(buffer->buffer_resource == resource); | ||
148 | assert(!buffer->params_resource); | ||
149 | |||
150 | if (buffer->user_data_destroy_func) | ||
151 | buffer->user_data_destroy_func(buffer); | ||
152 | |||
153 | linux_dmabuf_buffer_destroy(buffer); | ||
154 | } | ||
155 | |||
156 | static void | ||
157 | params_create(struct wl_client *client, | ||
158 | struct wl_resource *params_resource, | ||
159 | int32_t width, | ||
160 | int32_t height, | ||
161 | uint32_t format, | ||
162 | uint32_t flags) | ||
163 | { | ||
164 | struct linux_dmabuf_buffer *buffer; | ||
165 | int i; | ||
166 | |||
167 | buffer = wl_resource_get_user_data(params_resource); | ||
168 | |||
169 | if (!buffer) { | ||
170 | wl_resource_post_error(params_resource, | ||
171 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, | ||
172 | "params was already used to create a wl_buffer"); | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | assert(buffer->params_resource == params_resource); | ||
177 | assert(!buffer->buffer_resource); | ||
178 | |||
179 | /* Switch the linux_dmabuf_buffer object from params resource to | ||
180 | * eventually wl_buffer resource. | ||
181 | */ | ||
182 | wl_resource_set_user_data(buffer->params_resource, NULL); | ||
183 | buffer->params_resource = NULL; | ||
184 | |||
185 | if (!buffer->attributes.n_planes) { | ||
186 | wl_resource_post_error(params_resource, | ||
187 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, | ||
188 | "no dmabuf has been added to the params"); | ||
189 | goto err_out; | ||
190 | } | ||
191 | |||
192 | /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */ | ||
193 | for (i = 0; i < buffer->attributes.n_planes; i++) { | ||
194 | if (buffer->attributes.fd[i] == -1) { | ||
195 | wl_resource_post_error(params_resource, | ||
196 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, | ||
197 | "no dmabuf has been added for plane %i", i); | ||
198 | goto err_out; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | buffer->attributes.version = 1; | ||
203 | buffer->attributes.width = width; | ||
204 | buffer->attributes.height = height; | ||
205 | buffer->attributes.format = format; | ||
206 | buffer->attributes.flags = flags; | ||
207 | |||
208 | if (width < 1 || height < 1) { | ||
209 | wl_resource_post_error(params_resource, | ||
210 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, | ||
211 | "invalid width %d or height %d", width, height); | ||
212 | goto err_out; | ||
213 | } | ||
214 | |||
215 | for (i = 0; i < buffer->attributes.n_planes; i++) { | ||
216 | off_t size; | ||
217 | |||
218 | if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) { | ||
219 | wl_resource_post_error(params_resource, | ||
220 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, | ||
221 | "size overflow for plane %i", i); | ||
222 | goto err_out; | ||
223 | } | ||
224 | |||
225 | if (i == 0 && | ||
226 | (uint64_t) buffer->attributes.offset[i] + | ||
227 | (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) { | ||
228 | wl_resource_post_error(params_resource, | ||
229 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, | ||
230 | "size overflow for plane %i", i); | ||
231 | goto err_out; | ||
232 | } | ||
233 | |||
234 | /* Don't report an error as it might be caused | ||
235 | * by the kernel not supporting seeking on dmabuf */ | ||
236 | size = lseek(buffer->attributes.fd[i], 0, SEEK_END); | ||
237 | if (size == -1) | ||
238 | continue; | ||
239 | |||
240 | if (buffer->attributes.offset[i] >= size) { | ||
241 | wl_resource_post_error(params_resource, | ||
242 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, | ||
243 | "invalid offset %i for plane %i", | ||
244 | buffer->attributes.offset[i], i); | ||
245 | goto err_out; | ||
246 | } | ||
247 | |||
248 | if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) { | ||
249 | wl_resource_post_error(params_resource, | ||
250 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, | ||
251 | "invalid stride %i for plane %i", | ||
252 | buffer->attributes.stride[i], i); | ||
253 | goto err_out; | ||
254 | } | ||
255 | |||
256 | /* Only valid for first plane as other planes might be | ||
257 | * sub-sampled according to fourcc format */ | ||
258 | if (i == 0 && | ||
259 | buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) { | ||
260 | wl_resource_post_error(params_resource, | ||
261 | ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, | ||
262 | "invalid buffer stride or height for plane %i", i); | ||
263 | goto err_out; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /* XXX: Some additional sanity checks could be done with respect | ||
268 | * to the fourcc format. A centralized collection (kernel or | ||
269 | * libdrm) would be useful to avoid code duplication for these | ||
270 | * checks (e.g. drm_format_num_planes). | ||
271 | */ | ||
272 | |||
273 | if (!comp_dmabuf_test(buffer)) | ||
274 | goto err_failed; | ||
275 | |||
276 | buffer->buffer_resource = wl_resource_create(client, | ||
277 | &wl_buffer_interface, | ||
278 | 1, 0); | ||
279 | if (!buffer->buffer_resource) { | ||
280 | wl_resource_post_no_memory(params_resource); | ||
281 | goto err_buffer; | ||
282 | } | ||
283 | |||
284 | wl_resource_set_implementation(buffer->buffer_resource, | ||
285 | &linux_dmabuf_buffer_implementation, | ||
286 | buffer, destroy_linux_dmabuf_wl_buffer); | ||
287 | |||
288 | zwp_linux_buffer_params_v1_send_created(params_resource, | ||
289 | buffer->buffer_resource); | ||
290 | |||
291 | return; | ||
292 | |||
293 | err_buffer: | ||
294 | if (buffer->user_data_destroy_func) | ||
295 | buffer->user_data_destroy_func(buffer); | ||
296 | |||
297 | err_failed: | ||
298 | zwp_linux_buffer_params_v1_send_failed(params_resource); | ||
299 | |||
300 | err_out: | ||
301 | linux_dmabuf_buffer_destroy(buffer); | ||
302 | } | ||
303 | |||
304 | static const struct zwp_linux_buffer_params_v1_interface | ||
305 | zwp_linux_buffer_params_implementation = { | ||
306 | params_destroy, | ||
307 | params_add, | ||
308 | params_create | ||
309 | }; | ||
310 | |||
311 | static void | ||
312 | linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) | ||
313 | { | ||
314 | wl_resource_destroy(resource); | ||
315 | } | ||
316 | |||
317 | static void | ||
318 | linux_dmabuf_create_params(struct wl_client *client, | ||
319 | struct wl_resource *linux_dmabuf_resource, | ||
320 | uint32_t params_id) | ||
321 | { | ||
322 | void *compositor; | ||
323 | struct linux_dmabuf_buffer *buffer; | ||
324 | uint32_t version; | ||
325 | int i; | ||
326 | |||
327 | version = wl_resource_get_version(linux_dmabuf_resource); | ||
328 | compositor = wl_resource_get_user_data(linux_dmabuf_resource); | ||
329 | |||
330 | buffer = calloc(1, sizeof *buffer); | ||
331 | if (!buffer) | ||
332 | goto err_out; | ||
333 | |||
334 | for (i = 0; i < MAX_DMABUF_PLANES; i++) | ||
335 | buffer->attributes.fd[i] = -1; | ||
336 | |||
337 | buffer->compositor = compositor; | ||
338 | buffer->params_resource = | ||
339 | wl_resource_create(client, | ||
340 | &zwp_linux_buffer_params_v1_interface, | ||
341 | version, params_id); | ||
342 | if (!buffer->params_resource) | ||
343 | goto err_dealloc; | ||
344 | |||
345 | wl_resource_set_implementation(buffer->params_resource, | ||
346 | &zwp_linux_buffer_params_implementation, | ||
347 | buffer, destroy_params); | ||
348 | |||
349 | return; | ||
350 | |||
351 | err_dealloc: | ||
352 | free(buffer); | ||
353 | |||
354 | err_out: | ||
355 | wl_resource_post_no_memory(linux_dmabuf_resource); | ||
356 | } | ||
357 | |||
358 | /** Get the linux_dmabuf_buffer from a wl_buffer resource | ||
359 | * | ||
360 | * If the given wl_buffer resource was created through the linux_dmabuf | ||
361 | * protocol interface, returns the linux_dmabuf_buffer object. This can | ||
362 | * be used as a type check for a wl_buffer. | ||
363 | * | ||
364 | * \param resource A wl_buffer resource. | ||
365 | * \return The linux_dmabuf_buffer if it exists, or NULL otherwise. | ||
366 | */ | ||
367 | struct linux_dmabuf_buffer * | ||
368 | linux_dmabuf_buffer_get(struct wl_resource *resource) | ||
369 | { | ||
370 | struct linux_dmabuf_buffer *buffer; | ||
371 | |||
372 | if (!resource) | ||
373 | return NULL; | ||
374 | |||
375 | if (!wl_resource_instance_of(resource, &wl_buffer_interface, | ||
376 | &linux_dmabuf_buffer_implementation)) | ||
377 | return NULL; | ||
378 | |||
379 | buffer = wl_resource_get_user_data(resource); | ||
380 | assert(buffer); | ||
381 | assert(!buffer->params_resource); | ||
382 | assert(buffer->buffer_resource == resource); | ||
383 | |||
384 | return buffer; | ||
385 | } | ||
386 | |||
387 | /** Set renderer-private data | ||
388 | * | ||
389 | * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite | ||
390 | * a non-NULL user data with a new non-NULL pointer. This is meant to | ||
391 | * protect against renderers fighting over linux_dmabuf_buffer user data | ||
392 | * ownership. | ||
393 | * | ||
394 | * The renderer-private data is usually set from the | ||
395 | * weston_renderer::import_dmabuf hook. | ||
396 | * | ||
397 | * \param buffer The linux_dmabuf_buffer object to set for. | ||
398 | * \param data The new renderer-private data pointer. | ||
399 | * \param func Destructor function to be called for the renderer-private | ||
400 | * data when the linux_dmabuf_buffer gets destroyed. | ||
401 | * | ||
402 | * \sa weston_compositor_import_dmabuf | ||
403 | */ | ||
404 | void | ||
405 | linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer, | ||
406 | void *data, | ||
407 | dmabuf_user_data_destroy_func func) | ||
408 | { | ||
409 | assert(data == NULL || buffer->user_data == NULL); | ||
410 | |||
411 | buffer->user_data = data; | ||
412 | buffer->user_data_destroy_func = func; | ||
413 | } | ||
414 | |||
415 | /** Get renderer-private data | ||
416 | * | ||
417 | * Get the user data from the linux_dmabuf_buffer. | ||
418 | * | ||
419 | * \param buffer The linux_dmabuf_buffer to query. | ||
420 | * \return Renderer-private data pointer. | ||
421 | * | ||
422 | * \sa linux_dmabuf_buffer_set_user_data | ||
423 | */ | ||
424 | void * | ||
425 | linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer) | ||
426 | { | ||
427 | return buffer->user_data; | ||
428 | } | ||
429 | |||
430 | static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = { | ||
431 | linux_dmabuf_destroy, | ||
432 | linux_dmabuf_create_params | ||
433 | }; | ||
434 | |||
435 | static void | ||
436 | bind_linux_dmabuf(struct wl_client *client, | ||
437 | void *data, uint32_t version, uint32_t id) | ||
438 | { | ||
439 | void *compositor = data; | ||
440 | struct wl_resource *resource; | ||
441 | |||
442 | resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, | ||
443 | version, id); | ||
444 | if (resource == NULL) { | ||
445 | wl_client_post_no_memory(client); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | wl_resource_set_implementation(resource, &linux_dmabuf_implementation, | ||
450 | compositor, NULL); | ||
451 | |||
452 | /* EGL_EXT_image_dma_buf_import does not provide a way to query the | ||
453 | * supported pixel formats. */ | ||
454 | /* XXX: send formats */ | ||
455 | } | ||
456 | |||
457 | /** Advertise linux_dmabuf support | ||
458 | * | ||
459 | * Calling this initializes the zwp_linux_dmabuf protocol support, so that | ||
460 | * the interface will be advertised to clients. Essentially it creates a | ||
461 | * global. Do not call this function multiple times in the compositor's | ||
462 | * lifetime. There is no way to deinit explicitly, globals will be reaped | ||
463 | * when the wl_display gets destroyed. | ||
464 | * | ||
465 | * \param compositor The compositor to init for. | ||
466 | * \return Zero on success, -1 on failure. | ||
467 | */ | ||
468 | int | ||
469 | linux_dmabuf_setup(struct wl_display *display, void *comp) | ||
470 | { | ||
471 | if (!wl_global_create(display, | ||
472 | &zwp_linux_dmabuf_v1_interface, 1, | ||
473 | comp, bind_linux_dmabuf)) | ||
474 | return -1; | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | /** Resolve an internal compositor error by disconnecting the client. | ||
480 | * | ||
481 | * This function is used in cases when the dmabuf-based wl_buffer | ||
482 | * turns out unusable and there is no fallback path. This is used by | ||
483 | * renderers which are the fallback path in the first place. | ||
484 | * | ||
485 | * It is possible the fault is caused by a compositor bug, the underlying | ||
486 | * graphics stack bug or normal behaviour, or perhaps a client mistake. | ||
487 | * In any case, the options are to either composite garbage or nothing, | ||
488 | * or disconnect the client. This is a helper function for the latter. | ||
489 | * | ||
490 | * The error is sent as a INVALID_OBJECT error on the client's wl_display. | ||
491 | * | ||
492 | * \param buffer The linux_dmabuf_buffer that is unusable. | ||
493 | * \param msg A custom error message attached to the protocol error. | ||
494 | */ | ||
495 | void | ||
496 | linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer, | ||
497 | const char *msg) | ||
498 | { | ||
499 | struct wl_client *client; | ||
500 | struct wl_resource *display_resource; | ||
501 | uint32_t id; | ||
502 | |||
503 | assert(buffer->buffer_resource); | ||
504 | id = wl_resource_get_id(buffer->buffer_resource); | ||
505 | client = wl_resource_get_client(buffer->buffer_resource); | ||
506 | display_resource = wl_client_get_object(client, 1); | ||
507 | |||
508 | assert(display_resource); | ||
509 | wl_resource_post_error(display_resource, | ||
510 | WL_DISPLAY_ERROR_INVALID_OBJECT, | ||
511 | "linux_dmabuf server error with " | ||
512 | "wl_buffer@%u: %s", id, msg); | ||
513 | } | ||