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_transfer.h"
25 #include "glamor_prepare.h"
26 #include "glamor_transform.h"
27 
28 struct copy_args {
29     PixmapPtr           src_pixmap;
30     glamor_pixmap_fbo   *src;
31     uint32_t            bitplane;
32     int                 dx, dy;
33 };
34 
35 static Bool
use_copyarea(PixmapPtr dst,GCPtr gc,glamor_program * prog,void * arg)36 use_copyarea(PixmapPtr dst, GCPtr gc, glamor_program *prog, void *arg)
37 {
38     struct copy_args *args = arg;
39     glamor_pixmap_fbo *src = args->src;
40 
41     glamor_bind_texture(glamor_get_screen_private(dst->drawable.pScreen),
42                         GL_TEXTURE0, src, TRUE);
43 
44     glUniform2f(prog->fill_offset_uniform, args->dx, args->dy);
45     glUniform2f(prog->fill_size_inv_uniform, 1.0f/src->width, 1.0f/src->height);
46 
47     return TRUE;
48 }
49 
50 static const glamor_facet glamor_facet_copyarea = {
51     "copy_area",
52     .vs_vars = "attribute vec2 primitive;\n",
53     .vs_exec = (GLAMOR_POS(gl_Position, primitive.xy)
54                 "       fill_pos = (fill_offset + primitive.xy) * fill_size_inv;\n"),
55     .fs_exec = "       gl_FragColor = texture2D(sampler, fill_pos);\n",
56     .locations = glamor_program_location_fillsamp | glamor_program_location_fillpos,
57     .use = use_copyarea,
58 };
59 
60 /*
61  * Configure the copy plane program for the current operation
62  */
63 
64 static Bool
use_copyplane(PixmapPtr dst,GCPtr gc,glamor_program * prog,void * arg)65 use_copyplane(PixmapPtr dst, GCPtr gc, glamor_program *prog, void *arg)
66 {
67     struct copy_args *args = arg;
68     glamor_pixmap_fbo *src = args->src;
69 
70     glamor_bind_texture(glamor_get_screen_private(dst->drawable.pScreen),
71                         GL_TEXTURE0, src, TRUE);
72 
73     glUniform2f(prog->fill_offset_uniform, args->dx, args->dy);
74     glUniform2f(prog->fill_size_inv_uniform, 1.0f/src->width, 1.0f/src->height);
75 
76     glamor_set_color(dst, gc->fgPixel, prog->fg_uniform);
77     glamor_set_color(dst, gc->bgPixel, prog->bg_uniform);
78 
79     /* XXX handle 2 10 10 10 and 1555 formats; presumably the pixmap private knows this? */
80     switch (args->src_pixmap->drawable.depth) {
81     case 30:
82         glUniform4ui(prog->bitplane_uniform,
83                      (args->bitplane >> 20) & 0x3ff,
84                      (args->bitplane >> 10) & 0x3ff,
85                      (args->bitplane      ) & 0x3ff,
86                      0);
87 
88         glUniform4f(prog->bitmul_uniform, 0x3ff, 0x3ff, 0x3ff, 0);
89         break;
90     case 24:
91         glUniform4ui(prog->bitplane_uniform,
92                      (args->bitplane >> 16) & 0xff,
93                      (args->bitplane >>  8) & 0xff,
94                      (args->bitplane      ) & 0xff,
95                      0);
96 
97         glUniform4f(prog->bitmul_uniform, 0xff, 0xff, 0xff, 0);
98         break;
99     case 32:
100         glUniform4ui(prog->bitplane_uniform,
101                      (args->bitplane >> 16) & 0xff,
102                      (args->bitplane >>  8) & 0xff,
103                      (args->bitplane      ) & 0xff,
104                      (args->bitplane >> 24) & 0xff);
105 
106         glUniform4f(prog->bitmul_uniform, 0xff, 0xff, 0xff, 0xff);
107         break;
108     case 16:
109         glUniform4ui(prog->bitplane_uniform,
110                      (args->bitplane >> 11) & 0x1f,
111                      (args->bitplane >>  5) & 0x3f,
112                      (args->bitplane      ) & 0x1f,
113                      0);
114 
115         glUniform4f(prog->bitmul_uniform, 0x1f, 0x3f, 0x1f, 0);
116         break;
117     case 15:
118         glUniform4ui(prog->bitplane_uniform,
119                      (args->bitplane >> 10) & 0x1f,
120                      (args->bitplane >>  5) & 0x1f,
121                      (args->bitplane      ) & 0x1f,
122                      0);
123 
124         glUniform4f(prog->bitmul_uniform, 0x1f, 0x1f, 0x1f, 0);
125         break;
126     case 8:
127         glUniform4ui(prog->bitplane_uniform,
128                      0, 0, 0, args->bitplane);
129         glUniform4f(prog->bitmul_uniform, 0, 0, 0, 0xff);
130         break;
131     case 1:
132         glUniform4ui(prog->bitplane_uniform,
133                      0, 0, 0, args->bitplane);
134         glUniform4f(prog->bitmul_uniform, 0, 0, 0, 0xff);
135         break;
136     }
137 
138     return TRUE;
139 }
140 
141 static const glamor_facet glamor_facet_copyplane = {
142     "copy_plane",
143     .version = 130,
144     .vs_vars = "attribute vec2 primitive;\n",
145     .vs_exec = (GLAMOR_POS(gl_Position, (primitive.xy))
146                 "       fill_pos = (fill_offset + primitive.xy) * fill_size_inv;\n"),
147     .fs_exec = ("       uvec4 bits = uvec4(round(texture2D(sampler, fill_pos) * bitmul));\n"
148                 "       if ((bits & bitplane) != uvec4(0,0,0,0))\n"
149                 "               gl_FragColor = fg;\n"
150                 "       else\n"
151                 "               gl_FragColor = bg;\n"),
152     .locations = glamor_program_location_fillsamp|glamor_program_location_fillpos|glamor_program_location_fg|glamor_program_location_bg|glamor_program_location_bitplane,
153     .use = use_copyplane,
154 };
155 
156 /*
157  * When all else fails, pull the bits out of the GPU and do the
158  * operation with fb
159  */
160 
161 static void
glamor_copy_bail(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)162 glamor_copy_bail(DrawablePtr src,
163                  DrawablePtr dst,
164                  GCPtr gc,
165                  BoxPtr box,
166                  int nbox,
167                  int dx,
168                  int dy,
169                  Bool reverse,
170                  Bool upsidedown,
171                  Pixel bitplane,
172                  void *closure)
173 {
174     if (glamor_prepare_access(dst, GLAMOR_ACCESS_RW) && glamor_prepare_access(src, GLAMOR_ACCESS_RO)) {
175         if (bitplane) {
176             if (src->bitsPerPixel > 1)
177                 fbCopyNto1(src, dst, gc, box, nbox, dx, dy,
178                            reverse, upsidedown, bitplane, closure);
179             else
180                 fbCopy1toN(src, dst, gc, box, nbox, dx, dy,
181                            reverse, upsidedown, bitplane, closure);
182         } else {
183             fbCopyNtoN(src, dst, gc, box, nbox, dx, dy,
184                        reverse, upsidedown, bitplane, closure);
185         }
186     }
187     glamor_finish_access(dst);
188     glamor_finish_access(src);
189 }
190 
191 /**
192  * Implements CopyPlane and CopyArea from the CPU to the GPU by using
193  * the source as a texture and painting that into the destination.
194  *
195  * This requires that source and dest are different textures, or that
196  * (if the copy area doesn't overlap), GL_NV_texture_barrier is used
197  * to ensure that the caches are flushed at the right times.
198  */
199 static Bool
glamor_copy_cpu_fbo(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)200 glamor_copy_cpu_fbo(DrawablePtr src,
201                     DrawablePtr dst,
202                     GCPtr gc,
203                     BoxPtr box,
204                     int nbox,
205                     int dx,
206                     int dy,
207                     Bool reverse,
208                     Bool upsidedown,
209                     Pixel bitplane,
210                     void *closure)
211 {
212     ScreenPtr screen = dst->pScreen;
213     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
214     PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
215     int dst_xoff, dst_yoff;
216 
217     if (gc && gc->alu != GXcopy)
218         goto bail;
219 
220     if (gc && !glamor_pm_is_solid(gc->depth, gc->planemask))
221         goto bail;
222 
223     glamor_make_current(glamor_priv);
224 
225     if (!glamor_prepare_access(src, GLAMOR_ACCESS_RO))
226         goto bail;
227 
228     glamor_get_drawable_deltas(dst, dst_pixmap, &dst_xoff, &dst_yoff);
229 
230     if (bitplane) {
231         FbBits *tmp_bits;
232         FbStride tmp_stride;
233         int tmp_bpp;
234         int tmp_xoff, tmp_yoff;
235 
236         PixmapPtr tmp_pix = fbCreatePixmap(screen, dst_pixmap->drawable.width,
237                                            dst_pixmap->drawable.height,
238                                            dst->depth, 0);
239 
240         if (!tmp_pix) {
241             glamor_finish_access(src);
242             goto bail;
243         }
244 
245         tmp_pix->drawable.x = dst_xoff;
246         tmp_pix->drawable.y = dst_yoff;
247 
248         fbGetDrawable(&tmp_pix->drawable, tmp_bits, tmp_stride, tmp_bpp, tmp_xoff,
249                       tmp_yoff);
250 
251         if (src->bitsPerPixel > 1)
252             fbCopyNto1(src, &tmp_pix->drawable, gc, box, nbox, dx, dy,
253                        reverse, upsidedown, bitplane, closure);
254         else
255             fbCopy1toN(src, &tmp_pix->drawable, gc, box, nbox, dx, dy,
256                        reverse, upsidedown, bitplane, closure);
257 
258         glamor_upload_boxes(dst_pixmap, box, nbox, tmp_xoff, tmp_yoff,
259                             dst_xoff, dst_yoff, (uint8_t *) tmp_bits,
260                             tmp_stride * sizeof(FbBits));
261         fbDestroyPixmap(tmp_pix);
262     } else {
263         FbBits *src_bits;
264         FbStride src_stride;
265         int src_bpp;
266         int src_xoff, src_yoff;
267 
268         fbGetDrawable(src, src_bits, src_stride, src_bpp, src_xoff, src_yoff);
269         glamor_upload_boxes(dst_pixmap, box, nbox, src_xoff + dx, src_yoff + dy,
270                             dst_xoff, dst_yoff,
271                             (uint8_t *) src_bits, src_stride * sizeof (FbBits));
272     }
273     glamor_finish_access(src);
274 
275     return TRUE;
276 
277 bail:
278     return FALSE;
279 }
280 
281 /**
282  * Implements CopyArea from the GPU to the CPU using glReadPixels from the
283  * source FBO.
284  */
285 static Bool
glamor_copy_fbo_cpu(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)286 glamor_copy_fbo_cpu(DrawablePtr src,
287                     DrawablePtr dst,
288                     GCPtr gc,
289                     BoxPtr box,
290                     int nbox,
291                     int dx,
292                     int dy,
293                     Bool reverse,
294                     Bool upsidedown,
295                     Pixel bitplane,
296                     void *closure)
297 {
298     ScreenPtr screen = dst->pScreen;
299     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
300     PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
301     FbBits *dst_bits;
302     FbStride dst_stride;
303     int dst_bpp;
304     int src_xoff, src_yoff;
305     int dst_xoff, dst_yoff;
306 
307     if (gc && gc->alu != GXcopy)
308         goto bail;
309 
310     if (gc && !glamor_pm_is_solid(gc->depth, gc->planemask))
311         goto bail;
312 
313     glamor_make_current(glamor_priv);
314 
315     if (!glamor_prepare_access(dst, GLAMOR_ACCESS_RW))
316         goto bail;
317 
318     glamor_get_drawable_deltas(src, src_pixmap, &src_xoff, &src_yoff);
319 
320     fbGetDrawable(dst, dst_bits, dst_stride, dst_bpp, dst_xoff, dst_yoff);
321 
322     glamor_download_boxes(src_pixmap, box, nbox, src_xoff + dx, src_yoff + dy,
323                           dst_xoff, dst_yoff,
324                           (uint8_t *) dst_bits, dst_stride * sizeof (FbBits));
325     glamor_finish_access(dst);
326 
327     return TRUE;
328 
329 bail:
330     return FALSE;
331 }
332 
333 /* Include the enums here for the moment, to keep from needing to bump epoxy. */
334 #ifndef GL_TILE_RASTER_ORDER_FIXED_MESA
335 #define GL_TILE_RASTER_ORDER_FIXED_MESA          0x8BB8
336 #define GL_TILE_RASTER_ORDER_INCREASING_X_MESA   0x8BB9
337 #define GL_TILE_RASTER_ORDER_INCREASING_Y_MESA   0x8BBA
338 #endif
339 
340 /*
341  * Copy from GPU to GPU by using the source
342  * as a texture and painting that into the destination
343  */
344 
345 static Bool
glamor_copy_fbo_fbo_draw(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)346 glamor_copy_fbo_fbo_draw(DrawablePtr src,
347                          DrawablePtr dst,
348                          GCPtr gc,
349                          BoxPtr box,
350                          int nbox,
351                          int dx,
352                          int dy,
353                          Bool reverse,
354                          Bool upsidedown,
355                          Pixel bitplane,
356                          void *closure)
357 {
358     ScreenPtr screen = dst->pScreen;
359     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
360     PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
361     PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
362     glamor_pixmap_private *src_priv = glamor_get_pixmap_private(src_pixmap);
363     glamor_pixmap_private *dst_priv = glamor_get_pixmap_private(dst_pixmap);
364     int src_box_index, dst_box_index;
365     int dst_off_x, dst_off_y;
366     int src_off_x, src_off_y;
367     GLshort *v;
368     char *vbo_offset;
369     struct copy_args args;
370     glamor_program *prog;
371     const glamor_facet *copy_facet;
372     int n;
373     Bool ret = FALSE;
374     BoxRec bounds = glamor_no_rendering_bounds();
375 
376     glamor_make_current(glamor_priv);
377 
378     if (gc && !glamor_set_planemask(gc->depth, gc->planemask))
379         goto bail_ctx;
380 
381     if (!glamor_set_alu(screen, gc ? gc->alu : GXcopy))
382         goto bail_ctx;
383 
384     if (bitplane && !glamor_priv->can_copyplane)
385         goto bail_ctx;
386 
387     if (bitplane) {
388         prog = &glamor_priv->copy_plane_prog;
389         copy_facet = &glamor_facet_copyplane;
390     } else {
391         prog = &glamor_priv->copy_area_prog;
392         copy_facet = &glamor_facet_copyarea;
393     }
394 
395     if (prog->failed)
396         goto bail_ctx;
397 
398     if (!prog->prog) {
399         if (!glamor_build_program(screen, prog,
400                                   copy_facet, NULL, NULL, NULL))
401             goto bail_ctx;
402     }
403 
404     args.src_pixmap = src_pixmap;
405     args.bitplane = bitplane;
406 
407     /* Set up the vertex buffers for the points */
408 
409     v = glamor_get_vbo_space(dst->pScreen, nbox * 8 * sizeof (int16_t), &vbo_offset);
410 
411     if (src_pixmap == dst_pixmap && glamor_priv->has_mesa_tile_raster_order) {
412         glEnable(GL_TILE_RASTER_ORDER_FIXED_MESA);
413         if (dx >= 0)
414             glEnable(GL_TILE_RASTER_ORDER_INCREASING_X_MESA);
415         else
416             glDisable(GL_TILE_RASTER_ORDER_INCREASING_X_MESA);
417         if (dy >= 0)
418             glEnable(GL_TILE_RASTER_ORDER_INCREASING_Y_MESA);
419         else
420             glDisable(GL_TILE_RASTER_ORDER_INCREASING_Y_MESA);
421     }
422 
423     glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
424     glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_SHORT, GL_FALSE,
425                           2 * sizeof (GLshort), vbo_offset);
426 
427     if (nbox < 100) {
428         bounds = glamor_start_rendering_bounds();
429         for (int i = 0; i < nbox; i++)
430             glamor_bounds_union_box(&bounds, &box[i]);
431     }
432 
433     for (n = 0; n < nbox; n++) {
434         v[0] = box->x1; v[1] = box->y1;
435         v[2] = box->x1; v[3] = box->y2;
436         v[4] = box->x2; v[5] = box->y2;
437         v[6] = box->x2; v[7] = box->y1;
438 
439         v += 8;
440         box++;
441     }
442 
443     glamor_put_vbo_space(screen);
444 
445     glamor_get_drawable_deltas(src, src_pixmap, &src_off_x, &src_off_y);
446 
447     glEnable(GL_SCISSOR_TEST);
448 
449     glamor_pixmap_loop(src_priv, src_box_index) {
450         BoxPtr src_box = glamor_pixmap_box_at(src_priv, src_box_index);
451 
452         args.dx = dx + src_off_x - src_box->x1;
453         args.dy = dy + src_off_y - src_box->y1;
454         args.src = glamor_pixmap_fbo_at(src_priv, src_box_index);
455 
456         if (!glamor_use_program(dst_pixmap, gc, prog, &args))
457             goto bail_ctx;
458 
459         glamor_pixmap_loop(dst_priv, dst_box_index) {
460             BoxRec scissor = {
461                 .x1 = max(-args.dx, bounds.x1),
462                 .y1 = max(-args.dy, bounds.y1),
463                 .x2 = min(-args.dx + src_box->x2 - src_box->x1, bounds.x2),
464                 .y2 = min(-args.dy + src_box->y2 - src_box->y1, bounds.y2),
465             };
466             if (scissor.x1 >= scissor.x2 || scissor.y1 >= scissor.y2)
467                 continue;
468 
469             if (!glamor_set_destination_drawable(dst, dst_box_index, FALSE, FALSE,
470                                                  prog->matrix_uniform,
471                                                  &dst_off_x, &dst_off_y))
472                 goto bail_ctx;
473 
474             glScissor(scissor.x1 + dst_off_x,
475                       scissor.y1 + dst_off_y,
476                       scissor.x2 - scissor.x1,
477                       scissor.y2 - scissor.y1);
478 
479             glamor_glDrawArrays_GL_QUADS(glamor_priv, nbox);
480         }
481     }
482 
483     ret = TRUE;
484 
485 bail_ctx:
486     if (src_pixmap == dst_pixmap && glamor_priv->has_mesa_tile_raster_order) {
487         glDisable(GL_TILE_RASTER_ORDER_FIXED_MESA);
488     }
489     glDisable(GL_SCISSOR_TEST);
490     glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
491 
492     return ret;
493 }
494 
495 /**
496  * Copies from the GPU to the GPU using a temporary pixmap in between,
497  * to correctly handle overlapping copies.
498  */
499 
500 static Bool
glamor_copy_fbo_fbo_temp(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)501 glamor_copy_fbo_fbo_temp(DrawablePtr src,
502                          DrawablePtr dst,
503                          GCPtr gc,
504                          BoxPtr box,
505                          int nbox,
506                          int dx,
507                          int dy,
508                          Bool reverse,
509                          Bool upsidedown,
510                          Pixel bitplane,
511                          void *closure)
512 {
513     ScreenPtr screen = dst->pScreen;
514     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
515     PixmapPtr tmp_pixmap;
516     BoxRec bounds;
517     int n;
518     BoxPtr tmp_box;
519 
520     if (nbox == 0)
521         return TRUE;
522 
523     /* Sanity check state to avoid getting halfway through and bailing
524      * at the last second. Might be nice to have checks that didn't
525      * involve setting state.
526      */
527     glamor_make_current(glamor_priv);
528 
529     if (gc && !glamor_set_planemask(gc->depth, gc->planemask))
530         goto bail_ctx;
531 
532     if (!glamor_set_alu(screen, gc ? gc->alu : GXcopy))
533         goto bail_ctx;
534 
535     /* Find the size of the area to copy
536      */
537     bounds = box[0];
538     for (n = 1; n < nbox; n++) {
539         bounds.x1 = min(bounds.x1, box[n].x1);
540         bounds.x2 = max(bounds.x2, box[n].x2);
541         bounds.y1 = min(bounds.y1, box[n].y1);
542         bounds.y2 = max(bounds.y2, box[n].y2);
543     }
544 
545     /* Allocate a suitable temporary pixmap
546      */
547     tmp_pixmap = glamor_create_pixmap(screen,
548                                       bounds.x2 - bounds.x1,
549                                       bounds.y2 - bounds.y1,
550                                       src->depth, 0);
551     if (!tmp_pixmap)
552         goto bail;
553 
554     tmp_box = calloc(nbox, sizeof (BoxRec));
555     if (!tmp_box)
556         goto bail_pixmap;
557 
558     /* Convert destination boxes into tmp pixmap boxes
559      */
560     for (n = 0; n < nbox; n++) {
561         tmp_box[n].x1 = box[n].x1 - bounds.x1;
562         tmp_box[n].x2 = box[n].x2 - bounds.x1;
563         tmp_box[n].y1 = box[n].y1 - bounds.y1;
564         tmp_box[n].y2 = box[n].y2 - bounds.y1;
565     }
566 
567     if (!glamor_copy_fbo_fbo_draw(src,
568                                   &tmp_pixmap->drawable,
569                                   NULL,
570                                   tmp_box,
571                                   nbox,
572                                   dx + bounds.x1,
573                                   dy + bounds.y1,
574                                   FALSE, FALSE,
575                                   0, NULL))
576         goto bail_box;
577 
578     if (!glamor_copy_fbo_fbo_draw(&tmp_pixmap->drawable,
579                                   dst,
580                                   gc,
581                                   box,
582                                   nbox,
583                                   -bounds.x1,
584                                   -bounds.y1,
585                                   FALSE, FALSE,
586                                   bitplane, closure))
587         goto bail_box;
588 
589     free(tmp_box);
590 
591     glamor_destroy_pixmap(tmp_pixmap);
592 
593     return TRUE;
594 bail_box:
595     free(tmp_box);
596 bail_pixmap:
597     glamor_destroy_pixmap(tmp_pixmap);
598 bail:
599     return FALSE;
600 
601 bail_ctx:
602     return FALSE;
603 }
604 
605 /**
606  * Returns TRUE if the copy has to be implemented with
607  * glamor_copy_fbo_fbo_temp() instead of glamor_copy_fbo_fbo().
608  *
609  * If the src and dst are in the same pixmap, then glamor_copy_fbo_fbo()'s
610  * sampling would give undefined results (since the same texture would be
611  * bound as an FBO destination and as a texture source).  However, if we
612  * have GL_NV_texture_barrier, we can take advantage of the exception it
613  * added:
614  *
615  *    "- If a texel has been written, then in order to safely read the result
616  *       a texel fetch must be in a subsequent Draw separated by the command
617  *
618  *       void TextureBarrierNV(void);
619  *
620  *    TextureBarrierNV() will guarantee that writes have completed and caches
621  *    have been invalidated before subsequent Draws are executed."
622  */
623 static Bool
glamor_copy_needs_temp(DrawablePtr src,DrawablePtr dst,BoxPtr box,int nbox,int dx,int dy)624 glamor_copy_needs_temp(DrawablePtr src,
625                        DrawablePtr dst,
626                        BoxPtr box,
627                        int nbox,
628                        int dx,
629                        int dy)
630 {
631     PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
632     PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
633     ScreenPtr screen = dst->pScreen;
634     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
635     int n;
636     int dst_off_x, dst_off_y;
637     int src_off_x, src_off_y;
638     BoxRec bounds;
639 
640     if (src_pixmap != dst_pixmap)
641         return FALSE;
642 
643     if (nbox == 0)
644         return FALSE;
645 
646     if (!glamor_priv->has_nv_texture_barrier)
647         return TRUE;
648 
649     if (!glamor_priv->has_mesa_tile_raster_order) {
650         glamor_get_drawable_deltas(src, src_pixmap, &src_off_x, &src_off_y);
651         glamor_get_drawable_deltas(dst, dst_pixmap, &dst_off_x, &dst_off_y);
652 
653         bounds = box[0];
654         for (n = 1; n < nbox; n++) {
655             bounds.x1 = min(bounds.x1, box[n].x1);
656             bounds.y1 = min(bounds.y1, box[n].y1);
657 
658             bounds.x2 = max(bounds.x2, box[n].x2);
659             bounds.y2 = max(bounds.y2, box[n].y2);
660         }
661 
662         /* Check to see if the pixmap-relative boxes overlap in both X and Y,
663          * in which case we can't rely on NV_texture_barrier and must
664          * make a temporary copy
665          *
666          *  dst.x1                     < src.x2 &&
667          *  src.x1                     < dst.x2 &&
668          *
669          *  dst.y1                     < src.y2 &&
670          *  src.y1                     < dst.y2
671          */
672         if (bounds.x1 + dst_off_x      < bounds.x2 + dx + src_off_x &&
673             bounds.x1 + dx + src_off_x < bounds.x2 + dst_off_x &&
674 
675             bounds.y1 + dst_off_y      < bounds.y2 + dy + src_off_y &&
676             bounds.y1 + dy + src_off_y < bounds.y2 + dst_off_y) {
677             return TRUE;
678         }
679     }
680 
681     glTextureBarrierNV();
682 
683     return FALSE;
684 }
685 
686 static Bool
glamor_copy_gl(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)687 glamor_copy_gl(DrawablePtr src,
688                DrawablePtr dst,
689                GCPtr gc,
690                BoxPtr box,
691                int nbox,
692                int dx,
693                int dy,
694                Bool reverse,
695                Bool upsidedown,
696                Pixel bitplane,
697                void *closure)
698 {
699     PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
700     PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
701     glamor_pixmap_private *src_priv = glamor_get_pixmap_private(src_pixmap);
702     glamor_pixmap_private *dst_priv = glamor_get_pixmap_private(dst_pixmap);
703 
704     if (GLAMOR_PIXMAP_PRIV_HAS_FBO(dst_priv)) {
705         if (GLAMOR_PIXMAP_PRIV_HAS_FBO(src_priv)) {
706             if (glamor_copy_needs_temp(src, dst, box, nbox, dx, dy))
707                 return glamor_copy_fbo_fbo_temp(src, dst, gc, box, nbox, dx, dy,
708                                                 reverse, upsidedown, bitplane, closure);
709             else
710                 return glamor_copy_fbo_fbo_draw(src, dst, gc, box, nbox, dx, dy,
711                                                 reverse, upsidedown, bitplane, closure);
712         }
713 
714         return glamor_copy_cpu_fbo(src, dst, gc, box, nbox, dx, dy,
715                                    reverse, upsidedown, bitplane, closure);
716     } else if (GLAMOR_PIXMAP_PRIV_HAS_FBO(src_priv) &&
717                dst_priv->type != GLAMOR_DRM_ONLY &&
718                bitplane == 0) {
719             return glamor_copy_fbo_cpu(src, dst, gc, box, nbox, dx, dy,
720                                        reverse, upsidedown, bitplane, closure);
721     }
722     return FALSE;
723 }
724 
725 void
glamor_copy(DrawablePtr src,DrawablePtr dst,GCPtr gc,BoxPtr box,int nbox,int dx,int dy,Bool reverse,Bool upsidedown,Pixel bitplane,void * closure)726 glamor_copy(DrawablePtr src,
727             DrawablePtr dst,
728             GCPtr gc,
729             BoxPtr box,
730             int nbox,
731             int dx,
732             int dy,
733             Bool reverse,
734             Bool upsidedown,
735             Pixel bitplane,
736             void *closure)
737 {
738     if (nbox == 0)
739 	return;
740 
741     if (glamor_copy_gl(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure))
742         return;
743     glamor_copy_bail(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure);
744 }
745 
746 RegionPtr
glamor_copy_area(DrawablePtr src,DrawablePtr dst,GCPtr gc,int srcx,int srcy,int width,int height,int dstx,int dsty)747 glamor_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc,
748                  int srcx, int srcy, int width, int height, int dstx, int dsty)
749 {
750     return miDoCopy(src, dst, gc,
751                     srcx, srcy, width, height,
752                     dstx, dsty, glamor_copy, 0, NULL);
753 }
754 
755 RegionPtr
glamor_copy_plane(DrawablePtr src,DrawablePtr dst,GCPtr gc,int srcx,int srcy,int width,int height,int dstx,int dsty,unsigned long bitplane)756 glamor_copy_plane(DrawablePtr src, DrawablePtr dst, GCPtr gc,
757                   int srcx, int srcy, int width, int height, int dstx, int dsty,
758                   unsigned long bitplane)
759 {
760     if ((bitplane & FbFullMask(src->depth)) == 0)
761         return miHandleExposures(src, dst, gc,
762                                  srcx, srcy, width, height, dstx, dsty);
763     return miDoCopy(src, dst, gc,
764                     srcx, srcy, width, height,
765                     dstx, dsty, glamor_copy, bitplane, NULL);
766 }
767 
768 void
glamor_copy_window(WindowPtr window,DDXPointRec old_origin,RegionPtr src_region)769 glamor_copy_window(WindowPtr window, DDXPointRec old_origin, RegionPtr src_region)
770 {
771     PixmapPtr pixmap = glamor_get_drawable_pixmap(&window->drawable);
772     DrawablePtr drawable = &pixmap->drawable;
773     RegionRec dst_region;
774     int dx, dy;
775 
776     dx = old_origin.x - window->drawable.x;
777     dy = old_origin.y - window->drawable.y;
778     RegionTranslate(src_region, -dx, -dy);
779 
780     RegionNull(&dst_region);
781 
782     RegionIntersect(&dst_region, &window->borderClip, src_region);
783 
784 #ifdef COMPOSITE
785     if (pixmap->screen_x || pixmap->screen_y)
786         RegionTranslate(&dst_region, -pixmap->screen_x, -pixmap->screen_y);
787 #endif
788 
789     miCopyRegion(drawable, drawable,
790                  0, &dst_region, dx, dy, glamor_copy, 0, 0);
791 
792     RegionUninit(&dst_region);
793 }
794