blob: 78e77a249792c5b60c599f1f3157f1be11ebe913 [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>
26#include <unistd.h>
27#include <sys/types.h>
28
29#include "compositor.h"
30#include "linux-dmabuf.h"
Jonas Ådahl57e48f02015-11-17 16:00:28 +080031#include "linux-dmabuf-unstable-v1-server-protocol.h"
Pekka Paalanen230f3b12014-09-29 14:18:40 -040032
33static void
34linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
35{
36 int i;
37
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000038 for (i = 0; i < buffer->attributes.n_planes; i++) {
39 close(buffer->attributes.fd[i]);
40 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040041 }
42
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000043 buffer->attributes.n_planes = 0;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040044 free(buffer);
45}
46
47static void
48destroy_params(struct wl_resource *params_resource)
49{
50 struct linux_dmabuf_buffer *buffer;
51
52 buffer = wl_resource_get_user_data(params_resource);
53
54 if (!buffer)
55 return;
56
57 linux_dmabuf_buffer_destroy(buffer);
58}
59
60static void
61params_destroy(struct wl_client *client, struct wl_resource *resource)
62{
63 wl_resource_destroy(resource);
64}
65
66static void
67params_add(struct wl_client *client,
68 struct wl_resource *params_resource,
69 int32_t name_fd,
70 uint32_t plane_idx,
71 uint32_t offset,
72 uint32_t stride,
73 uint32_t modifier_hi,
74 uint32_t modifier_lo)
75{
76 struct linux_dmabuf_buffer *buffer;
77
78 buffer = wl_resource_get_user_data(params_resource);
79 if (!buffer) {
80 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080081 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040082 "params was already used to create a wl_buffer");
83 close(name_fd);
84 return;
85 }
86
87 assert(buffer->params_resource == params_resource);
88 assert(!buffer->buffer_resource);
89
90 if (plane_idx >= MAX_DMABUF_PLANES) {
91 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080092 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040093 "plane index %u is too high", plane_idx);
94 close(name_fd);
95 return;
96 }
97
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000098 if (buffer->attributes.fd[plane_idx] != -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -040099 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800100 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400101 "a dmabuf has already been added for plane %u",
102 plane_idx);
103 close(name_fd);
104 return;
105 }
106
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000107 buffer->attributes.fd[plane_idx] = name_fd;
108 buffer->attributes.offset[plane_idx] = offset;
109 buffer->attributes.stride[plane_idx] = stride;
110 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
111 modifier_lo;
112 buffer->attributes.n_planes++;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400113}
114
115static void
116linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
117 struct wl_resource *resource)
118{
119 wl_resource_destroy(resource);
120}
121
122static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
123 linux_dmabuf_wl_buffer_destroy
124};
125
126static void
127destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
128{
129 struct linux_dmabuf_buffer *buffer;
130
131 buffer = wl_resource_get_user_data(resource);
132 assert(buffer->buffer_resource == resource);
133 assert(!buffer->params_resource);
134
135 if (buffer->user_data_destroy_func)
136 buffer->user_data_destroy_func(buffer);
137
138 linux_dmabuf_buffer_destroy(buffer);
139}
140
141static void
142params_create(struct wl_client *client,
143 struct wl_resource *params_resource,
144 int32_t width,
145 int32_t height,
146 uint32_t format,
147 uint32_t flags)
148{
149 struct linux_dmabuf_buffer *buffer;
150 int i;
151
152 buffer = wl_resource_get_user_data(params_resource);
153
154 if (!buffer) {
155 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800156 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400157 "params was already used to create a wl_buffer");
158 return;
159 }
160
161 assert(buffer->params_resource == params_resource);
162 assert(!buffer->buffer_resource);
163
164 /* Switch the linux_dmabuf_buffer object from params resource to
165 * eventually wl_buffer resource.
166 */
167 wl_resource_set_user_data(buffer->params_resource, NULL);
168 buffer->params_resource = NULL;
169
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000170 if (!buffer->attributes.n_planes) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400171 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800172 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400173 "no dmabuf has been added to the params");
174 goto err_out;
175 }
176
177 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000178 for (i = 0; i < buffer->attributes.n_planes; i++) {
179 if (buffer->attributes.fd[i] == -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400180 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800181 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400182 "no dmabuf has been added for plane %i", i);
183 goto err_out;
184 }
185 }
186
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000187 buffer->attributes.width = width;
188 buffer->attributes.height = height;
189 buffer->attributes.format = format;
190 buffer->attributes.flags = flags;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400191
192 if (width < 1 || height < 1) {
193 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800194 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400195 "invalid width %d or height %d", width, height);
196 goto err_out;
197 }
198
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000199 for (i = 0; i < buffer->attributes.n_planes; i++) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400200 off_t size;
201
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000202 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400203 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800204 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400205 "size overflow for plane %i", i);
206 goto err_out;
207 }
208
209 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000210 (uint64_t) buffer->attributes.offset[i] +
211 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400212 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800213 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400214 "size overflow for plane %i", i);
215 goto err_out;
216 }
217
218 /* Don't report an error as it might be caused
219 * by the kernel not supporting seeking on dmabuf */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000220 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400221 if (size == -1)
Derek Foremanc06389a2016-04-25 09:23:24 -0500222 continue;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400223
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000224 if (buffer->attributes.offset[i] >= size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400225 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800226 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400227 "invalid offset %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000228 buffer->attributes.offset[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400229 goto err_out;
230 }
231
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000232 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400233 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800234 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400235 "invalid stride %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000236 buffer->attributes.stride[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400237 goto err_out;
238 }
239
240 /* Only valid for first plane as other planes might be
241 * sub-sampled according to fourcc format */
242 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000243 buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400244 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800245 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400246 "invalid buffer stride or height for plane %i", i);
247 goto err_out;
248 }
249 }
250
251 /* XXX: Some additional sanity checks could be done with respect
252 * to the fourcc format. A centralized collection (kernel or
253 * libdrm) would be useful to avoid code duplication for these
254 * checks (e.g. drm_format_num_planes).
255 */
256
257 if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
258 goto err_failed;
259
260 buffer->buffer_resource = wl_resource_create(client,
261 &wl_buffer_interface,
262 1, 0);
263 if (!buffer->buffer_resource) {
264 wl_resource_post_no_memory(params_resource);
265 goto err_buffer;
266 }
267
268 wl_resource_set_implementation(buffer->buffer_resource,
269 &linux_dmabuf_buffer_implementation,
270 buffer, destroy_linux_dmabuf_wl_buffer);
271
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800272 zwp_linux_buffer_params_v1_send_created(params_resource,
273 buffer->buffer_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400274
275 return;
276
277err_buffer:
278 if (buffer->user_data_destroy_func)
279 buffer->user_data_destroy_func(buffer);
280
281err_failed:
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800282 zwp_linux_buffer_params_v1_send_failed(params_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400283
284err_out:
285 linux_dmabuf_buffer_destroy(buffer);
286}
287
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800288static const struct zwp_linux_buffer_params_v1_interface
289zwp_linux_buffer_params_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400290 params_destroy,
291 params_add,
292 params_create
293};
294
295static void
296linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
297{
298 wl_resource_destroy(resource);
299}
300
301static void
302linux_dmabuf_create_params(struct wl_client *client,
303 struct wl_resource *linux_dmabuf_resource,
304 uint32_t params_id)
305{
306 struct weston_compositor *compositor;
307 struct linux_dmabuf_buffer *buffer;
308 uint32_t version;
309 int i;
310
311 version = wl_resource_get_version(linux_dmabuf_resource);
312 compositor = wl_resource_get_user_data(linux_dmabuf_resource);
313
314 buffer = zalloc(sizeof *buffer);
315 if (!buffer)
316 goto err_out;
317
318 for (i = 0; i < MAX_DMABUF_PLANES; i++)
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000319 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400320
321 buffer->compositor = compositor;
322 buffer->params_resource =
323 wl_resource_create(client,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800324 &zwp_linux_buffer_params_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400325 version, params_id);
326 if (!buffer->params_resource)
327 goto err_dealloc;
328
329 wl_resource_set_implementation(buffer->params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800330 &zwp_linux_buffer_params_implementation,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400331 buffer, destroy_params);
332
333 return;
334
335err_dealloc:
336 free(buffer);
337
338err_out:
339 wl_resource_post_no_memory(linux_dmabuf_resource);
340}
341
342/** Get the linux_dmabuf_buffer from a wl_buffer resource
343 *
344 * If the given wl_buffer resource was created through the linux_dmabuf
345 * protocol interface, returns the linux_dmabuf_buffer object. This can
346 * be used as a type check for a wl_buffer.
347 *
348 * \param resource A wl_buffer resource.
349 * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
350 */
351WL_EXPORT struct linux_dmabuf_buffer *
352linux_dmabuf_buffer_get(struct wl_resource *resource)
353{
354 struct linux_dmabuf_buffer *buffer;
355
356 if (!resource)
357 return NULL;
358
359 if (!wl_resource_instance_of(resource, &wl_buffer_interface,
360 &linux_dmabuf_buffer_implementation))
361 return NULL;
362
363 buffer = wl_resource_get_user_data(resource);
364 assert(buffer);
365 assert(!buffer->params_resource);
366 assert(buffer->buffer_resource == resource);
367
368 return buffer;
369}
370
371/** Set renderer-private data
372 *
373 * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
374 * a non-NULL user data with a new non-NULL pointer. This is meant to
375 * protect against renderers fighting over linux_dmabuf_buffer user data
376 * ownership.
377 *
378 * The renderer-private data is usually set from the
379 * weston_renderer::import_dmabuf hook.
380 *
381 * \param buffer The linux_dmabuf_buffer object to set for.
382 * \param data The new renderer-private data pointer.
383 * \param func Destructor function to be called for the renderer-private
384 * data when the linux_dmabuf_buffer gets destroyed.
385 *
386 * \sa weston_compositor_import_dmabuf
387 */
388WL_EXPORT void
389linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
390 void *data,
391 dmabuf_user_data_destroy_func func)
392{
393 assert(data == NULL || buffer->user_data == NULL);
394
395 buffer->user_data = data;
396 buffer->user_data_destroy_func = func;
397}
398
399/** Get renderer-private data
400 *
401 * Get the user data from the linux_dmabuf_buffer.
402 *
403 * \param buffer The linux_dmabuf_buffer to query.
404 * \return Renderer-private data pointer.
405 *
406 * \sa linux_dmabuf_buffer_set_user_data
407 */
408WL_EXPORT void *
409linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
410{
411 return buffer->user_data;
412}
413
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800414static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400415 linux_dmabuf_destroy,
416 linux_dmabuf_create_params
417};
418
419static void
420bind_linux_dmabuf(struct wl_client *client,
421 void *data, uint32_t version, uint32_t id)
422{
423 struct weston_compositor *compositor = data;
424 struct wl_resource *resource;
425
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800426 resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400427 version, id);
428 if (resource == NULL) {
429 wl_client_post_no_memory(client);
430 return;
431 }
432
433 wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
434 compositor, NULL);
435
436 /* EGL_EXT_image_dma_buf_import does not provide a way to query the
437 * supported pixel formats. */
438 /* XXX: send formats */
439}
440
441/** Advertise linux_dmabuf support
442 *
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800443 * Calling this initializes the zwp_linux_dmabuf protocol support, so that
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400444 * the interface will be advertised to clients. Essentially it creates a
445 * global. Do not call this function multiple times in the compositor's
446 * lifetime. There is no way to deinit explicitly, globals will be reaped
447 * when the wl_display gets destroyed.
448 *
449 * \param compositor The compositor to init for.
450 * \return Zero on success, -1 on failure.
451 */
452WL_EXPORT int
453linux_dmabuf_setup(struct weston_compositor *compositor)
454{
455 if (!wl_global_create(compositor->wl_display,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800456 &zwp_linux_dmabuf_v1_interface, 1,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400457 compositor, bind_linux_dmabuf))
458 return -1;
459
460 return 0;
461}
462
463/** Resolve an internal compositor error by disconnecting the client.
464 *
465 * This function is used in cases when the dmabuf-based wl_buffer
466 * turns out unusable and there is no fallback path. This is used by
467 * renderers which are the fallback path in the first place.
468 *
469 * It is possible the fault is caused by a compositor bug, the underlying
470 * graphics stack bug or normal behaviour, or perhaps a client mistake.
471 * In any case, the options are to either composite garbage or nothing,
472 * or disconnect the client. This is a helper function for the latter.
473 *
474 * The error is sent as a INVALID_OBJECT error on the client's wl_display.
475 *
476 * \param buffer The linux_dmabuf_buffer that is unusable.
477 * \param msg A custom error message attached to the protocol error.
478 */
479WL_EXPORT void
480linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
481 const char *msg)
482{
483 struct wl_client *client;
484 struct wl_resource *display_resource;
485 uint32_t id;
486
487 assert(buffer->buffer_resource);
488 id = wl_resource_get_id(buffer->buffer_resource);
489 client = wl_resource_get_client(buffer->buffer_resource);
490 display_resource = wl_client_get_object(client, 1);
491
492 assert(display_resource);
493 wl_resource_post_error(display_resource,
494 WL_DISPLAY_ERROR_INVALID_OBJECT,
495 "linux_dmabuf server error with "
496 "wl_buffer@%u: %s", id, msg);
497}