blob: c1627d6efb1b49c524bfa93b1aea3fd8bac1a2dd [file] [log] [blame]
Pekka Paalanen230f3b12014-09-29 14:18:40 -04001/*
2 * Copyright © 2014, 2015 Collabora, Ltd.
3 *
Yong Bakos53361532017-01-23 06:17:44 -08004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Pekka Paalanen230f3b12014-09-29 14:18:40 -040011 *
Yong Bakos53361532017-01-23 06:17:44 -080012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Pekka Paalanen230f3b12014-09-29 14:18:40 -040024 */
25
26#include "config.h"
27
28#include <assert.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030029#include <stdint.h>
Pekka Paalanen230f3b12014-09-29 14:18:40 -040030#include <unistd.h>
31#include <sys/types.h>
32
33#include "compositor.h"
34#include "linux-dmabuf.h"
Jonas Ådahl57e48f02015-11-17 16:00:28 +080035#include "linux-dmabuf-unstable-v1-server-protocol.h"
Pekka Paalanen230f3b12014-09-29 14:18:40 -040036
37static void
38linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
39{
40 int i;
41
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000042 for (i = 0; i < buffer->attributes.n_planes; i++) {
43 close(buffer->attributes.fd[i]);
44 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040045 }
46
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000047 buffer->attributes.n_planes = 0;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040048 free(buffer);
49}
50
51static void
52destroy_params(struct wl_resource *params_resource)
53{
54 struct linux_dmabuf_buffer *buffer;
55
56 buffer = wl_resource_get_user_data(params_resource);
57
58 if (!buffer)
59 return;
60
61 linux_dmabuf_buffer_destroy(buffer);
62}
63
64static void
65params_destroy(struct wl_client *client, struct wl_resource *resource)
66{
67 wl_resource_destroy(resource);
68}
69
70static void
71params_add(struct wl_client *client,
72 struct wl_resource *params_resource,
73 int32_t name_fd,
74 uint32_t plane_idx,
75 uint32_t offset,
76 uint32_t stride,
77 uint32_t modifier_hi,
78 uint32_t modifier_lo)
79{
80 struct linux_dmabuf_buffer *buffer;
81
82 buffer = wl_resource_get_user_data(params_resource);
83 if (!buffer) {
84 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080085 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040086 "params was already used to create a wl_buffer");
87 close(name_fd);
88 return;
89 }
90
91 assert(buffer->params_resource == params_resource);
92 assert(!buffer->buffer_resource);
93
94 if (plane_idx >= MAX_DMABUF_PLANES) {
95 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080096 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040097 "plane index %u is too high", plane_idx);
98 close(name_fd);
99 return;
100 }
101
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000102 if (buffer->attributes.fd[plane_idx] != -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400103 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800104 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400105 "a dmabuf has already been added for plane %u",
106 plane_idx);
107 close(name_fd);
108 return;
109 }
110
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000111 buffer->attributes.fd[plane_idx] = name_fd;
112 buffer->attributes.offset[plane_idx] = offset;
113 buffer->attributes.stride[plane_idx] = stride;
114 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
115 modifier_lo;
116 buffer->attributes.n_planes++;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400117}
118
119static void
120linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
121 struct wl_resource *resource)
122{
123 wl_resource_destroy(resource);
124}
125
126static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
127 linux_dmabuf_wl_buffer_destroy
128};
129
130static void
131destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
132{
133 struct linux_dmabuf_buffer *buffer;
134
135 buffer = wl_resource_get_user_data(resource);
136 assert(buffer->buffer_resource == resource);
137 assert(!buffer->params_resource);
138
139 if (buffer->user_data_destroy_func)
140 buffer->user_data_destroy_func(buffer);
141
142 linux_dmabuf_buffer_destroy(buffer);
143}
144
145static void
146params_create(struct wl_client *client,
147 struct wl_resource *params_resource,
148 int32_t width,
149 int32_t height,
150 uint32_t format,
151 uint32_t flags)
152{
153 struct linux_dmabuf_buffer *buffer;
154 int i;
155
156 buffer = wl_resource_get_user_data(params_resource);
157
158 if (!buffer) {
159 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800160 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400161 "params was already used to create a wl_buffer");
162 return;
163 }
164
165 assert(buffer->params_resource == params_resource);
166 assert(!buffer->buffer_resource);
167
168 /* Switch the linux_dmabuf_buffer object from params resource to
169 * eventually wl_buffer resource.
170 */
171 wl_resource_set_user_data(buffer->params_resource, NULL);
172 buffer->params_resource = NULL;
173
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000174 if (!buffer->attributes.n_planes) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400175 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800176 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400177 "no dmabuf has been added to the params");
178 goto err_out;
179 }
180
181 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000182 for (i = 0; i < buffer->attributes.n_planes; i++) {
183 if (buffer->attributes.fd[i] == -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400184 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800185 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400186 "no dmabuf has been added for plane %i", i);
187 goto err_out;
188 }
189 }
190
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000191 buffer->attributes.width = width;
192 buffer->attributes.height = height;
193 buffer->attributes.format = format;
194 buffer->attributes.flags = flags;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400195
196 if (width < 1 || height < 1) {
197 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800198 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400199 "invalid width %d or height %d", width, height);
200 goto err_out;
201 }
202
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000203 for (i = 0; i < buffer->attributes.n_planes; i++) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400204 off_t size;
205
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000206 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400207 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800208 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400209 "size overflow for plane %i", i);
210 goto err_out;
211 }
212
213 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000214 (uint64_t) buffer->attributes.offset[i] +
215 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400216 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800217 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400218 "size overflow for plane %i", i);
219 goto err_out;
220 }
221
222 /* Don't report an error as it might be caused
223 * by the kernel not supporting seeking on dmabuf */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000224 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400225 if (size == -1)
Derek Foremanc06389a2016-04-25 09:23:24 -0500226 continue;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400227
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000228 if (buffer->attributes.offset[i] >= size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400229 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800230 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400231 "invalid offset %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000232 buffer->attributes.offset[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400233 goto err_out;
234 }
235
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000236 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400237 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800238 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400239 "invalid stride %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000240 buffer->attributes.stride[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400241 goto err_out;
242 }
243
244 /* Only valid for first plane as other planes might be
245 * sub-sampled according to fourcc format */
246 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000247 buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400248 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800249 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400250 "invalid buffer stride or height for plane %i", i);
251 goto err_out;
252 }
253 }
254
255 /* XXX: Some additional sanity checks could be done with respect
256 * to the fourcc format. A centralized collection (kernel or
257 * libdrm) would be useful to avoid code duplication for these
258 * checks (e.g. drm_format_num_planes).
259 */
260
261 if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
262 goto err_failed;
263
264 buffer->buffer_resource = wl_resource_create(client,
265 &wl_buffer_interface,
266 1, 0);
267 if (!buffer->buffer_resource) {
268 wl_resource_post_no_memory(params_resource);
269 goto err_buffer;
270 }
271
272 wl_resource_set_implementation(buffer->buffer_resource,
273 &linux_dmabuf_buffer_implementation,
274 buffer, destroy_linux_dmabuf_wl_buffer);
275
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800276 zwp_linux_buffer_params_v1_send_created(params_resource,
277 buffer->buffer_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400278
279 return;
280
281err_buffer:
282 if (buffer->user_data_destroy_func)
283 buffer->user_data_destroy_func(buffer);
284
285err_failed:
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800286 zwp_linux_buffer_params_v1_send_failed(params_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400287
288err_out:
289 linux_dmabuf_buffer_destroy(buffer);
290}
291
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800292static const struct zwp_linux_buffer_params_v1_interface
293zwp_linux_buffer_params_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400294 params_destroy,
295 params_add,
296 params_create
297};
298
299static void
300linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
301{
302 wl_resource_destroy(resource);
303}
304
305static void
306linux_dmabuf_create_params(struct wl_client *client,
307 struct wl_resource *linux_dmabuf_resource,
308 uint32_t params_id)
309{
310 struct weston_compositor *compositor;
311 struct linux_dmabuf_buffer *buffer;
312 uint32_t version;
313 int i;
314
315 version = wl_resource_get_version(linux_dmabuf_resource);
316 compositor = wl_resource_get_user_data(linux_dmabuf_resource);
317
318 buffer = zalloc(sizeof *buffer);
319 if (!buffer)
320 goto err_out;
321
322 for (i = 0; i < MAX_DMABUF_PLANES; i++)
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000323 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400324
325 buffer->compositor = compositor;
326 buffer->params_resource =
327 wl_resource_create(client,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800328 &zwp_linux_buffer_params_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400329 version, params_id);
330 if (!buffer->params_resource)
331 goto err_dealloc;
332
333 wl_resource_set_implementation(buffer->params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800334 &zwp_linux_buffer_params_implementation,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400335 buffer, destroy_params);
336
337 return;
338
339err_dealloc:
340 free(buffer);
341
342err_out:
343 wl_resource_post_no_memory(linux_dmabuf_resource);
344}
345
346/** Get the linux_dmabuf_buffer from a wl_buffer resource
347 *
348 * If the given wl_buffer resource was created through the linux_dmabuf
349 * protocol interface, returns the linux_dmabuf_buffer object. This can
350 * be used as a type check for a wl_buffer.
351 *
352 * \param resource A wl_buffer resource.
353 * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
354 */
355WL_EXPORT struct linux_dmabuf_buffer *
356linux_dmabuf_buffer_get(struct wl_resource *resource)
357{
358 struct linux_dmabuf_buffer *buffer;
359
360 if (!resource)
361 return NULL;
362
363 if (!wl_resource_instance_of(resource, &wl_buffer_interface,
364 &linux_dmabuf_buffer_implementation))
365 return NULL;
366
367 buffer = wl_resource_get_user_data(resource);
368 assert(buffer);
369 assert(!buffer->params_resource);
370 assert(buffer->buffer_resource == resource);
371
372 return buffer;
373}
374
375/** Set renderer-private data
376 *
377 * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
378 * a non-NULL user data with a new non-NULL pointer. This is meant to
379 * protect against renderers fighting over linux_dmabuf_buffer user data
380 * ownership.
381 *
382 * The renderer-private data is usually set from the
383 * weston_renderer::import_dmabuf hook.
384 *
385 * \param buffer The linux_dmabuf_buffer object to set for.
386 * \param data The new renderer-private data pointer.
387 * \param func Destructor function to be called for the renderer-private
388 * data when the linux_dmabuf_buffer gets destroyed.
389 *
390 * \sa weston_compositor_import_dmabuf
391 */
392WL_EXPORT void
393linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
394 void *data,
395 dmabuf_user_data_destroy_func func)
396{
397 assert(data == NULL || buffer->user_data == NULL);
398
399 buffer->user_data = data;
400 buffer->user_data_destroy_func = func;
401}
402
403/** Get renderer-private data
404 *
405 * Get the user data from the linux_dmabuf_buffer.
406 *
407 * \param buffer The linux_dmabuf_buffer to query.
408 * \return Renderer-private data pointer.
409 *
410 * \sa linux_dmabuf_buffer_set_user_data
411 */
412WL_EXPORT void *
413linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
414{
415 return buffer->user_data;
416}
417
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800418static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400419 linux_dmabuf_destroy,
420 linux_dmabuf_create_params
421};
422
423static void
424bind_linux_dmabuf(struct wl_client *client,
425 void *data, uint32_t version, uint32_t id)
426{
427 struct weston_compositor *compositor = data;
428 struct wl_resource *resource;
429
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800430 resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400431 version, id);
432 if (resource == NULL) {
433 wl_client_post_no_memory(client);
434 return;
435 }
436
437 wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
438 compositor, NULL);
439
440 /* EGL_EXT_image_dma_buf_import does not provide a way to query the
441 * supported pixel formats. */
442 /* XXX: send formats */
443}
444
445/** Advertise linux_dmabuf support
446 *
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800447 * Calling this initializes the zwp_linux_dmabuf protocol support, so that
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400448 * the interface will be advertised to clients. Essentially it creates a
449 * global. Do not call this function multiple times in the compositor's
450 * lifetime. There is no way to deinit explicitly, globals will be reaped
451 * when the wl_display gets destroyed.
452 *
453 * \param compositor The compositor to init for.
454 * \return Zero on success, -1 on failure.
455 */
456WL_EXPORT int
457linux_dmabuf_setup(struct weston_compositor *compositor)
458{
459 if (!wl_global_create(compositor->wl_display,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800460 &zwp_linux_dmabuf_v1_interface, 1,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400461 compositor, bind_linux_dmabuf))
462 return -1;
463
464 return 0;
465}
466
467/** Resolve an internal compositor error by disconnecting the client.
468 *
469 * This function is used in cases when the dmabuf-based wl_buffer
470 * turns out unusable and there is no fallback path. This is used by
471 * renderers which are the fallback path in the first place.
472 *
473 * It is possible the fault is caused by a compositor bug, the underlying
474 * graphics stack bug or normal behaviour, or perhaps a client mistake.
475 * In any case, the options are to either composite garbage or nothing,
476 * or disconnect the client. This is a helper function for the latter.
477 *
Bryce Harringtonba63fae2016-07-06 15:30:54 -0700478 * The error is sent as an INVALID_OBJECT error on the client's wl_display.
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400479 *
480 * \param buffer The linux_dmabuf_buffer that is unusable.
481 * \param msg A custom error message attached to the protocol error.
482 */
483WL_EXPORT void
484linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
485 const char *msg)
486{
487 struct wl_client *client;
488 struct wl_resource *display_resource;
489 uint32_t id;
490
491 assert(buffer->buffer_resource);
492 id = wl_resource_get_id(buffer->buffer_resource);
493 client = wl_resource_get_client(buffer->buffer_resource);
494 display_resource = wl_client_get_object(client, 1);
495
496 assert(display_resource);
497 wl_resource_post_error(display_resource,
498 WL_DISPLAY_ERROR_INVALID_OBJECT,
499 "linux_dmabuf server error with "
500 "wl_buffer@%u: %s", id, msg);
501}