blob: 7b29f08d883b8dad7e9e2e8dc3f9679890c11bc8 [file] [log] [blame]
Pekka Paalanen230f3b12014-09-29 14:18:40 -04001/*
2 * Copyright © 2014, 2015 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "config.h"
24
25#include <assert.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030026#include <stdint.h>
Pekka Paalanen230f3b12014-09-29 14:18:40 -040027#include <unistd.h>
28#include <sys/types.h>
29
30#include "compositor.h"
31#include "linux-dmabuf.h"
Jonas Ådahl57e48f02015-11-17 16:00:28 +080032#include "linux-dmabuf-unstable-v1-server-protocol.h"
Pekka Paalanen230f3b12014-09-29 14:18:40 -040033
34static void
35linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
36{
37 int i;
38
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000039 for (i = 0; i < buffer->attributes.n_planes; i++) {
40 close(buffer->attributes.fd[i]);
41 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040042 }
43
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000044 buffer->attributes.n_planes = 0;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040045 free(buffer);
46}
47
48static void
49destroy_params(struct wl_resource *params_resource)
50{
51 struct linux_dmabuf_buffer *buffer;
52
53 buffer = wl_resource_get_user_data(params_resource);
54
55 if (!buffer)
56 return;
57
58 linux_dmabuf_buffer_destroy(buffer);
59}
60
61static void
62params_destroy(struct wl_client *client, struct wl_resource *resource)
63{
64 wl_resource_destroy(resource);
65}
66
67static void
68params_add(struct wl_client *client,
69 struct wl_resource *params_resource,
70 int32_t name_fd,
71 uint32_t plane_idx,
72 uint32_t offset,
73 uint32_t stride,
74 uint32_t modifier_hi,
75 uint32_t modifier_lo)
76{
77 struct linux_dmabuf_buffer *buffer;
78
79 buffer = wl_resource_get_user_data(params_resource);
80 if (!buffer) {
81 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080082 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040083 "params was already used to create a wl_buffer");
84 close(name_fd);
85 return;
86 }
87
88 assert(buffer->params_resource == params_resource);
89 assert(!buffer->buffer_resource);
90
91 if (plane_idx >= MAX_DMABUF_PLANES) {
92 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080093 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040094 "plane index %u is too high", plane_idx);
95 close(name_fd);
96 return;
97 }
98
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000099 if (buffer->attributes.fd[plane_idx] != -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400100 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800101 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400102 "a dmabuf has already been added for plane %u",
103 plane_idx);
104 close(name_fd);
105 return;
106 }
107
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000108 buffer->attributes.fd[plane_idx] = name_fd;
109 buffer->attributes.offset[plane_idx] = offset;
110 buffer->attributes.stride[plane_idx] = stride;
111 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
112 modifier_lo;
113 buffer->attributes.n_planes++;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400114}
115
116static void
117linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
118 struct wl_resource *resource)
119{
120 wl_resource_destroy(resource);
121}
122
123static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
124 linux_dmabuf_wl_buffer_destroy
125};
126
127static void
128destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
129{
130 struct linux_dmabuf_buffer *buffer;
131
132 buffer = wl_resource_get_user_data(resource);
133 assert(buffer->buffer_resource == resource);
134 assert(!buffer->params_resource);
135
136 if (buffer->user_data_destroy_func)
137 buffer->user_data_destroy_func(buffer);
138
139 linux_dmabuf_buffer_destroy(buffer);
140}
141
142static void
143params_create(struct wl_client *client,
144 struct wl_resource *params_resource,
145 int32_t width,
146 int32_t height,
147 uint32_t format,
148 uint32_t flags)
149{
150 struct linux_dmabuf_buffer *buffer;
151 int i;
152
153 buffer = wl_resource_get_user_data(params_resource);
154
155 if (!buffer) {
156 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800157 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400158 "params was already used to create a wl_buffer");
159 return;
160 }
161
162 assert(buffer->params_resource == params_resource);
163 assert(!buffer->buffer_resource);
164
165 /* Switch the linux_dmabuf_buffer object from params resource to
166 * eventually wl_buffer resource.
167 */
168 wl_resource_set_user_data(buffer->params_resource, NULL);
169 buffer->params_resource = NULL;
170
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000171 if (!buffer->attributes.n_planes) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400172 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800173 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400174 "no dmabuf has been added to the params");
175 goto err_out;
176 }
177
178 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000179 for (i = 0; i < buffer->attributes.n_planes; i++) {
180 if (buffer->attributes.fd[i] == -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400181 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800182 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400183 "no dmabuf has been added for plane %i", i);
184 goto err_out;
185 }
186 }
187
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000188 buffer->attributes.width = width;
189 buffer->attributes.height = height;
190 buffer->attributes.format = format;
191 buffer->attributes.flags = flags;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400192
193 if (width < 1 || height < 1) {
194 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800195 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400196 "invalid width %d or height %d", width, height);
197 goto err_out;
198 }
199
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000200 for (i = 0; i < buffer->attributes.n_planes; i++) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400201 off_t size;
202
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000203 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400204 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800205 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400206 "size overflow for plane %i", i);
207 goto err_out;
208 }
209
210 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000211 (uint64_t) buffer->attributes.offset[i] +
212 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400213 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800214 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400215 "size overflow for plane %i", i);
216 goto err_out;
217 }
218
219 /* Don't report an error as it might be caused
220 * by the kernel not supporting seeking on dmabuf */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000221 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400222 if (size == -1)
Derek Foremanc06389a2016-04-25 09:23:24 -0500223 continue;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400224
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000225 if (buffer->attributes.offset[i] >= size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400226 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800227 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400228 "invalid offset %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000229 buffer->attributes.offset[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400230 goto err_out;
231 }
232
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000233 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400234 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800235 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400236 "invalid stride %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000237 buffer->attributes.stride[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400238 goto err_out;
239 }
240
241 /* Only valid for first plane as other planes might be
242 * sub-sampled according to fourcc format */
243 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000244 buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400245 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800246 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400247 "invalid buffer stride or height for plane %i", i);
248 goto err_out;
249 }
250 }
251
252 /* XXX: Some additional sanity checks could be done with respect
253 * to the fourcc format. A centralized collection (kernel or
254 * libdrm) would be useful to avoid code duplication for these
255 * checks (e.g. drm_format_num_planes).
256 */
257
258 if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
259 goto err_failed;
260
261 buffer->buffer_resource = wl_resource_create(client,
262 &wl_buffer_interface,
263 1, 0);
264 if (!buffer->buffer_resource) {
265 wl_resource_post_no_memory(params_resource);
266 goto err_buffer;
267 }
268
269 wl_resource_set_implementation(buffer->buffer_resource,
270 &linux_dmabuf_buffer_implementation,
271 buffer, destroy_linux_dmabuf_wl_buffer);
272
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800273 zwp_linux_buffer_params_v1_send_created(params_resource,
274 buffer->buffer_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400275
276 return;
277
278err_buffer:
279 if (buffer->user_data_destroy_func)
280 buffer->user_data_destroy_func(buffer);
281
282err_failed:
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800283 zwp_linux_buffer_params_v1_send_failed(params_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400284
285err_out:
286 linux_dmabuf_buffer_destroy(buffer);
287}
288
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800289static const struct zwp_linux_buffer_params_v1_interface
290zwp_linux_buffer_params_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400291 params_destroy,
292 params_add,
293 params_create
294};
295
296static void
297linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
298{
299 wl_resource_destroy(resource);
300}
301
302static void
303linux_dmabuf_create_params(struct wl_client *client,
304 struct wl_resource *linux_dmabuf_resource,
305 uint32_t params_id)
306{
307 struct weston_compositor *compositor;
308 struct linux_dmabuf_buffer *buffer;
309 uint32_t version;
310 int i;
311
312 version = wl_resource_get_version(linux_dmabuf_resource);
313 compositor = wl_resource_get_user_data(linux_dmabuf_resource);
314
315 buffer = zalloc(sizeof *buffer);
316 if (!buffer)
317 goto err_out;
318
319 for (i = 0; i < MAX_DMABUF_PLANES; i++)
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000320 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400321
322 buffer->compositor = compositor;
323 buffer->params_resource =
324 wl_resource_create(client,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800325 &zwp_linux_buffer_params_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400326 version, params_id);
327 if (!buffer->params_resource)
328 goto err_dealloc;
329
330 wl_resource_set_implementation(buffer->params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800331 &zwp_linux_buffer_params_implementation,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400332 buffer, destroy_params);
333
334 return;
335
336err_dealloc:
337 free(buffer);
338
339err_out:
340 wl_resource_post_no_memory(linux_dmabuf_resource);
341}
342
343/** Get the linux_dmabuf_buffer from a wl_buffer resource
344 *
345 * If the given wl_buffer resource was created through the linux_dmabuf
346 * protocol interface, returns the linux_dmabuf_buffer object. This can
347 * be used as a type check for a wl_buffer.
348 *
349 * \param resource A wl_buffer resource.
350 * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
351 */
352WL_EXPORT struct linux_dmabuf_buffer *
353linux_dmabuf_buffer_get(struct wl_resource *resource)
354{
355 struct linux_dmabuf_buffer *buffer;
356
357 if (!resource)
358 return NULL;
359
360 if (!wl_resource_instance_of(resource, &wl_buffer_interface,
361 &linux_dmabuf_buffer_implementation))
362 return NULL;
363
364 buffer = wl_resource_get_user_data(resource);
365 assert(buffer);
366 assert(!buffer->params_resource);
367 assert(buffer->buffer_resource == resource);
368
369 return buffer;
370}
371
372/** Set renderer-private data
373 *
374 * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
375 * a non-NULL user data with a new non-NULL pointer. This is meant to
376 * protect against renderers fighting over linux_dmabuf_buffer user data
377 * ownership.
378 *
379 * The renderer-private data is usually set from the
380 * weston_renderer::import_dmabuf hook.
381 *
382 * \param buffer The linux_dmabuf_buffer object to set for.
383 * \param data The new renderer-private data pointer.
384 * \param func Destructor function to be called for the renderer-private
385 * data when the linux_dmabuf_buffer gets destroyed.
386 *
387 * \sa weston_compositor_import_dmabuf
388 */
389WL_EXPORT void
390linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
391 void *data,
392 dmabuf_user_data_destroy_func func)
393{
394 assert(data == NULL || buffer->user_data == NULL);
395
396 buffer->user_data = data;
397 buffer->user_data_destroy_func = func;
398}
399
400/** Get renderer-private data
401 *
402 * Get the user data from the linux_dmabuf_buffer.
403 *
404 * \param buffer The linux_dmabuf_buffer to query.
405 * \return Renderer-private data pointer.
406 *
407 * \sa linux_dmabuf_buffer_set_user_data
408 */
409WL_EXPORT void *
410linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
411{
412 return buffer->user_data;
413}
414
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800415static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400416 linux_dmabuf_destroy,
417 linux_dmabuf_create_params
418};
419
420static void
421bind_linux_dmabuf(struct wl_client *client,
422 void *data, uint32_t version, uint32_t id)
423{
424 struct weston_compositor *compositor = data;
425 struct wl_resource *resource;
426
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800427 resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400428 version, id);
429 if (resource == NULL) {
430 wl_client_post_no_memory(client);
431 return;
432 }
433
434 wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
435 compositor, NULL);
436
437 /* EGL_EXT_image_dma_buf_import does not provide a way to query the
438 * supported pixel formats. */
439 /* XXX: send formats */
440}
441
442/** Advertise linux_dmabuf support
443 *
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800444 * Calling this initializes the zwp_linux_dmabuf protocol support, so that
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400445 * the interface will be advertised to clients. Essentially it creates a
446 * global. Do not call this function multiple times in the compositor's
447 * lifetime. There is no way to deinit explicitly, globals will be reaped
448 * when the wl_display gets destroyed.
449 *
450 * \param compositor The compositor to init for.
451 * \return Zero on success, -1 on failure.
452 */
453WL_EXPORT int
454linux_dmabuf_setup(struct weston_compositor *compositor)
455{
456 if (!wl_global_create(compositor->wl_display,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800457 &zwp_linux_dmabuf_v1_interface, 1,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400458 compositor, bind_linux_dmabuf))
459 return -1;
460
461 return 0;
462}
463
464/** Resolve an internal compositor error by disconnecting the client.
465 *
466 * This function is used in cases when the dmabuf-based wl_buffer
467 * turns out unusable and there is no fallback path. This is used by
468 * renderers which are the fallback path in the first place.
469 *
470 * It is possible the fault is caused by a compositor bug, the underlying
471 * graphics stack bug or normal behaviour, or perhaps a client mistake.
472 * In any case, the options are to either composite garbage or nothing,
473 * or disconnect the client. This is a helper function for the latter.
474 *
Bryce Harringtonba63fae2016-07-06 15:30:54 -0700475 * The error is sent as an INVALID_OBJECT error on the client's wl_display.
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400476 *
477 * \param buffer The linux_dmabuf_buffer that is unusable.
478 * \param msg A custom error message attached to the protocol error.
479 */
480WL_EXPORT void
481linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
482 const char *msg)
483{
484 struct wl_client *client;
485 struct wl_resource *display_resource;
486 uint32_t id;
487
488 assert(buffer->buffer_resource);
489 id = wl_resource_get_id(buffer->buffer_resource);
490 client = wl_resource_get_client(buffer->buffer_resource);
491 display_resource = wl_client_get_object(client, 1);
492
493 assert(display_resource);
494 wl_resource_post_error(display_resource,
495 WL_DISPLAY_ERROR_INVALID_OBJECT,
496 "linux_dmabuf server error with "
497 "wl_buffer@%u: %s", id, msg);
498}