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(®ion, 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(®ion, ®ion, &priv->prepare_region);
71 if (!RegionNotEmpty(®ion))
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(®ion), RegionNumRects(®ion),
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(®ion);
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