xref: /OK3568_Linux_fs/external/xserver/glamor/glamor_prepare.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright © 2014 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 "glamor_priv.h"
24 #include "glamor_prepare.h"
25 #include "glamor_transfer.h"
26 
27 /*
28  * Make a pixmap ready to draw with fb by
29  * creating a PBO large enough for the whole object
30  * and downloading all of the FBOs into it.
31  */
32 
33 static Bool
glamor_prep_pixmap_box(PixmapPtr pixmap,glamor_access_t access,BoxPtr box)34 glamor_prep_pixmap_box(PixmapPtr pixmap, glamor_access_t access, BoxPtr box)
35 {
36     ScreenPtr                   screen = pixmap->drawable.pScreen;
37     glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
38     glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
39     int                         gl_access, gl_usage;
40     RegionRec                   region;
41 
42     if (priv->type == GLAMOR_DRM_ONLY)
43         return FALSE;
44 
45     if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
46         return TRUE;
47 
48     glamor_make_current(glamor_priv);
49 
50     RegionInit(&region, box, 1);
51 
52     /* See if it's already mapped */
53     if (pixmap->devPrivate.ptr) {
54         /*
55          * Someone else has mapped this pixmap;
56          * we'll assume that it's directly mapped
57          * by a lower level driver
58          */
59         if (!priv->prepared)
60             goto done;
61 
62         /* In X, multiple Drawables can be stored in the same Pixmap (such as
63          * each individual window in a non-composited screen pixmap, or the
64          * reparented window contents inside the window-manager-decorated window
65          * pixmap on a composited screen).
66          *
67          * As a result, when doing a series of mappings for a fallback, we may
68          * need to add more boxes to the set of data we've downloaded, as we go.
69          */
70         RegionSubtract(&region, &region, &priv->prepare_region);
71         if (!RegionNotEmpty(&region))
72             goto done;
73 
74         if (access == GLAMOR_ACCESS_RW)
75             FatalError("attempt to remap buffer as writable");
76 
77         if (priv->pbo) {
78             glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo);
79             glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
80             pixmap->devPrivate.ptr = NULL;
81         }
82     } else {
83 #ifdef GLAMOR_HAS_GBM_MAP
84         struct gbm_bo *gbm = NULL;
85         uint32_t stride;
86 
87         RegionInit(&priv->prepare_region, box, 1);
88 
89         if (!priv->exporting)
90             gbm = glamor_gbm_bo_from_pixmap(screen, pixmap);
91 
92         if (gbm) {
93             pixmap->devPrivate.ptr =
94                 gbm_bo_map(gbm, 0, 0, pixmap->drawable.width,
95                            pixmap->drawable.height,
96                            (access == GLAMOR_ACCESS_RW) ?
97                            GBM_BO_TRANSFER_READ_WRITE : GBM_BO_TRANSFER_READ,
98                            &stride, &priv->map_data);
99 
100             if (pixmap->devPrivate.ptr) {
101                 pixmap->devKind = stride;
102                 priv->bo_mapped = TRUE;
103                 priv->map_access = access;
104                 goto done;
105             }
106         }
107 #endif
108 
109         if (glamor_priv->has_rw_pbo) {
110             if (priv->pbo == 0)
111                 glGenBuffers(1, &priv->pbo);
112 
113             gl_usage = GL_STREAM_READ;
114 
115             glamor_priv->suppress_gl_out_of_memory_logging = true;
116 
117             glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo);
118             glBufferData(GL_PIXEL_PACK_BUFFER,
119                          pixmap->devKind * pixmap->drawable.height, NULL,
120                          gl_usage);
121 
122             glamor_priv->suppress_gl_out_of_memory_logging = false;
123 
124             if (glGetError() == GL_OUT_OF_MEMORY) {
125                 if (!glamor_priv->logged_any_pbo_allocation_failure) {
126                     LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %d "
127                                    "bytes PBO due to GL_OUT_OF_MEMORY.\n",
128                                    pixmap->devKind * pixmap->drawable.height);
129                     glamor_priv->logged_any_pbo_allocation_failure = true;
130                 }
131                 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
132                 glDeleteBuffers(1, &priv->pbo);
133                 priv->pbo = 0;
134             }
135         }
136 
137         if (!priv->pbo) {
138             pixmap->devPrivate.ptr = xallocarray(pixmap->devKind,
139                                                  pixmap->drawable.height);
140             if (!pixmap->devPrivate.ptr)
141                 return FALSE;
142         }
143         priv->map_access = access;
144     }
145 
146     glamor_download_boxes(pixmap, RegionRects(&region), RegionNumRects(&region),
147                           0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
148 
149     if (priv->pbo) {
150         if (priv->map_access == GLAMOR_ACCESS_RW)
151             gl_access = GL_READ_WRITE;
152         else
153             gl_access = GL_READ_ONLY;
154 
155         pixmap->devPrivate.ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, gl_access);
156         glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
157     }
158 
159 done:
160     RegionUninit(&region);
161 
162     if (priv->bo_mapped) {
163         /* Finish all gpu commands before accessing the buffer */
164         if (!priv->gl_synced && !glamor_priv->gl_synced)
165             glamor_finish(screen);
166 
167         priv->gl_synced = TRUE;
168 
169         /* No prepared flag for directly mapping */
170         return TRUE;
171     }
172 
173     priv->prepared = TRUE;
174     return TRUE;
175 }
176 
177 /*
178  * When we're done with the drawable, unmap the PBO, reupload
179  * if we were writing to it and then unbind it to release the memory
180  */
181 
182 void
glamor_finish_access_pixmap(PixmapPtr pixmap,Bool force)183 glamor_finish_access_pixmap(PixmapPtr pixmap, Bool force)
184 {
185     glamor_pixmap_private       *priv = glamor_get_pixmap_private(pixmap);
186 
187     if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv))
188         return;
189 
190 #ifdef GLAMOR_HAS_GBM
191     if (priv->bo_mapped) {
192         if (priv->prepared)
193             FatalError("something wrong during buffer mapping");
194 
195         /* Delay unmap to finalize when not forced */
196         if (force) {
197             pixmap->devPrivate.ptr = NULL;
198 
199             gbm_bo_unmap(priv->bo, priv->map_data);
200             priv->bo_mapped = FALSE;
201         }
202     }
203 #endif
204 
205     if (!priv->prepared)
206         return;
207 
208     if (priv->pbo) {
209         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, priv->pbo);
210         glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
211         pixmap->devPrivate.ptr = NULL;
212     }
213 
214     if (priv->map_access == GLAMOR_ACCESS_RW) {
215         glamor_upload_boxes(pixmap,
216                             RegionRects(&priv->prepare_region),
217                             RegionNumRects(&priv->prepare_region),
218                             0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind);
219     }
220 
221     RegionUninit(&priv->prepare_region);
222 
223     if (priv->pbo) {
224         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
225         glDeleteBuffers(1, &priv->pbo);
226         priv->pbo = 0;
227     } else {
228         free(pixmap->devPrivate.ptr);
229         pixmap->devPrivate.ptr = NULL;
230     }
231 
232     priv->prepared = FALSE;
233 }
234 
235 Bool
glamor_prepare_access(DrawablePtr drawable,glamor_access_t access)236 glamor_prepare_access(DrawablePtr drawable, glamor_access_t access)
237 {
238     PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
239     BoxRec box;
240     int off_x, off_y;
241 
242     glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
243 
244     box.x1 = drawable->x + off_x;
245     box.x2 = box.x1 + drawable->width;
246     box.y1 = drawable->y + off_y;
247     box.y2 = box.y1 + drawable->height;
248     return glamor_prep_pixmap_box(pixmap, access, &box);
249 }
250 
251 Bool
glamor_prepare_access_box(DrawablePtr drawable,glamor_access_t access,int x,int y,int w,int h)252 glamor_prepare_access_box(DrawablePtr drawable, glamor_access_t access,
253                          int x, int y, int w, int h)
254 {
255     PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
256     BoxRec box;
257     int off_x, off_y;
258 
259     glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y);
260     box.x1 = drawable->x + x + off_x;
261     box.x2 = box.x1 + w;
262     box.y1 = drawable->y + y + off_y;
263     box.y2 = box.y1 + h;
264     return glamor_prep_pixmap_box(pixmap, access, &box);
265 }
266 
267 void
glamor_finish_access(DrawablePtr drawable)268 glamor_finish_access(DrawablePtr drawable)
269 {
270     glamor_finish_access_pixmap(glamor_get_drawable_pixmap(drawable), FALSE);
271 }
272 
273 /*
274  * Make a picture ready to use with fb.
275  */
276 
277 Bool
glamor_prepare_access_picture(PicturePtr picture,glamor_access_t access)278 glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access)
279 {
280     if (!picture || !picture->pDrawable)
281         return TRUE;
282 
283     return glamor_prepare_access(picture->pDrawable, access);
284 }
285 
286 Bool
glamor_prepare_access_picture_box(PicturePtr picture,glamor_access_t access,int x,int y,int w,int h)287 glamor_prepare_access_picture_box(PicturePtr picture, glamor_access_t access,
288                         int x, int y, int w, int h)
289 {
290     if (!picture || !picture->pDrawable)
291         return TRUE;
292 
293     /* If a transform is set, we don't know what the bounds is on the
294      * source, so just prepare the whole pixmap.  XXX: We could
295      * potentially work out where in the source would be sampled based
296      * on the transform, and we don't need do do this for destination
297      * pixmaps at all.
298      */
299     if (picture->transform) {
300         return glamor_prepare_access_box(picture->pDrawable, access,
301                                          0, 0,
302                                          picture->pDrawable->width,
303                                          picture->pDrawable->height);
304     } else {
305         return glamor_prepare_access_box(picture->pDrawable, access,
306                                          x, y, w, h);
307     }
308 }
309 
310 void
glamor_finish_access_picture(PicturePtr picture)311 glamor_finish_access_picture(PicturePtr picture)
312 {
313     if (!picture || !picture->pDrawable)
314         return;
315 
316     glamor_finish_access(picture->pDrawable);
317 }
318 
319 /*
320  * Make a GC ready to use with fb. This just
321  * means making sure the appropriate fill pixmap is
322  * in CPU memory again
323  */
324 
325 Bool
glamor_prepare_access_gc(GCPtr gc)326 glamor_prepare_access_gc(GCPtr gc)
327 {
328     switch (gc->fillStyle) {
329     case FillTiled:
330         return glamor_prepare_access(&gc->tile.pixmap->drawable,
331                                      GLAMOR_ACCESS_RO);
332     case FillStippled:
333     case FillOpaqueStippled:
334         return glamor_prepare_access(&gc->stipple->drawable, GLAMOR_ACCESS_RO);
335     }
336     return TRUE;
337 }
338 
339 /*
340  * Free any temporary CPU pixmaps for the GC
341  */
342 void
glamor_finish_access_gc(GCPtr gc)343 glamor_finish_access_gc(GCPtr gc)
344 {
345     switch (gc->fillStyle) {
346     case FillTiled:
347         glamor_finish_access(&gc->tile.pixmap->drawable);
348         break;
349     case FillStippled:
350     case FillOpaqueStippled:
351         glamor_finish_access(&gc->stipple->drawable);
352         break;
353     }
354 }
355