1 /*
2  * Copyright © 2013 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include "dri3_priv.h"
24 #include <syncsdk.h>
25 #include <misync.h>
26 #include <misyncshm.h>
27 #include <randrstr.h>
28 #include <drm_fourcc.h>
29 #include <unistd.h>
30 
31 int
dri3_open(ClientPtr client,ScreenPtr screen,RRProviderPtr provider,int * fd)32 dri3_open(ClientPtr client, ScreenPtr screen, RRProviderPtr provider, int *fd)
33 {
34     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
35     const dri3_screen_info_rec *info = ds->info;
36 
37     if (info == NULL)
38         return BadMatch;
39 
40     if (info->version >= 1 && info->open_client != NULL)
41         return (*info->open_client) (client, screen, provider, fd);
42     if (info->open != NULL)
43         return (*info->open) (screen, provider, fd);
44 
45     return BadMatch;
46 }
47 
48 int
dri3_pixmap_from_fds(PixmapPtr * ppixmap,ScreenPtr screen,CARD8 num_fds,const int * fds,CARD16 width,CARD16 height,const CARD32 * strides,const CARD32 * offsets,CARD8 depth,CARD8 bpp,CARD64 modifier)49 dri3_pixmap_from_fds(PixmapPtr *ppixmap, ScreenPtr screen,
50                      CARD8 num_fds, const int *fds,
51                      CARD16 width, CARD16 height,
52                      const CARD32 *strides, const CARD32 *offsets,
53                      CARD8 depth, CARD8 bpp, CARD64 modifier)
54 {
55     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
56     const dri3_screen_info_rec *info = ds->info;
57     PixmapPtr                   pixmap;
58 
59     if (!info)
60         return BadImplementation;
61 
62     if (info->version >= 2 && info->pixmap_from_fds != NULL) {
63         pixmap = (*info->pixmap_from_fds) (screen, num_fds, fds, width, height,
64                                            strides, offsets, depth, bpp, modifier);
65     } else if (info->pixmap_from_fd != NULL && num_fds == 1) {
66         pixmap = (*info->pixmap_from_fd) (screen, fds[0], width, height,
67                                           strides[0], depth, bpp);
68     } else {
69         return BadImplementation;
70     }
71 
72     if (!pixmap)
73         return BadAlloc;
74 
75     *ppixmap = pixmap;
76     return Success;
77 }
78 
79 int
dri3_fds_from_pixmap(PixmapPtr pixmap,int * fds,uint32_t * strides,uint32_t * offsets,uint64_t * modifier)80 dri3_fds_from_pixmap(PixmapPtr pixmap, int *fds,
81                      uint32_t *strides, uint32_t *offsets,
82                      uint64_t *modifier)
83 {
84     ScreenPtr                   screen = pixmap->drawable.pScreen;
85     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
86     const dri3_screen_info_rec *info = ds->info;
87 
88     if (!info)
89         return 0;
90 
91     if (info->version >= 2 && info->fds_from_pixmap != NULL) {
92         return (*info->fds_from_pixmap)(screen, pixmap, fds, strides, offsets,
93                                         modifier);
94     } else if (info->fd_from_pixmap != NULL) {
95         CARD16 stride;
96         CARD32 size;
97 
98         fds[0] = (*info->fd_from_pixmap)(screen, pixmap, &stride, &size);
99         if (fds[0] < 0)
100             return 0;
101 
102         strides[0] = stride;
103         offsets[0] = 0;
104         *modifier = DRM_FORMAT_MOD_INVALID;
105         return 1;
106     } else {
107         return 0;
108     }
109 }
110 
111 int
dri3_fd_from_pixmap(PixmapPtr pixmap,CARD16 * stride,CARD32 * size)112 dri3_fd_from_pixmap(PixmapPtr pixmap, CARD16 *stride, CARD32 *size)
113 {
114     ScreenPtr                   screen = pixmap->drawable.pScreen;
115     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
116     const dri3_screen_info_rec  *info = ds->info;
117     uint32_t                    strides[4];
118     uint32_t                    offsets[4];
119     uint64_t                    modifier;
120     int                         fds[4];
121     int                         num_fds;
122 
123     if (!info)
124         return -1;
125 
126     /* Preferentially use the old interface, allowing the implementation to
127      * ensure the buffer is in a single-plane format which doesn't need
128      * modifiers. */
129     if (info->fd_from_pixmap != NULL)
130         return (*info->fd_from_pixmap)(screen, pixmap, stride, size);
131 
132     if (info->version < 2 || info->fds_from_pixmap == NULL)
133         return -1;
134 
135     /* If using the new interface, make sure that it's a single plane starting
136      * at 0 within the BO. We don't check the modifier, as the client may
137      * have an auxiliary mechanism for determining the modifier itself. */
138     num_fds = info->fds_from_pixmap(screen, pixmap, fds, strides, offsets,
139                                     &modifier);
140     if (num_fds != 1 || offsets[0] != 0) {
141         int i;
142         for (i = 0; i < num_fds; i++)
143             close(fds[i]);
144         return -1;
145     }
146 
147     *stride = strides[0];
148     *size = size[0];
149     return fds[0];
150 }
151 
152 static int
cache_formats_and_modifiers(ScreenPtr screen)153 cache_formats_and_modifiers(ScreenPtr screen)
154 {
155     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
156     const dri3_screen_info_rec *info = ds->info;
157     CARD32                      num_formats;
158     CARD32                     *formats;
159     uint32_t                    num_modifiers;
160     uint64_t                   *modifiers;
161     int                         i;
162 
163     if (ds->formats_cached)
164         return Success;
165 
166     if (!info)
167         return BadImplementation;
168 
169     if (info->version < 2 || !info->get_formats || !info->get_modifiers) {
170         ds->formats = NULL;
171         ds->num_formats = 0;
172         ds->formats_cached = TRUE;
173         return Success;
174     }
175 
176     if (!info->get_formats(screen, &num_formats, &formats))
177         return BadAlloc;
178 
179     if (!num_formats) {
180         ds->num_formats = 0;
181         ds->formats_cached = TRUE;
182         return Success;
183     }
184 
185     ds->formats = calloc(num_formats, sizeof(dri3_dmabuf_format_rec));
186     if (!ds->formats)
187         return BadAlloc;
188 
189     for (i = 0; i < num_formats; i++) {
190         dri3_dmabuf_format_ptr iter = &ds->formats[i];
191 
192         if (!info->get_modifiers(screen, formats[i],
193                                  &num_modifiers,
194                                  &modifiers))
195             continue;
196 
197         if (!num_modifiers)
198             continue;
199 
200         iter->format = formats[i];
201         iter->num_modifiers = num_modifiers;
202         iter->modifiers = modifiers;
203     }
204 
205     ds->num_formats = i;
206     ds->formats_cached = TRUE;
207 
208     return Success;
209 }
210 
211 int
dri3_get_supported_modifiers(ScreenPtr screen,DrawablePtr drawable,CARD8 depth,CARD8 bpp,CARD32 * num_intersect_modifiers,CARD64 ** intersect_modifiers,CARD32 * num_screen_modifiers,CARD64 ** screen_modifiers)212 dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
213                              CARD8 depth, CARD8 bpp,
214                              CARD32 *num_intersect_modifiers,
215                              CARD64 **intersect_modifiers,
216                              CARD32 *num_screen_modifiers,
217                              CARD64 **screen_modifiers)
218 {
219     dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
220     const dri3_screen_info_rec *info = ds->info;
221     int                         i, j;
222     int                         ret;
223     uint32_t                    num_drawable_mods;
224     uint64_t                   *drawable_mods;
225     CARD64                     *intersect_mods = NULL;
226     CARD64                     *screen_mods = NULL;
227     CARD32                      format;
228     dri3_dmabuf_format_ptr      screen_format = NULL;
229 
230     ret = cache_formats_and_modifiers(screen);
231     if (ret != Success)
232         return ret;
233 
234     format = drm_format_for_depth(depth, bpp);
235     if (format == 0)
236         return BadValue;
237 
238     /* Find screen-global modifiers from cache
239      */
240     for (i = 0; i < ds->num_formats; i++) {
241         if (ds->formats[i].format == format) {
242             screen_format = &ds->formats[i];
243             break;
244         }
245     }
246     if (screen_format == NULL)
247         return BadMatch;
248 
249     if (screen_format->num_modifiers == 0) {
250         *num_screen_modifiers = 0;
251         *num_intersect_modifiers = 0;
252         return Success;
253     }
254 
255     if (!info->get_drawable_modifiers ||
256         !info->get_drawable_modifiers(drawable, format,
257                                       &num_drawable_mods,
258                                       &drawable_mods)) {
259         num_drawable_mods = 0;
260         drawable_mods = NULL;
261     }
262 
263     /* We're allocating slightly more memory than necessary but it reduces
264      * the complexity of finding the intersection set.
265      */
266     screen_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
267     if (!screen_mods)
268         return BadAlloc;
269     if (num_drawable_mods > 0) {
270         intersect_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
271         if (!intersect_mods) {
272             free(screen_mods);
273             return BadAlloc;
274         }
275     }
276 
277     *num_screen_modifiers = 0;
278     *num_intersect_modifiers = 0;
279     for (i = 0; i < screen_format->num_modifiers; i++) {
280         CARD64 modifier = screen_format->modifiers[i];
281         Bool intersect = FALSE;
282 
283         for (j = 0; j < num_drawable_mods; j++) {
284             if (drawable_mods[j] == modifier) {
285                 intersect = TRUE;
286                 break;
287             }
288         }
289 
290         if (intersect) {
291             intersect_mods[*num_intersect_modifiers] = modifier;
292             *num_intersect_modifiers += 1;
293         } else {
294             screen_mods[*num_screen_modifiers] = modifier;
295             *num_screen_modifiers += 1;
296         }
297     }
298 
299     assert(*num_intersect_modifiers + *num_screen_modifiers == screen_format->num_modifiers);
300 
301     *intersect_modifiers = intersect_mods;
302     *screen_modifiers = screen_mods;
303     free(drawable_mods);
304 
305     return Success;
306 }
307