1 /**************************************************************************
2 *
3 * Copyright 2009, VMware, Inc.
4 * All Rights Reserved.
5 * Copyright 2010 George Sapountzis <[email protected]>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29 #ifdef HAVE_SYS_SHM_H
30 #include <sys/ipc.h>
31 #include <sys/shm.h>
32 #ifdef __FreeBSD__
33 /* sys/ipc.h -> sys/_types.h -> machine/param.h
34 * - defines ALIGN which clashes with our ALIGN
35 */
36 #undef ALIGN
37 #endif
38 #endif
39
40 #include "util/compiler.h"
41 #include "util/format/u_formats.h"
42 #include "util/detect_os.h"
43
44 #if DETECT_OS_POSIX
45 # include <sys/stat.h>
46 # include <errno.h>
47 # include <sys/mman.h>
48 #endif
49
50 #include "pipe/p_state.h"
51 #include "util/u_inlines.h"
52 #include "util/format/u_format.h"
53 #include "util/u_math.h"
54 #include "util/u_memory.h"
55 #include "util/os_file.h"
56
57 #include "frontend/sw_winsys.h"
58 #include "dri_sw_winsys.h"
59
60
61 struct dri_sw_displaytarget
62 {
63 enum pipe_format format;
64 unsigned width;
65 unsigned height;
66 unsigned stride;
67
68 unsigned map_flags;
69 int shmid;
70 void *data;
71 void *mapped;
72 const void *front_private;
73 /* dmabuf */
74 int fd;
75 int offset;
76 size_t size;
77 bool unbacked;
78 };
79
80 struct dri_sw_winsys
81 {
82 struct sw_winsys base;
83
84 const struct drisw_loader_funcs *lf;
85 };
86
87 static inline struct dri_sw_displaytarget *
dri_sw_displaytarget(struct sw_displaytarget * dt)88 dri_sw_displaytarget( struct sw_displaytarget *dt )
89 {
90 return (struct dri_sw_displaytarget *)dt;
91 }
92
93 static inline struct dri_sw_winsys *
dri_sw_winsys(struct sw_winsys * ws)94 dri_sw_winsys( struct sw_winsys *ws )
95 {
96 return (struct dri_sw_winsys *)ws;
97 }
98
99
100 static bool
dri_sw_is_displaytarget_format_supported(struct sw_winsys * ws,unsigned tex_usage,enum pipe_format format)101 dri_sw_is_displaytarget_format_supported( struct sw_winsys *ws,
102 unsigned tex_usage,
103 enum pipe_format format )
104 {
105 /* TODO: check visuals or other sensible thing here */
106 return true;
107 }
108
109 #ifdef HAVE_SYS_SHM_H
110 static char *
alloc_shm(struct dri_sw_displaytarget * dri_sw_dt,unsigned size)111 alloc_shm(struct dri_sw_displaytarget *dri_sw_dt, unsigned size)
112 {
113 char *addr;
114
115 /* 0600 = user read+write */
116 dri_sw_dt->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
117 if (dri_sw_dt->shmid < 0)
118 return NULL;
119
120 addr = (char *) shmat(dri_sw_dt->shmid, NULL, 0);
121 /* mark the segment immediately for deletion to avoid leaks */
122 shmctl(dri_sw_dt->shmid, IPC_RMID, NULL);
123
124 if (addr == (char *) -1)
125 return NULL;
126
127 return addr;
128 }
129 #endif
130
131 static struct sw_displaytarget *
dri_sw_displaytarget_create(struct sw_winsys * winsys,unsigned tex_usage,enum pipe_format format,unsigned width,unsigned height,unsigned alignment,const void * front_private,unsigned * stride)132 dri_sw_displaytarget_create(struct sw_winsys *winsys,
133 unsigned tex_usage,
134 enum pipe_format format,
135 unsigned width, unsigned height,
136 unsigned alignment,
137 const void *front_private,
138 unsigned *stride)
139 {
140 UNUSED struct dri_sw_winsys *ws = dri_sw_winsys(winsys);
141 struct dri_sw_displaytarget *dri_sw_dt;
142 unsigned nblocksy, size, format_stride;
143
144 dri_sw_dt = CALLOC_STRUCT(dri_sw_displaytarget);
145 if(!dri_sw_dt)
146 goto no_dt;
147
148 dri_sw_dt->format = format;
149 dri_sw_dt->width = width;
150 dri_sw_dt->height = height;
151 dri_sw_dt->front_private = front_private;
152
153 format_stride = util_format_get_stride(format, width);
154 dri_sw_dt->stride = align(format_stride, alignment);
155
156 nblocksy = util_format_get_nblocksy(format, height);
157 size = dri_sw_dt->stride * nblocksy;
158 dri_sw_dt->size = size;
159
160 dri_sw_dt->shmid = -1;
161 dri_sw_dt->fd = -1;
162
163 #ifdef HAVE_SYS_SHM_H
164 if (ws->lf->put_image_shm)
165 dri_sw_dt->data = alloc_shm(dri_sw_dt, size);
166 #endif
167
168 if(!dri_sw_dt->data)
169 dri_sw_dt->data = align_malloc(size, alignment);
170
171 if(!dri_sw_dt->data)
172 goto no_data;
173
174 *stride = dri_sw_dt->stride;
175 return (struct sw_displaytarget *)dri_sw_dt;
176
177 no_data:
178 FREE(dri_sw_dt);
179 no_dt:
180 return NULL;
181 }
182
183 static struct sw_displaytarget *
dri_sw_displaytarget_create_mapped(struct sw_winsys * winsys,unsigned tex_usage,enum pipe_format format,unsigned width,unsigned height,unsigned stride,void * data)184 dri_sw_displaytarget_create_mapped(struct sw_winsys *winsys,
185 unsigned tex_usage,
186 enum pipe_format format,
187 unsigned width, unsigned height,
188 unsigned stride,
189 void *data)
190 {
191 UNUSED struct dri_sw_winsys *ws = dri_sw_winsys(winsys);
192 struct dri_sw_displaytarget *dri_sw_dt;
193 unsigned nblocksy, size;
194
195 dri_sw_dt = CALLOC_STRUCT(dri_sw_displaytarget);
196 if(!dri_sw_dt)
197 return NULL;
198
199 dri_sw_dt->format = format;
200 dri_sw_dt->width = width;
201 dri_sw_dt->height = height;
202
203 dri_sw_dt->stride = stride;
204
205 nblocksy = util_format_get_nblocksy(format, height);
206 size = dri_sw_dt->stride * nblocksy;
207 dri_sw_dt->size = size;
208
209 dri_sw_dt->shmid = -1;
210 dri_sw_dt->fd = -1;
211 dri_sw_dt->unbacked = true;
212
213 dri_sw_dt->data = data;
214 dri_sw_dt->mapped = data;
215
216 return (struct sw_displaytarget *)dri_sw_dt;
217 }
218
219 static void
dri_sw_displaytarget_destroy(struct sw_winsys * ws,struct sw_displaytarget * dt)220 dri_sw_displaytarget_destroy(struct sw_winsys *ws,
221 struct sw_displaytarget *dt)
222 {
223 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
224
225 if (dri_sw_dt->unbacked) {}
226 else if (dri_sw_dt->fd >= 0) {
227 if (dri_sw_dt->mapped)
228 ws->displaytarget_unmap(ws, dt);
229 close(dri_sw_dt->fd);
230 } else if (dri_sw_dt->shmid >= 0) {
231 #ifdef HAVE_SYS_SHM_H
232 shmdt(dri_sw_dt->data);
233 shmctl(dri_sw_dt->shmid, IPC_RMID, NULL);
234 #endif
235 } else {
236 align_free(dri_sw_dt->data);
237 }
238
239 FREE(dri_sw_dt);
240 }
241
242 static void *
dri_sw_displaytarget_map(struct sw_winsys * ws,struct sw_displaytarget * dt,unsigned flags)243 dri_sw_displaytarget_map(struct sw_winsys *ws,
244 struct sw_displaytarget *dt,
245 unsigned flags)
246 {
247 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
248 dri_sw_dt->map_flags = flags;
249 if (dri_sw_dt->unbacked)
250 return dri_sw_dt->mapped;
251 #if DETECT_OS_POSIX
252 if (dri_sw_dt->fd > -1) {
253 bool success = false;
254 if (!success) {
255 /* if this fails, it's a dmabuf that wasn't exported by us,
256 * so it doesn't have the header that we're looking for
257 */
258 off_t size = lseek(dri_sw_dt->fd, 0, SEEK_END);
259 lseek(dri_sw_dt->fd, 0, SEEK_SET);
260 if (size < 1) {
261 fprintf(stderr, "dmabuf import failed: fd has no data\n");
262 return NULL;
263 }
264 unsigned prot = 0;
265 if (flags & PIPE_MAP_READ)
266 prot |= PROT_READ;
267 if (flags & PIPE_MAP_WRITE)
268 prot |= PROT_WRITE;
269 dri_sw_dt->size = size;
270 dri_sw_dt->data = mmap(NULL, dri_sw_dt->size, prot, MAP_SHARED, dri_sw_dt->fd, 0);
271 if (dri_sw_dt->data == MAP_FAILED) {
272 dri_sw_dt->data = NULL;
273 fprintf(stderr, "dmabuf import failed to mmap: %s\n", strerror(errno));
274 } else
275 dri_sw_dt->mapped = ((uint8_t*)dri_sw_dt->data) + dri_sw_dt->offset;
276 } else
277 dri_sw_dt->mapped = ((uint8_t*)dri_sw_dt->data) + dri_sw_dt->offset;
278 } else
279 #endif
280 if (dri_sw_dt->front_private && (flags & PIPE_MAP_READ)) {
281 struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
282 dri_sw_ws->lf->get_image((void *)dri_sw_dt->front_private, 0, 0, dri_sw_dt->width, dri_sw_dt->height, dri_sw_dt->stride, dri_sw_dt->data);
283 dri_sw_dt->mapped = dri_sw_dt->data;
284 } else {
285 dri_sw_dt->mapped = dri_sw_dt->data;
286 }
287 return dri_sw_dt->mapped;
288 }
289
290 static void
dri_sw_displaytarget_unmap(struct sw_winsys * ws,struct sw_displaytarget * dt)291 dri_sw_displaytarget_unmap(struct sw_winsys *ws,
292 struct sw_displaytarget *dt)
293 {
294 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
295
296 if (dri_sw_dt->unbacked) {
297 dri_sw_dt->map_flags = 0;
298 return;
299 }
300 #if DETECT_OS_POSIX
301 if (dri_sw_dt->fd > -1) {
302 munmap(dri_sw_dt->data, dri_sw_dt->size);
303 dri_sw_dt->data = NULL;
304 } else
305 #endif
306 if (dri_sw_dt->front_private && (dri_sw_dt->map_flags & PIPE_MAP_WRITE)) {
307 struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
308 dri_sw_ws->lf->put_image2((void *)dri_sw_dt->front_private, dri_sw_dt->data, 0, 0, dri_sw_dt->width, dri_sw_dt->height, dri_sw_dt->stride);
309 }
310 dri_sw_dt->map_flags = 0;
311 dri_sw_dt->mapped = NULL;
312 }
313
314 static struct sw_displaytarget *
dri_sw_displaytarget_from_handle(struct sw_winsys * winsys,const struct pipe_resource * templ,struct winsys_handle * whandle,unsigned * stride)315 dri_sw_displaytarget_from_handle(struct sw_winsys *winsys,
316 const struct pipe_resource *templ,
317 struct winsys_handle *whandle,
318 unsigned *stride)
319 {
320 #if DETECT_OS_POSIX
321 int fd = os_dupfd_cloexec(whandle->handle);
322 struct sw_displaytarget *sw = dri_sw_displaytarget_create(winsys, templ->usage, templ->format, templ->width0, templ->height0, 64, NULL, stride);
323 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(sw);
324 dri_sw_dt->fd = fd;
325 dri_sw_dt->offset = whandle->offset;
326 return sw;
327 #else
328 assert(0);
329 return NULL;
330 #endif
331 }
332
333 static bool
dri_sw_displaytarget_get_handle(struct sw_winsys * winsys,struct sw_displaytarget * dt,struct winsys_handle * whandle)334 dri_sw_displaytarget_get_handle(struct sw_winsys *winsys,
335 struct sw_displaytarget *dt,
336 struct winsys_handle *whandle)
337 {
338 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
339
340 if (whandle->type == WINSYS_HANDLE_TYPE_SHMID) {
341 if (dri_sw_dt->shmid < 0)
342 return false;
343 whandle->handle = dri_sw_dt->shmid;
344 return true;
345 }
346
347 return false;
348 }
349
350 static void
dri_sw_displaytarget_display(struct sw_winsys * ws,struct sw_displaytarget * dt,void * context_private,unsigned nboxes,struct pipe_box * box)351 dri_sw_displaytarget_display(struct sw_winsys *ws,
352 struct sw_displaytarget *dt,
353 void *context_private,
354 unsigned nboxes,
355 struct pipe_box *box)
356 {
357 struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
358 struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
359 struct dri_drawable *dri_drawable = (struct dri_drawable *)context_private;
360 unsigned width, height;
361 unsigned blsize = util_format_get_blocksize(dri_sw_dt->format);
362 bool is_shm = dri_sw_dt->shmid != -1;
363 /* Set the width to 'stride / cpp'.
364 *
365 * PutImage correctly clips to the width of the dst drawable.
366 */
367 width = dri_sw_dt->stride / blsize;
368 height = dri_sw_dt->height;
369 if (is_shm)
370 dri_sw_ws->lf->put_image_shm(dri_drawable, dri_sw_dt->shmid, dri_sw_dt->data, 0, 0,
371 0, 0, width, height, dri_sw_dt->stride);
372 else
373 dri_sw_ws->lf->put_image(dri_drawable, dri_sw_dt->data, width, height);
374 return;
375 }
376
377 static void
dri_destroy_sw_winsys(struct sw_winsys * winsys)378 dri_destroy_sw_winsys(struct sw_winsys *winsys)
379 {
380 FREE(winsys);
381 }
382
383 struct sw_winsys *
dri_create_sw_winsys(const struct drisw_loader_funcs * lf)384 dri_create_sw_winsys(const struct drisw_loader_funcs *lf)
385 {
386 struct dri_sw_winsys *ws;
387
388 ws = CALLOC_STRUCT(dri_sw_winsys);
389 if (!ws)
390 return NULL;
391
392 ws->lf = lf;
393 ws->base.destroy = dri_destroy_sw_winsys;
394
395 ws->base.is_displaytarget_format_supported = dri_sw_is_displaytarget_format_supported;
396
397 /* screen texture functions */
398 ws->base.displaytarget_create = dri_sw_displaytarget_create;
399 ws->base.displaytarget_create_mapped = dri_sw_displaytarget_create_mapped;
400 ws->base.displaytarget_destroy = dri_sw_displaytarget_destroy;
401 ws->base.displaytarget_from_handle = dri_sw_displaytarget_from_handle;
402 ws->base.displaytarget_get_handle = dri_sw_displaytarget_get_handle;
403
404 /* texture functions */
405 ws->base.displaytarget_map = dri_sw_displaytarget_map;
406 ws->base.displaytarget_unmap = dri_sw_displaytarget_unmap;
407
408 ws->base.displaytarget_display = dri_sw_displaytarget_display;
409
410 return &ws->base;
411 }
412
413 /* vim: set sw=3 ts=8 sts=3 expandtab: */
414