Edit

IABSD.fr/xenocara/xserver/glamor/glamor_copy.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2017-12-08 15:01:59
    Hash : 1a66cad3
    Message : Update to xserver 1.19.5. Tested by bru@, jsg@ and others

  • xserver/glamor/glamor_copy.c
  • /*
     * Copyright © 2014 Keith Packard
     *
     * Permission to use, copy, modify, distribute, and sell this software and its
     * documentation for any purpose is hereby granted without fee, provided that
     * the above copyright notice appear in all copies and that both that copyright
     * notice and this permission notice appear in supporting documentation, and
     * that the name of the copyright holders not be used in advertising or
     * publicity pertaining to distribution of the software without specific,
     * written prior permission.  The copyright holders make no representations
     * about the suitability of this software for any purpose.  It is provided "as
     * is" without express or implied warranty.
     *
     * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     * OF THIS SOFTWARE.
     */
    
    #include "glamor_priv.h"
    #include "glamor_transfer.h"
    #include "glamor_prepare.h"
    #include "glamor_transform.h"
    
    struct copy_args {
        PixmapPtr           src_pixmap;
        glamor_pixmap_fbo   *src;
        uint32_t            bitplane;
        int                 dx, dy;
    };
    
    static Bool
    use_copyarea(PixmapPtr dst, GCPtr gc, glamor_program *prog, void *arg)
    {
        struct copy_args *args = arg;
        glamor_pixmap_fbo *src = args->src;
    
        glamor_bind_texture(glamor_get_screen_private(dst->drawable.pScreen),
                            GL_TEXTURE0, src, TRUE);
    
        glUniform2f(prog->fill_offset_uniform, args->dx, args->dy);
        glUniform2f(prog->fill_size_inv_uniform, 1.0f/src->width, 1.0f/src->height);
    
        return TRUE;
    }
    
    static const glamor_facet glamor_facet_copyarea = {
        "copy_area",
        .vs_vars = "attribute vec2 primitive;\n",
        .vs_exec = (GLAMOR_POS(gl_Position, primitive.xy)
                    "       fill_pos = (fill_offset + primitive.xy) * fill_size_inv;\n"),
        .fs_exec = "       gl_FragColor = texture2D(sampler, fill_pos);\n",
        .locations = glamor_program_location_fillsamp | glamor_program_location_fillpos,
        .use = use_copyarea,
    };
    
    /*
     * Configure the copy plane program for the current operation
     */
    
    static Bool
    use_copyplane(PixmapPtr dst, GCPtr gc, glamor_program *prog, void *arg)
    {
        struct copy_args *args = arg;
        glamor_pixmap_fbo *src = args->src;
    
        glamor_bind_texture(glamor_get_screen_private(dst->drawable.pScreen),
                            GL_TEXTURE0, src, TRUE);
    
        glUniform2f(prog->fill_offset_uniform, args->dx, args->dy);
        glUniform2f(prog->fill_size_inv_uniform, 1.0f/src->width, 1.0f/src->height);
    
        glamor_set_color(dst, gc->fgPixel, prog->fg_uniform);
        glamor_set_color(dst, gc->bgPixel, prog->bg_uniform);
    
        /* XXX handle 2 10 10 10 and 1555 formats; presumably the pixmap private knows this? */
        switch (args->src_pixmap->drawable.depth) {
        case 24:
            glUniform4ui(prog->bitplane_uniform,
                         (args->bitplane >> 16) & 0xff,
                         (args->bitplane >>  8) & 0xff,
                         (args->bitplane      ) & 0xff,
                         0);
    
            glUniform4f(prog->bitmul_uniform, 0xff, 0xff, 0xff, 0);
            break;
        case 32:
            glUniform4ui(prog->bitplane_uniform,
                         (args->bitplane >> 16) & 0xff,
                         (args->bitplane >>  8) & 0xff,
                         (args->bitplane      ) & 0xff,
                         (args->bitplane >> 24) & 0xff);
    
            glUniform4f(prog->bitmul_uniform, 0xff, 0xff, 0xff, 0xff);
            break;
        case 16:
            glUniform4ui(prog->bitplane_uniform,
                         (args->bitplane >> 11) & 0x1f,
                         (args->bitplane >>  5) & 0x3f,
                         (args->bitplane      ) & 0x1f,
                         0);
    
            glUniform4f(prog->bitmul_uniform, 0x1f, 0x3f, 0x1f, 0);
            break;
        case 15:
            glUniform4ui(prog->bitplane_uniform,
                         (args->bitplane >> 10) & 0x1f,
                         (args->bitplane >>  5) & 0x1f,
                         (args->bitplane      ) & 0x1f,
                         0);
    
            glUniform4f(prog->bitmul_uniform, 0x1f, 0x1f, 0x1f, 0);
            break;
        case 8:
            glUniform4ui(prog->bitplane_uniform,
                         0, 0, 0, args->bitplane);
            glUniform4f(prog->bitmul_uniform, 0, 0, 0, 0xff);
            break;
        case 1:
            glUniform4ui(prog->bitplane_uniform,
                         0, 0, 0, args->bitplane);
            glUniform4f(prog->bitmul_uniform, 0, 0, 0, 0xff);
            break;
        }
    
        return TRUE;
    }
    
    static const glamor_facet glamor_facet_copyplane = {
        "copy_plane",
        .version = 130,
        .vs_vars = "attribute vec2 primitive;\n",
        .vs_exec = (GLAMOR_POS(gl_Position, (primitive.xy))
                    "       fill_pos = (fill_offset + primitive.xy) * fill_size_inv;\n"),
        .fs_exec = ("       uvec4 bits = uvec4(round(texture2D(sampler, fill_pos) * bitmul));\n"
                    "       if ((bits & bitplane) != uvec4(0,0,0,0))\n"
                    "               gl_FragColor = fg;\n"
                    "       else\n"
                    "               gl_FragColor = bg;\n"),
        .locations = glamor_program_location_fillsamp|glamor_program_location_fillpos|glamor_program_location_fg|glamor_program_location_bg|glamor_program_location_bitplane,
        .use = use_copyplane,
    };
    
    /*
     * When all else fails, pull the bits out of the GPU and do the
     * operation with fb
     */
    
    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)
    {
        if (glamor_prepare_access(dst, GLAMOR_ACCESS_RW) && glamor_prepare_access(src, GLAMOR_ACCESS_RO)) {
            if (bitplane) {
                if (src->bitsPerPixel > 1)
                    fbCopyNto1(src, dst, gc, box, nbox, dx, dy,
                               reverse, upsidedown, bitplane, closure);
                else
                    fbCopy1toN(src, dst, gc, box, nbox, dx, dy,
                               reverse, upsidedown, bitplane, closure);
            } else {
                fbCopyNtoN(src, dst, gc, box, nbox, dx, dy,
                           reverse, upsidedown, bitplane, closure);
            }
        }
        glamor_finish_access(dst);
        glamor_finish_access(src);
    }
    
    /**
     * Implements CopyPlane and CopyArea from the GPU to the GPU by using
     * the source as a texture and painting that into the destination.
     *
     * This requires that source and dest are different textures, or that
     * (if the copy area doesn't overlap), GL_NV_texture_barrier is used
     * to ensure that the caches are flushed at the right times.
     */
    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)
    {
        ScreenPtr screen = dst->pScreen;
        glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
        PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
        FbBits *src_bits;
        FbStride src_stride;
        int src_bpp;
        int src_xoff, src_yoff;
        int dst_xoff, dst_yoff;
    
        if (gc && gc->alu != GXcopy)
            goto bail;
    
        if (gc && !glamor_pm_is_solid(gc->depth, gc->planemask))
            goto bail;
    
        glamor_make_current(glamor_priv);
        glamor_prepare_access(src, GLAMOR_ACCESS_RO);
    
        glamor_get_drawable_deltas(dst, dst_pixmap, &dst_xoff, &dst_yoff);
    
        if (bitplane) {
            PixmapPtr src_pix = fbCreatePixmap(screen, dst_pixmap->drawable.width,
                                               dst_pixmap->drawable.height,
                                               dst->depth, 0);
    
            if (!src_pix) {
                glamor_finish_access(src);
                goto bail;
            }
    
            src_pix->drawable.x = dst_xoff;
            src_pix->drawable.y = dst_yoff;
    
            fbGetDrawable(&src_pix->drawable, src_bits, src_stride, src_bpp, src_xoff,
                          src_yoff);
    
            if (src->bitsPerPixel > 1)
                fbCopyNto1(src, &src_pix->drawable, gc, box, nbox, dx, dy,
                           reverse, upsidedown, bitplane, closure);
            else
                fbCopy1toN(src, &src_pix->drawable, gc, box, nbox, dx, dy,
                           reverse, upsidedown, bitplane, closure);
    
            glamor_upload_boxes(dst_pixmap, box, nbox, src_xoff, src_yoff,
                                dst_xoff, dst_yoff, (uint8_t *) src_bits,
                                src_stride * sizeof(FbBits));
            fbDestroyPixmap(src_pix);
        } else {
            fbGetDrawable(src, src_bits, src_stride, src_bpp, src_xoff, src_yoff);
            glamor_upload_boxes(dst_pixmap, box, nbox, src_xoff + dx, src_yoff + dy,
                                dst_xoff, dst_yoff,
                                (uint8_t *) src_bits, src_stride * sizeof (FbBits));
        }
        glamor_finish_access(src);
    
        return TRUE;
    
    bail:
        return FALSE;
    }
    
    /**
     * Implements CopyArea from the GPU to the CPU using glReadPixels from the
     * source FBO.
     */
    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)
    {
        ScreenPtr screen = dst->pScreen;
        glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
        PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
        FbBits *dst_bits;
        FbStride dst_stride;
        int dst_bpp;
        int src_xoff, src_yoff;
        int dst_xoff, dst_yoff;
    
        if (gc && gc->alu != GXcopy)
            goto bail;
    
        if (gc && !glamor_pm_is_solid(gc->depth, gc->planemask))
            goto bail;
    
        glamor_make_current(glamor_priv);
        glamor_prepare_access(dst, GLAMOR_ACCESS_RW);
    
        glamor_get_drawable_deltas(src, src_pixmap, &src_xoff, &src_yoff);
    
        fbGetDrawable(dst, dst_bits, dst_stride, dst_bpp, dst_xoff, dst_yoff);
    
        glamor_download_boxes(src_pixmap, box, nbox, src_xoff + dx, src_yoff + dy,
                              dst_xoff, dst_yoff,
                              (uint8_t *) dst_bits, dst_stride * sizeof (FbBits));
        glamor_finish_access(dst);
    
        return TRUE;
    
    bail:
        return FALSE;
    }
    
    /*
     * Copy from GPU to GPU by using the source
     * as a texture and painting that into the destination
     */
    
    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)
    {
        ScreenPtr screen = dst->pScreen;
        glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
        PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
        PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
        glamor_pixmap_private *src_priv = glamor_get_pixmap_private(src_pixmap);
        glamor_pixmap_private *dst_priv = glamor_get_pixmap_private(dst_pixmap);
        int src_box_index, dst_box_index;
        int dst_off_x, dst_off_y;
        int src_off_x, src_off_y;
        GLshort *v;
        char *vbo_offset;
        struct copy_args args;
        glamor_program *prog;
        const glamor_facet *copy_facet;
        int n;
        Bool ret = FALSE;
    
        glamor_make_current(glamor_priv);
    
        if (gc && !glamor_set_planemask(gc->depth, gc->planemask))
            goto bail_ctx;
    
        if (!glamor_set_alu(screen, gc ? gc->alu : GXcopy))
            goto bail_ctx;
    
        if (bitplane && !glamor_priv->can_copyplane)
            goto bail_ctx;
    
        if (bitplane) {
            prog = &glamor_priv->copy_plane_prog;
            copy_facet = &glamor_facet_copyplane;
        } else {
            prog = &glamor_priv->copy_area_prog;
            copy_facet = &glamor_facet_copyarea;
        }
    
        if (prog->failed)
            goto bail_ctx;
    
        if (!prog->prog) {
            if (!glamor_build_program(screen, prog,
                                      copy_facet, NULL, NULL, NULL))
                goto bail_ctx;
        }
    
        args.src_pixmap = src_pixmap;
        args.bitplane = bitplane;
    
        /* Set up the vertex buffers for the points */
    
        v = glamor_get_vbo_space(dst->pScreen, nbox * 8 * sizeof (int16_t), &vbo_offset);
    
        glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
        glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_SHORT, GL_FALSE,
                              2 * sizeof (GLshort), vbo_offset);
    
        for (n = 0; n < nbox; n++) {
            v[0] = box->x1; v[1] = box->y1;
            v[2] = box->x1; v[3] = box->y2;
            v[4] = box->x2; v[5] = box->y2;
            v[6] = box->x2; v[7] = box->y1;
            v += 8;
            box++;
        }
    
        glamor_put_vbo_space(screen);
    
        glamor_get_drawable_deltas(src, src_pixmap, &src_off_x, &src_off_y);
    
        glEnable(GL_SCISSOR_TEST);
    
        glamor_pixmap_loop(src_priv, src_box_index) {
            BoxPtr src_box = glamor_pixmap_box_at(src_priv, src_box_index);
    
            args.dx = dx + src_off_x - src_box->x1;
            args.dy = dy + src_off_y - src_box->y1;
            args.src = glamor_pixmap_fbo_at(src_priv, src_box_index);
    
            if (!glamor_use_program(dst_pixmap, gc, prog, &args))
                goto bail_ctx;
    
            glamor_pixmap_loop(dst_priv, dst_box_index) {
                if (!glamor_set_destination_drawable(dst, dst_box_index, FALSE, FALSE,
                                                     prog->matrix_uniform,
                                                     &dst_off_x, &dst_off_y))
                    goto bail_ctx;
    
                glScissor(dst_off_x - args.dx,
                          dst_off_y - args.dy,
                          src_box->x2 - src_box->x1,
                          src_box->y2 - src_box->y1);
    
                glamor_glDrawArrays_GL_QUADS(glamor_priv, nbox);
            }
        }
    
        ret = TRUE;
    
    bail_ctx:
        glDisable(GL_SCISSOR_TEST);
        glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
    
        return ret;
    }
    
    /**
     * Copies from the GPU to the GPU using a temporary pixmap in between,
     * to correctly handle overlapping copies.
     */
    
    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)
    {
        ScreenPtr screen = dst->pScreen;
        glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
        PixmapPtr tmp_pixmap;
        BoxRec bounds;
        int n;
        BoxPtr tmp_box;
    
        if (nbox == 0)
            return TRUE;
    
        /* Sanity check state to avoid getting halfway through and bailing
         * at the last second. Might be nice to have checks that didn't
         * involve setting state.
         */
        glamor_make_current(glamor_priv);
    
        if (gc && !glamor_set_planemask(gc->depth, gc->planemask))
            goto bail_ctx;
    
        if (!glamor_set_alu(screen, gc ? gc->alu : GXcopy))
            goto bail_ctx;
    
        /* Find the size of the area to copy
         */
        bounds = box[0];
        for (n = 1; n < nbox; n++) {
            bounds.x1 = min(bounds.x1, box[n].x1);
            bounds.x2 = max(bounds.x2, box[n].x2);
            bounds.y1 = min(bounds.y1, box[n].y1);
            bounds.y2 = max(bounds.y2, box[n].y2);
        }
    
        /* Allocate a suitable temporary pixmap
         */
        tmp_pixmap = glamor_create_pixmap(screen,
                                          bounds.x2 - bounds.x1,
                                          bounds.y2 - bounds.y1,
                                          src->depth, 0);
        if (!tmp_pixmap)
            goto bail;
    
        tmp_box = calloc(nbox, sizeof (BoxRec));
        if (!tmp_box)
            goto bail_pixmap;
    
        /* Convert destination boxes into tmp pixmap boxes
         */
        for (n = 0; n < nbox; n++) {
            tmp_box[n].x1 = box[n].x1 - bounds.x1;
            tmp_box[n].x2 = box[n].x2 - bounds.x1;
            tmp_box[n].y1 = box[n].y1 - bounds.y1;
            tmp_box[n].y2 = box[n].y2 - bounds.y1;
        }
    
        if (!glamor_copy_fbo_fbo_draw(src,
                                      &tmp_pixmap->drawable,
                                      NULL,
                                      tmp_box,
                                      nbox,
                                      dx + bounds.x1,
                                      dy + bounds.y1,
                                      FALSE, FALSE,
                                      0, NULL))
            goto bail_box;
    
        if (!glamor_copy_fbo_fbo_draw(&tmp_pixmap->drawable,
                                      dst,
                                      gc,
                                      box,
                                      nbox,
                                      -bounds.x1,
                                      -bounds.y1,
                                      FALSE, FALSE,
                                      bitplane, closure))
            goto bail_box;
    
        free(tmp_box);
    
        glamor_destroy_pixmap(tmp_pixmap);
    
        return TRUE;
    bail_box:
        free(tmp_box);
    bail_pixmap:
        glamor_destroy_pixmap(tmp_pixmap);
    bail:
        return FALSE;
    
    bail_ctx:
        return FALSE;
    }
    
    /**
     * Returns TRUE if the copy has to be implemented with
     * glamor_copy_fbo_fbo_temp() instead of glamor_copy_fbo_fbo().
     *
     * If the src and dst are in the same pixmap, then glamor_copy_fbo_fbo()'s
     * sampling would give undefined results (since the same texture would be
     * bound as an FBO destination and as a texture source).  However, if we
     * have GL_NV_texture_barrier, we can take advantage of the exception it
     * added:
     *
     *    "- If a texel has been written, then in order to safely read the result
     *       a texel fetch must be in a subsequent Draw separated by the command
     *
     *       void TextureBarrierNV(void);
     *
     *    TextureBarrierNV() will guarantee that writes have completed and caches
     *    have been invalidated before subsequent Draws are executed."
     */
    static Bool
    glamor_copy_needs_temp(DrawablePtr src,
                           DrawablePtr dst,
                           BoxPtr box,
                           int nbox,
                           int dx,
                           int dy)
    {
        PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
        PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
        ScreenPtr screen = dst->pScreen;
        glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
        int n;
        int dst_off_x, dst_off_y;
        int src_off_x, src_off_y;
        BoxRec bounds;
    
        if (src_pixmap != dst_pixmap)
            return FALSE;
    
        if (nbox == 0)
            return FALSE;
    
        if (!glamor_priv->has_nv_texture_barrier)
            return TRUE;
    
        glamor_get_drawable_deltas(src, src_pixmap, &src_off_x, &src_off_y);
        glamor_get_drawable_deltas(dst, dst_pixmap, &dst_off_x, &dst_off_y);
    
        bounds = box[0];
        for (n = 1; n < nbox; n++) {
            bounds.x1 = min(bounds.x1, box[n].x1);
            bounds.y1 = min(bounds.y1, box[n].y1);
    
            bounds.x2 = max(bounds.x2, box[n].x2);
            bounds.y2 = max(bounds.y2, box[n].y2);
        }
    
        /* Check to see if the pixmap-relative boxes overlap in both X and Y,
         * in which case we can't rely on NV_texture_barrier and must
         * make a temporary copy
         *
         *  dst.x1                     < src.x2 &&
         *  src.x1                     < dst.x2 &&
         *
         *  dst.y1                     < src.y2 &&
         *  src.y1                     < dst.y2
         */
        if (bounds.x1 + dst_off_x      < bounds.x2 + dx + src_off_x &&
            bounds.x1 + dx + src_off_x < bounds.x2 + dst_off_x &&
    
            bounds.y1 + dst_off_y      < bounds.y2 + dy + src_off_y &&
            bounds.y1 + dy + src_off_y < bounds.y2 + dst_off_y) {
            return TRUE;
        }
    
        glTextureBarrierNV();
    
        return FALSE;
    }
    
    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)
    {
        PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src);
        PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst);
        glamor_pixmap_private *src_priv = glamor_get_pixmap_private(src_pixmap);
        glamor_pixmap_private *dst_priv = glamor_get_pixmap_private(dst_pixmap);
    
        if (GLAMOR_PIXMAP_PRIV_HAS_FBO(dst_priv)) {
            if (GLAMOR_PIXMAP_PRIV_HAS_FBO(src_priv)) {
                if (glamor_copy_needs_temp(src, dst, box, nbox, dx, dy))
                    return glamor_copy_fbo_fbo_temp(src, dst, gc, box, nbox, dx, dy,
                                                    reverse, upsidedown, bitplane, closure);
                else
                    return glamor_copy_fbo_fbo_draw(src, dst, gc, box, nbox, dx, dy,
                                                    reverse, upsidedown, bitplane, closure);
            }
    
            return glamor_copy_cpu_fbo(src, dst, gc, box, nbox, dx, dy,
                                       reverse, upsidedown, bitplane, closure);
        } else if (GLAMOR_PIXMAP_PRIV_HAS_FBO(src_priv) &&
                   dst_priv->type != GLAMOR_DRM_ONLY &&
                   bitplane == 0) {
                return glamor_copy_fbo_cpu(src, dst, gc, box, nbox, dx, dy,
                                           reverse, upsidedown, bitplane, closure);
        }
        return FALSE;
    }
    
    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)
    {
        if (nbox == 0)
    	return;
    
        if (glamor_copy_gl(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure))
            return;
        glamor_copy_bail(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure);
    }
    
    RegionPtr
    glamor_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc,
                     int srcx, int srcy, int width, int height, int dstx, int dsty)
    {
        return miDoCopy(src, dst, gc,
                        srcx, srcy, width, height,
                        dstx, dsty, glamor_copy, 0, NULL);
    }
    
    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)
    {
        if ((bitplane & FbFullMask(src->depth)) == 0)
            return miHandleExposures(src, dst, gc,
                                     srcx, srcy, width, height, dstx, dsty);
        return miDoCopy(src, dst, gc,
                        srcx, srcy, width, height,
                        dstx, dsty, glamor_copy, bitplane, NULL);
    }
    
    void
    glamor_copy_window(WindowPtr window, DDXPointRec old_origin, RegionPtr src_region)
    {
        PixmapPtr pixmap = glamor_get_drawable_pixmap(&window->drawable);
        DrawablePtr drawable = &pixmap->drawable;
        RegionRec dst_region;
        int dx, dy;
    
        dx = old_origin.x - window->drawable.x;
        dy = old_origin.y - window->drawable.y;
        RegionTranslate(src_region, -dx, -dy);
    
        RegionNull(&dst_region);
    
        RegionIntersect(&dst_region, &window->borderClip, src_region);
    
    #ifdef COMPOSITE
        if (pixmap->screen_x || pixmap->screen_y)
            RegionTranslate(&dst_region, -pixmap->screen_x, -pixmap->screen_y);
    #endif
    
        miCopyRegion(drawable, drawable,
                     0, &dst_region, dx, dy, glamor_copy, 0, 0);
    
        RegionUninit(&dst_region);
    }