glamor_prepare.c revision 5a7dfde8
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 33static Bool 34glamor_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 return TRUE; 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 return TRUE; 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 RegionInit(&priv->prepare_region, box, 1); 84 85 if (glamor_priv->has_rw_pbo) { 86 if (priv->pbo == 0) 87 glGenBuffers(1, &priv->pbo); 88 89 gl_usage = GL_STREAM_READ; 90 91 glamor_priv->suppress_gl_out_of_memory_logging = true; 92 93 glBindBuffer(GL_PIXEL_PACK_BUFFER, priv->pbo); 94 glBufferData(GL_PIXEL_PACK_BUFFER, 95 pixmap->devKind * pixmap->drawable.height, NULL, 96 gl_usage); 97 98 glamor_priv->suppress_gl_out_of_memory_logging = false; 99 100 if (glGetError() == GL_OUT_OF_MEMORY) { 101 if (!glamor_priv->logged_any_pbo_allocation_failure) { 102 LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %d " 103 "bytes PBO due to GL_OUT_OF_MEMORY.\n", 104 pixmap->devKind * pixmap->drawable.height); 105 glamor_priv->logged_any_pbo_allocation_failure = true; 106 } 107 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 108 glDeleteBuffers(1, &priv->pbo); 109 priv->pbo = 0; 110 } 111 } 112 113 if (!priv->pbo) { 114 pixmap->devPrivate.ptr = xallocarray(pixmap->devKind, 115 pixmap->drawable.height); 116 if (!pixmap->devPrivate.ptr) 117 return FALSE; 118 } 119 priv->map_access = access; 120 } 121 122 glamor_download_boxes(pixmap, RegionRects(®ion), RegionNumRects(®ion), 123 0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind); 124 125 RegionUninit(®ion); 126 127 if (priv->pbo) { 128 if (priv->map_access == GLAMOR_ACCESS_RW) 129 gl_access = GL_READ_WRITE; 130 else 131 gl_access = GL_READ_ONLY; 132 133 pixmap->devPrivate.ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, gl_access); 134 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 135 } 136 137 priv->prepared = TRUE; 138 return TRUE; 139} 140 141/* 142 * When we're done with the drawable, unmap the PBO, reupload 143 * if we were writing to it and then unbind it to release the memory 144 */ 145 146static void 147glamor_fini_pixmap(PixmapPtr pixmap) 148{ 149 glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap); 150 151 if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv)) 152 return; 153 154 if (!priv->prepared) 155 return; 156 157 if (priv->pbo) { 158 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, priv->pbo); 159 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); 160 pixmap->devPrivate.ptr = NULL; 161 } 162 163 if (priv->map_access == GLAMOR_ACCESS_RW) { 164 glamor_upload_boxes(pixmap, 165 RegionRects(&priv->prepare_region), 166 RegionNumRects(&priv->prepare_region), 167 0, 0, 0, 0, pixmap->devPrivate.ptr, pixmap->devKind); 168 } 169 170 RegionUninit(&priv->prepare_region); 171 172 if (priv->pbo) { 173 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 174 glDeleteBuffers(1, &priv->pbo); 175 priv->pbo = 0; 176 } else { 177 free(pixmap->devPrivate.ptr); 178 pixmap->devPrivate.ptr = NULL; 179 } 180 181 priv->prepared = FALSE; 182} 183 184Bool 185glamor_prepare_access(DrawablePtr drawable, glamor_access_t access) 186{ 187 PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); 188 BoxRec box; 189 int off_x, off_y; 190 191 glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y); 192 193 box.x1 = drawable->x + off_x; 194 box.x2 = box.x1 + drawable->width; 195 box.y1 = drawable->y + off_y; 196 box.y2 = box.y1 + drawable->height; 197 return glamor_prep_pixmap_box(pixmap, access, &box); 198} 199 200Bool 201glamor_prepare_access_box(DrawablePtr drawable, glamor_access_t access, 202 int x, int y, int w, int h) 203{ 204 PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); 205 BoxRec box; 206 int off_x, off_y; 207 208 glamor_get_drawable_deltas(drawable, pixmap, &off_x, &off_y); 209 box.x1 = drawable->x + x + off_x; 210 box.x2 = box.x1 + w; 211 box.y1 = drawable->y + y + off_y; 212 box.y2 = box.y1 + h; 213 return glamor_prep_pixmap_box(pixmap, access, &box); 214} 215 216void 217glamor_finish_access(DrawablePtr drawable) 218{ 219 glamor_fini_pixmap(glamor_get_drawable_pixmap(drawable)); 220} 221 222/* 223 * Make a picture ready to use with fb. 224 */ 225 226Bool 227glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access) 228{ 229 if (!picture || !picture->pDrawable) 230 return TRUE; 231 232 return glamor_prepare_access(picture->pDrawable, access); 233} 234 235Bool 236glamor_prepare_access_picture_box(PicturePtr picture, glamor_access_t access, 237 int x, int y, int w, int h) 238{ 239 if (!picture || !picture->pDrawable) 240 return TRUE; 241 242 /* If a transform is set, we don't know what the bounds is on the 243 * source, so just prepare the whole pixmap. XXX: We could 244 * potentially work out where in the source would be sampled based 245 * on the transform, and we don't need do do this for destination 246 * pixmaps at all. 247 */ 248 if (picture->transform) { 249 return glamor_prepare_access_box(picture->pDrawable, access, 250 0, 0, 251 picture->pDrawable->width, 252 picture->pDrawable->height); 253 } else { 254 return glamor_prepare_access_box(picture->pDrawable, access, 255 x, y, w, h); 256 } 257} 258 259void 260glamor_finish_access_picture(PicturePtr picture) 261{ 262 if (!picture || !picture->pDrawable) 263 return; 264 265 glamor_finish_access(picture->pDrawable); 266} 267 268/* 269 * Make a GC ready to use with fb. This just 270 * means making sure the appropriate fill pixmap is 271 * in CPU memory again 272 */ 273 274Bool 275glamor_prepare_access_gc(GCPtr gc) 276{ 277 switch (gc->fillStyle) { 278 case FillTiled: 279 return glamor_prepare_access(&gc->tile.pixmap->drawable, 280 GLAMOR_ACCESS_RO); 281 case FillStippled: 282 case FillOpaqueStippled: 283 return glamor_prepare_access(&gc->stipple->drawable, GLAMOR_ACCESS_RO); 284 } 285 return TRUE; 286} 287 288/* 289 * Free any temporary CPU pixmaps for the GC 290 */ 291void 292glamor_finish_access_gc(GCPtr gc) 293{ 294 switch (gc->fillStyle) { 295 case FillTiled: 296 glamor_finish_access(&gc->tile.pixmap->drawable); 297 break; 298 case FillStippled: 299 case FillOpaqueStippled: 300 glamor_finish_access(&gc->stipple->drawable); 301 break; 302 } 303} 304