e_linux_dmabuf: add first implementation of ds_linux_dmabuf interface
[platform/upstream/enlightenment.git] / src / bin / e_linux_dmabuf.c
1 #include "e_linux_dmabuf_intern.h"
2 #include "e_comp_screen_intern.h"
3 #include "e_comp_intern.h"
4
5 #include <assert.h>
6 #include <stdbool.h>
7 #include <libds/linux_dmabuf_v1.h>
8 #include <EGL/egl.h>
9 #include <EGL/eglext.h>
10 #include <drm_fourcc.h>
11 #include <libds/util/addon.h>
12 #include <libds/types/ds_buffer.h>
13
14 #ifndef EGL_EXT_image_dma_buf_import_modifiers
15 #define EGL_EXT_image_dma_buf_import_modifiers 1
16 #define EGL_DMA_BUF_PLANE3_FD_EXT         0x3440
17 #define EGL_DMA_BUF_PLANE3_OFFSET_EXT     0x3441
18 #define EGL_DMA_BUF_PLANE3_PITCH_EXT      0x3442
19 #define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443
20 #define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444
21 #define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445
22 #define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446
23 #define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447
24 #define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448
25 #define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449
26 #define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A
27 typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFFORMATSEXTPROC) (EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
28 typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFMODIFIERSEXTPROC) (EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
29 #ifdef EGL_EGLEXT_PROTOTYPES
30 EGLAPI EGLBoolean EGLAPIENTRY eglQueryDmaBufFormatsEXT (EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
31 EGLAPI EGLBoolean EGLAPIENTRY eglQueryDmaBufModifiersEXT (EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
32 #endif
33 #endif /* EGL_EXT_image_dma_buf_import_modifiers */
34
35 typedef struct _E_Linux_Dmabuf E_Linux_Dmabuf;
36 typedef struct _E_Linux_Dmabuf_Buffer E_Linux_Dmabuf_Buffer;
37
38 static E_Linux_Dmabuf *_dmabuf = NULL;
39
40 struct _E_Linux_Dmabuf
41 {
42    struct ds_linux_dmabuf_v1 *ds_dmabuf;
43
44    struct {
45       struct wl_listener display_destroy;
46    } listener;
47
48    EGLDisplay egl_display;
49    PFNEGLQUERYDMABUFFORMATSEXTPROC query_dmabuf_formats;
50
51    struct ds_linux_dmabuf_v1_format *supported_formats;
52    int num_formats;
53 };
54
55 struct _E_Linux_Dmabuf_Buffer
56 {
57    struct ds_buffer *ds_buffer;
58    struct ds_addon ds_buffer_addon;
59
60    tbm_surface_h tsurface;
61 };
62
63 static void
64 _e_linux_dmabuf_cb_display_destroy(struct wl_listener *listener, void *data)
65 {
66    E_Linux_Dmabuf *dmabuf;
67
68    dmabuf = wl_container_of(listener, dmabuf, listener.display_destroy);
69    wl_list_remove(&dmabuf->listener.display_destroy.link);
70    free(dmabuf->supported_formats);
71    free(dmabuf);
72 }
73
74 static Eina_Bool
75 _e_linux_dmabuf_extension_check(const char *extensions, const char *extension)
76 {
77    size_t extlen = strlen(extension);
78    const char *end = extensions + strlen(extensions);
79
80    while (extensions < end)
81      {
82         size_t n = 0;
83
84         /* Skip whitespaces, if any */
85         if (*extensions == ' ')
86           {
87              extensions++;
88              continue;
89           }
90
91         n = strcspn(extensions, " ");
92
93         /* Compare strings */
94         if (n == extlen && strncmp(extension, extensions, n) == 0)
95           return EINA_TRUE;
96
97         extensions += n;
98     }
99
100    return EINA_FALSE;
101 }
102
103 static int *
104 _e_linux_dmabuf_egl_format_get(E_Linux_Dmabuf *dmabuf, size_t *num_formats)
105 {
106    int *formats;
107    int num = 0;
108
109    if (!dmabuf->query_dmabuf_formats(dmabuf->egl_display, 0, NULL, &num))
110      {
111         ERR("fail to eglQueryDmaBufFormatsEXT");
112         return NULL;
113      }
114
115    formats = E_NEW(int, num);
116    EINA_SAFETY_ON_NULL_RETURN_VAL(formats, NULL);
117    
118    if (!dmabuf->query_dmabuf_formats(dmabuf->egl_display, num, formats, &num))
119      {
120         ERR("fail to eglQueryDmaBufFormatsEXT");
121         free(formats);
122         return NULL;
123      }
124
125    *num_formats = num;
126
127    return formats;
128 }
129
130 static Eina_Bool
131 _e_linux_dmabuf_egl_format_init(E_Linux_Dmabuf *dmabuf)
132 {
133    const char *extensions = NULL;
134
135    if (!e_comp_gl_get()) return EINA_FALSE;
136
137    dmabuf->egl_display = eglGetCurrentDisplay();
138    if (!dmabuf->egl_display) return EINA_FALSE;
139
140    extensions = (const char *)eglQueryString(dmabuf->egl_display, EGL_EXTENSIONS);
141    if (!extensions) return EINA_FALSE;
142
143    if (!_e_linux_dmabuf_extension_check(extensions, "EGL_EXT_image_dma_buf_import_modifiers"))
144      return EINA_FALSE;
145
146    dmabuf->query_dmabuf_formats = (void *)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
147    if (!dmabuf->query_dmabuf_formats) return EINA_FALSE;
148
149    return EINA_TRUE;
150 }
151
152 static struct ds_linux_dmabuf_v1_format *
153 _e_linux_dmabuf_supported_format_get(E_Linux_Dmabuf *dmabuf, int *num_formats)
154 {
155    struct ds_linux_dmabuf_v1_format *formats;
156    static int fallback_formats[] = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888};
157    static uint64_t fallback_modifier = DRM_FORMAT_MOD_LINEAR;
158    int *egl_formats;
159    size_t num = 0;
160    int i;
161
162    if (!_e_linux_dmabuf_egl_format_init(dmabuf))
163      goto fallback_formats;
164
165    egl_formats = _e_linux_dmabuf_egl_format_get(dmabuf, &num);
166    if (!egl_formats) goto fallback_formats;
167
168    formats = calloc(num, sizeof *formats);
169    EINA_SAFETY_ON_NULL_RETURN_VAL(formats, NULL);
170
171    for (i = 0; i < num; i++)
172      {
173         formats[i].format = egl_formats[i];
174         /* Currently, modifier is not supported */
175         formats[i].modifiers = &fallback_modifier;
176         formats[i].num_modifiers = 1;
177      }
178
179    *num_formats = num;
180
181    return formats;
182
183 fallback_formats:
184    num = sizeof(fallback_formats) / sizeof(fallback_formats[0]);
185
186    formats = calloc(num, sizeof *formats);
187    EINA_SAFETY_ON_NULL_RETURN_VAL(formats, NULL);
188
189    for (i = 0; i < num; i++)
190      {
191         formats[i].format = fallback_formats[0];
192         formats[i].modifiers = &fallback_modifier;
193         formats[i].num_modifiers = 1;
194      }
195
196    *num_formats = num;
197
198    return formats;
199 }
200
201 EINTERN Eina_Bool
202 e_linux_dmabuf_init(struct wl_display *display)
203 {
204    E_Linux_Dmabuf *dmabuf;
205
206    if (_dmabuf) return EINA_TRUE;
207
208    dmabuf = E_NEW(E_Linux_Dmabuf, 1);
209    if (!dmabuf)
210      return EINA_FALSE;
211
212    dmabuf->supported_formats = _e_linux_dmabuf_supported_format_get(dmabuf,
213          &dmabuf->num_formats);
214    if (!dmabuf->supported_formats)
215      {
216         ERR("fail to _e_linux_dmabuf_supported_format_get");
217         return EINA_FALSE;
218      }
219
220    dmabuf->ds_dmabuf = ds_linux_dmabuf_v1_create(display,
221          dmabuf->supported_formats, dmabuf->num_formats);
222    if (!dmabuf->ds_dmabuf)
223      {
224         ERR("Could not create ds_linux_dmabuf_v1");
225         free(dmabuf);
226         return EINA_FALSE;
227      }
228
229    dmabuf->listener.display_destroy.notify = _e_linux_dmabuf_cb_display_destroy;
230    wl_display_add_destroy_listener(display, &dmabuf->listener.display_destroy);
231
232    _dmabuf = dmabuf;
233
234    return EINA_TRUE;
235 }
236
237 static void
238 _e_linux_dmabuf_buffer_cb_ds_buffer_destroy(struct ds_addon *addon)
239 {
240    E_Linux_Dmabuf_Buffer *dmabuf_buffer;
241
242    dmabuf_buffer = wl_container_of(addon, dmabuf_buffer, ds_buffer_addon);
243
244    ds_addon_finish(&dmabuf_buffer->ds_buffer_addon);
245
246    tbm_surface_internal_unref(dmabuf_buffer->tsurface);
247    free(dmabuf_buffer);
248 }
249
250 static struct ds_addon_interface _e_linux_dmabuf_buffer_addon_impl = {
251    .name = "e_linux_dmabuf_buffer",
252    .destroy = _e_linux_dmabuf_buffer_cb_ds_buffer_destroy,
253 };
254
255 static E_Linux_Dmabuf_Buffer *
256 _e_linux_dmabuf_buffer_get_from_ds_buffer(struct ds_buffer *ds_buffer)
257 {
258    struct ds_addon *addon;
259    E_Linux_Dmabuf_Buffer *dmabuf_buffer;
260
261    addon = ds_addon_find(&ds_buffer->addons, _dmabuf,
262                          &_e_linux_dmabuf_buffer_addon_impl);
263    if (!addon) return NULL;
264
265    dmabuf_buffer = wl_container_of(addon, dmabuf_buffer, ds_buffer_addon);
266
267    return dmabuf_buffer;
268 }
269
270 EINTERN tbm_surface_h
271 e_linux_dmabuf_tbm_surface_get_from_buffer(struct ds_buffer *ds_buffer)
272 {
273    E_Linux_Dmabuf_Buffer *dmabuf_buffer;
274    struct ds_linux_dmabuf_v1_buffer *ds_dmabuf_buffer;
275    const struct ds_linux_dmabuf_v1_attributes *ds_attributes;
276    tbm_surface_h tsurface = NULL;
277    tbm_surface_info_s info = {0, };
278    tbm_bo bos[LINUX_DMABUF_MAX_PLANES] = {0, };
279    off_t bos_size[LINUX_DMABUF_MAX_PLANES] = {0, };
280    int i = 0;
281
282    EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp, NULL);
283    EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp->e_comp_screen, NULL);
284    EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp->e_comp_screen->bufmgr, NULL);
285
286    if (!_dmabuf) return NULL;
287
288    ds_dmabuf_buffer = ds_linux_dmabuf_v1_buffer_from_buffer(ds_buffer);
289    if (!ds_dmabuf_buffer)
290      return NULL;
291
292    dmabuf_buffer = _e_linux_dmabuf_buffer_get_from_ds_buffer(ds_buffer);
293    if (dmabuf_buffer)
294       return dmabuf_buffer->tsurface;
295
296    ds_attributes = ds_linux_dmabuf_v1_buffer_get_attributes(ds_dmabuf_buffer);
297    EINA_SAFETY_ON_NULL_RETURN_VAL(ds_attributes, NULL);
298
299    info.width = ds_attributes->width;
300    info.height = ds_attributes->height;
301    info.format = ds_attributes->format;
302    info.num_planes = ds_attributes->num_planes;
303
304    for (i = 0; i < info.num_planes; i++)
305      {
306         info.planes[i].stride = ds_attributes->stride[i];
307         info.planes[i].offset = ds_attributes->offset[i];
308
309         bos[i] = tbm_bo_import_fd(e_comp->e_comp_screen->bufmgr, ds_attributes->fd[i]);
310         EINA_SAFETY_ON_NULL_GOTO(bos[i], failed);
311
312         bos_size[i] = lseek(ds_attributes->fd[i], 0, SEEK_END);
313         EINA_SAFETY_ON_TRUE_GOTO(bos_size[i] == -1, failed);
314      }
315
316    for (i = 0; i < info.num_planes; i++)
317      { 
318         if ((i + 1 ==  info.num_planes) || (bos[i] != bos[i + 1]))
319           info.planes[i].size = bos_size[i] - info.planes[i].offset;
320         else
321           info.planes[i].size = info.planes[i + 1].offset - info.planes[i].offset;
322
323         info.size += info.planes[i].size;
324      }
325
326    tsurface = tbm_surface_internal_create_with_bos(&info, bos, info.num_planes);
327    EINA_SAFETY_ON_NULL_GOTO(tsurface, failed);
328
329    for (i = 0; i < info.num_planes; i++)
330      {
331         if (bos[i])
332            tbm_bo_unref(bos[i]);
333      }
334
335    dmabuf_buffer = E_NEW(E_Linux_Dmabuf_Buffer, 1);
336    EINA_SAFETY_ON_NULL_GOTO(dmabuf_buffer, failed);
337
338    dmabuf_buffer->tsurface = tsurface;
339
340    ds_addon_init(&dmabuf_buffer->ds_buffer_addon, &ds_buffer->addons,
341                  _dmabuf, &_e_linux_dmabuf_buffer_addon_impl);
342    dmabuf_buffer->ds_buffer = ds_buffer;
343
344    return tsurface;
345
346 failed:
347    for (i = 0; i < info.num_planes; i++)
348      {
349         if (bos[i])
350            tbm_bo_unref(bos[i]);
351      }
352
353    if (tsurface)
354       tbm_surface_destroy(tsurface);
355
356    return NULL;
357 }