1/**************************************************************************
2 *
3 * Copyright 2009, VMware, Inc.
4 * All Rights Reserved.
5 * Copyright 2010 George Sapountzis <gsapountzis@gmail.com>
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#if !defined(ANDROID) || ANDROID_API_LEVEL >= 26
30/* Android's libc began supporting shm in Oreo */
31#define HAVE_SHM
32#include <sys/ipc.h>
33#include <sys/shm.h>
34#endif
35
36#include "pipe/p_compiler.h"
37#include "pipe/p_format.h"
38#include "pipe/p_state.h"
39#include "util/u_inlines.h"
40#include "util/u_format.h"
41#include "util/u_math.h"
42#include "util/u_memory.h"
43
44#include "state_tracker/sw_winsys.h"
45#include "dri_sw_winsys.h"
46
47
48struct dri_sw_displaytarget
49{
50   enum pipe_format format;
51   unsigned width;
52   unsigned height;
53   unsigned stride;
54
55   unsigned map_flags;
56   int shmid;
57   void *data;
58   void *mapped;
59   const void *front_private;
60};
61
62struct dri_sw_winsys
63{
64   struct sw_winsys base;
65
66   const struct drisw_loader_funcs *lf;
67};
68
69static inline struct dri_sw_displaytarget *
70dri_sw_displaytarget( struct sw_displaytarget *dt )
71{
72   return (struct dri_sw_displaytarget *)dt;
73}
74
75static inline struct dri_sw_winsys *
76dri_sw_winsys( struct sw_winsys *ws )
77{
78   return (struct dri_sw_winsys *)ws;
79}
80
81
82static boolean
83dri_sw_is_displaytarget_format_supported( struct sw_winsys *ws,
84                                          unsigned tex_usage,
85                                          enum pipe_format format )
86{
87   /* TODO: check visuals or other sensible thing here */
88   return TRUE;
89}
90
91#ifdef HAVE_SHM
92static char *
93alloc_shm(struct dri_sw_displaytarget *dri_sw_dt, unsigned size)
94{
95   char *addr;
96
97   /* 0600 = user read+write */
98   dri_sw_dt->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
99   if (dri_sw_dt->shmid < 0)
100      return NULL;
101
102   addr = (char *) shmat(dri_sw_dt->shmid, 0, 0);
103   /* mark the segment immediately for deletion to avoid leaks */
104   shmctl(dri_sw_dt->shmid, IPC_RMID, 0);
105
106   if (addr == (char *) -1)
107      return NULL;
108
109   return addr;
110}
111#endif
112
113static struct sw_displaytarget *
114dri_sw_displaytarget_create(struct sw_winsys *winsys,
115                            unsigned tex_usage,
116                            enum pipe_format format,
117                            unsigned width, unsigned height,
118                            unsigned alignment,
119                            const void *front_private,
120                            unsigned *stride)
121{
122   struct dri_sw_winsys *ws = dri_sw_winsys(winsys);
123   struct dri_sw_displaytarget *dri_sw_dt;
124   unsigned nblocksy, size, format_stride;
125
126   dri_sw_dt = CALLOC_STRUCT(dri_sw_displaytarget);
127   if(!dri_sw_dt)
128      goto no_dt;
129
130   dri_sw_dt->format = format;
131   dri_sw_dt->width = width;
132   dri_sw_dt->height = height;
133   dri_sw_dt->front_private = front_private;
134
135   format_stride = util_format_get_stride(format, width);
136   dri_sw_dt->stride = align(format_stride, alignment);
137
138   nblocksy = util_format_get_nblocksy(format, height);
139   size = dri_sw_dt->stride * nblocksy;
140
141   dri_sw_dt->shmid = -1;
142
143#ifdef HAVE_SHM
144   if (ws->lf->put_image_shm)
145      dri_sw_dt->data = alloc_shm(dri_sw_dt, size);
146#endif
147
148   if(!dri_sw_dt->data)
149      dri_sw_dt->data = align_malloc(size, alignment);
150
151   if(!dri_sw_dt->data)
152      goto no_data;
153
154   *stride = dri_sw_dt->stride;
155   return (struct sw_displaytarget *)dri_sw_dt;
156
157no_data:
158   FREE(dri_sw_dt);
159no_dt:
160   return NULL;
161}
162
163static void
164dri_sw_displaytarget_destroy(struct sw_winsys *ws,
165                             struct sw_displaytarget *dt)
166{
167   struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
168
169   if (dri_sw_dt->shmid >= 0) {
170#ifdef HAVE_SHM
171      shmdt(dri_sw_dt->data);
172      shmctl(dri_sw_dt->shmid, IPC_RMID, 0);
173#endif
174   } else {
175      align_free(dri_sw_dt->data);
176   }
177
178   FREE(dri_sw_dt);
179}
180
181static void *
182dri_sw_displaytarget_map(struct sw_winsys *ws,
183                         struct sw_displaytarget *dt,
184                         unsigned flags)
185{
186   struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
187   dri_sw_dt->mapped = dri_sw_dt->data;
188
189   if (dri_sw_dt->front_private && (flags & PIPE_TRANSFER_READ)) {
190      struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
191      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);
192   }
193   dri_sw_dt->map_flags = flags;
194   return dri_sw_dt->mapped;
195}
196
197static void
198dri_sw_displaytarget_unmap(struct sw_winsys *ws,
199                           struct sw_displaytarget *dt)
200{
201   struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
202   if (dri_sw_dt->front_private && (dri_sw_dt->map_flags & PIPE_TRANSFER_WRITE)) {
203      struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
204      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);
205   }
206   dri_sw_dt->map_flags = 0;
207   dri_sw_dt->mapped = NULL;
208}
209
210static struct sw_displaytarget *
211dri_sw_displaytarget_from_handle(struct sw_winsys *winsys,
212                                 const struct pipe_resource *templ,
213                                 struct winsys_handle *whandle,
214                                 unsigned *stride)
215{
216   assert(0);
217   return NULL;
218}
219
220static boolean
221dri_sw_displaytarget_get_handle(struct sw_winsys *winsys,
222                                struct sw_displaytarget *dt,
223                                struct winsys_handle *whandle)
224{
225   struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
226
227   if (whandle->type == WINSYS_HANDLE_TYPE_SHMID) {
228      if (dri_sw_dt->shmid < 0)
229         return FALSE;
230      whandle->handle = dri_sw_dt->shmid;
231      return TRUE;
232   }
233
234   return FALSE;
235}
236
237static void
238dri_sw_displaytarget_display(struct sw_winsys *ws,
239                             struct sw_displaytarget *dt,
240                             void *context_private,
241                             struct pipe_box *box)
242{
243   struct dri_sw_winsys *dri_sw_ws = dri_sw_winsys(ws);
244   struct dri_sw_displaytarget *dri_sw_dt = dri_sw_displaytarget(dt);
245   struct dri_drawable *dri_drawable = (struct dri_drawable *)context_private;
246   unsigned width, height, x = 0, y = 0;
247   unsigned blsize = util_format_get_blocksize(dri_sw_dt->format);
248   unsigned offset = 0;
249   unsigned offset_x = 0;
250   char *data = dri_sw_dt->data;
251   bool is_shm = dri_sw_dt->shmid != -1;
252   /* Set the width to 'stride / cpp'.
253    *
254    * PutImage correctly clips to the width of the dst drawable.
255    */
256   if (box) {
257      offset = dri_sw_dt->stride * box->y;
258      offset_x = box->x * blsize;
259      data += offset;
260      /* don't add x offset for shm, the put_image_shm will deal with it */
261      if (!is_shm)
262         data += offset_x;
263      x = box->x;
264      y = box->y;
265      width = box->width;
266      height = box->height;
267   } else {
268      width = dri_sw_dt->stride / blsize;
269      height = dri_sw_dt->height;
270   }
271
272   if (is_shm) {
273      dri_sw_ws->lf->put_image_shm(dri_drawable, dri_sw_dt->shmid, dri_sw_dt->data, offset, offset_x,
274                                   x, y, width, height, dri_sw_dt->stride);
275      return;
276   }
277
278   if (box)
279      dri_sw_ws->lf->put_image2(dri_drawable, data,
280                                x, y, width, height, dri_sw_dt->stride);
281   else
282      dri_sw_ws->lf->put_image(dri_drawable, data, width, height);
283}
284
285static void
286dri_destroy_sw_winsys(struct sw_winsys *winsys)
287{
288   FREE(winsys);
289}
290
291struct sw_winsys *
292dri_create_sw_winsys(const struct drisw_loader_funcs *lf)
293{
294   struct dri_sw_winsys *ws;
295
296   ws = CALLOC_STRUCT(dri_sw_winsys);
297   if (!ws)
298      return NULL;
299
300   ws->lf = lf;
301   ws->base.destroy = dri_destroy_sw_winsys;
302
303   ws->base.is_displaytarget_format_supported = dri_sw_is_displaytarget_format_supported;
304
305   /* screen texture functions */
306   ws->base.displaytarget_create = dri_sw_displaytarget_create;
307   ws->base.displaytarget_destroy = dri_sw_displaytarget_destroy;
308   ws->base.displaytarget_from_handle = dri_sw_displaytarget_from_handle;
309   ws->base.displaytarget_get_handle = dri_sw_displaytarget_get_handle;
310
311   /* texture functions */
312   ws->base.displaytarget_map = dri_sw_displaytarget_map;
313   ws->base.displaytarget_unmap = dri_sw_displaytarget_unmap;
314
315   ws->base.displaytarget_display = dri_sw_displaytarget_display;
316
317   return &ws->base;
318}
319
320/* vim: set sw=3 ts=8 sts=3 expandtab: */
321