/* * Copyright © 2008 Intel Corporation * Copyright © 1998 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 Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD 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. * * Authors: * Eric Anholt * Zhigang Gong */ #include "glamor_priv.h" /** @file glamor_copyarea.c * * GC CopyArea implementation */ #ifndef GLAMOR_GLES2 static Bool glamor_copy_n_to_n_fbo_blit(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy) { ScreenPtr screen = dst->pScreen; PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst); PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src); glamor_pixmap_private *src_pixmap_priv, *dst_pixmap_priv; glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); glamor_gl_dispatch *dispatch; int dst_x_off, dst_y_off, src_x_off, src_y_off, i; int fbo_x_off, fbo_y_off; int src_fbo_x_off, src_fbo_y_off; if (!glamor_priv->has_fbo_blit) { glamor_delayed_fallback(screen, "no EXT_framebuffer_blit\n"); return FALSE; } src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); if (gc) { if (gc->alu != GXcopy) { glamor_delayed_fallback(screen, "non-copy ALU\n"); return FALSE; } } if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(src_pixmap_priv)) { glamor_delayed_fallback(screen, "no src fbo\n"); return FALSE; } if (glamor_set_destination_pixmap(dst_pixmap)) return FALSE; pixmap_priv_get_fbo_off(dst_pixmap_priv, &fbo_x_off, &fbo_y_off); pixmap_priv_get_fbo_off(src_pixmap_priv, &src_fbo_x_off, &src_fbo_y_off); dispatch = glamor_get_dispatch(glamor_priv); dispatch->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, src_pixmap_priv->base.fbo->fb); glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, &dst_y_off); glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, &src_y_off); dst_x_off += fbo_x_off; dst_y_off += fbo_y_off; src_y_off += dy + src_fbo_y_off; src_x_off += src_fbo_x_off; for (i = 0; i < nbox; i++) { if (glamor_priv->yInverted) { dispatch->glBlitFramebuffer((box[i].x1 + dx + src_x_off), (box[i].y1 + src_y_off), (box[i].x2 + dx + src_x_off), (box[i].y2 + src_y_off), (box[i].x1 + dst_x_off), (box[i].y1 + dst_y_off), (box[i].x2 + dst_x_off), (box[i].y2 + dst_y_off), GL_COLOR_BUFFER_BIT, GL_NEAREST); } else { int flip_dst_y1 = dst_pixmap->drawable.height - (box[i].y2 + dst_y_off); int flip_dst_y2 = dst_pixmap->drawable.height - (box[i].y1 + dst_y_off); int flip_src_y1 = src_pixmap->drawable.height - (box[i].y2 + src_y_off); int flip_src_y2 = src_pixmap->drawable.height - (box[i].y1 + src_y_off); dispatch->glBlitFramebuffer(box[i].x1 + dx + src_x_off, flip_src_y1, box[i].x2 + dx + src_x_off, flip_src_y2, box[i].x1 + dst_x_off, flip_dst_y1, box[i].x2 + dst_x_off, flip_dst_y2, GL_COLOR_BUFFER_BIT, GL_NEAREST); } } glamor_put_dispatch(glamor_priv); glamor_priv->state = BLIT_STATE; return TRUE; } #endif static Bool glamor_copy_n_to_n_textured(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy) { glamor_screen_private *glamor_priv = glamor_get_screen_private(dst->pScreen); glamor_gl_dispatch *dispatch; PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src); PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst); int i; float vertices[8], texcoords[8]; glamor_pixmap_private *src_pixmap_priv; glamor_pixmap_private *dst_pixmap_priv; int src_x_off, src_y_off, dst_x_off, dst_y_off; GLfloat dst_xscale, dst_yscale, src_xscale, src_yscale; src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, &src_y_off); glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, &dst_y_off); if (!src_pixmap_priv->base.gl_fbo) { /* Optimize when the source doesn't have an FBO, just upload the data directly to the dest FBO */ int src_stride = src_pixmap->devKind; int bpp = src_pixmap->drawable.bitsPerPixel; void *src_data = NULL; if (src->bitsPerPixel != dst->bitsPerPixel) { DEBUGF("Non-matching bpp\n"); return FALSE; } if (src->bitsPerPixel < 8) { DEBUGF("bpp < 8\n"); return FALSE; } if (gc && !(gc->alu == GXcopy && glamor_pm_is_solid(src, gc->planemask))) { DEBUGF("non gxcopy and solid\n"); return FALSE; } for (i = 0; i < nbox; i++) { int x = box[i].x1 + dst_x_off; int y = box[i].y1 + dst_y_off; int w = box[i].x2 - box[i].x1; int h = box[i].y2 - box[i].y1; src_data = (char *)src_pixmap->devPrivate.ptr + (box[i].y1 + dy + src_y_off) * src_stride + (box[i].x1 + dx + src_x_off) * (bpp / 8); if (!glamor_upload_sub_pixmap_to_texture(dst_pixmap, x, y, w, h, src_stride, src_data, 0)) { ErrorF("Failed to upload the sub pixmap to dst\n"); return FALSE; } } return TRUE; } pixmap_priv_get_dest_scale(dst_pixmap_priv, &dst_xscale, &dst_yscale); pixmap_priv_get_scale(src_pixmap_priv, &src_xscale, &src_yscale); dispatch = glamor_get_dispatch(glamor_priv); glamor_set_destination_pixmap_priv_nc(dst_pixmap_priv); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); dx += src_x_off; dy += src_y_off; dispatch->glActiveTexture(GL_TEXTURE0); dispatch->glBindTexture(GL_TEXTURE_2D, src_pixmap_priv->base.fbo->tex); #ifndef GLAMOR_GLES2 dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); #endif dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), texcoords); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); dispatch->glUseProgram(glamor_priv->finish_access_prog[0]); dispatch->glUniform1i(glamor_priv->finish_access_revert[0], REVERT_NONE); dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[0], SWAP_NONE_UPLOADING); for (i = 0; i < nbox; i++) { glamor_set_normalize_vcoords(dst_pixmap_priv, dst_xscale, dst_yscale, box[i].x1 + dst_x_off, box[i].y1 + dst_y_off, box[i].x2 + dst_x_off, box[i].y2 + dst_y_off, glamor_priv->yInverted, vertices); glamor_set_normalize_tcoords(src_pixmap_priv, src_xscale, src_yscale, box[i].x1 + dx, box[i].y1 + dy, box[i].x2 + dx, box[i].y2 + dy, glamor_priv->yInverted, texcoords); dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); /* The source texture is bound to a fbo, we have to flush it here. */ glamor_put_dispatch(glamor_priv); glamor_priv->state = RENDER_STATE; glamor_priv->render_idle_cnt = 0; return TRUE; } static Bool __glamor_copy_n_to_n(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure) { PixmapPtr dst_pixmap, src_pixmap, temp_pixmap = NULL; DrawablePtr temp_src = src; glamor_pixmap_private *dst_pixmap_priv, *src_pixmap_priv; glamor_screen_private *glamor_priv; BoxRec bound; ScreenPtr screen; int temp_dx = dx; int temp_dy = dy; int src_x_off, src_y_off, dst_x_off, dst_y_off; int i; int overlaped = 0; Bool ret = FALSE; dst_pixmap = glamor_get_drawable_pixmap(dst); dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); src_pixmap = glamor_get_drawable_pixmap(src); src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); screen = dst_pixmap->drawable.pScreen; glamor_priv = glamor_get_screen_private(dst->pScreen); glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, &src_y_off); glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, &dst_y_off); if (src_pixmap_priv->base.fbo && src_pixmap_priv->base.fbo->fb == dst_pixmap_priv->base.fbo->fb) { int x_shift = abs(src_x_off - dx - dst_x_off); int y_shift = abs(src_y_off - dy - dst_y_off); for (i = 0; i < nbox; i++) { if (x_shift < abs(box[i].x2 - box[i].x1) && y_shift < abs(box[i].y2 - box[i].y1)) { overlaped = 1; break; } } } DEBUGF("Copy %d %d %dx%d dx %d dy %d from %p to %p \n", box[0].x1, box[0].y1, box[0].x2 - box[0].x1, box[0].y2 - box[0].y1, dx, dy, src_pixmap, dst_pixmap); #ifndef GLAMOR_GLES2 if (!overlaped && (glamor_priv->state != RENDER_STATE || !src_pixmap_priv->base.gl_tex || !dst_pixmap_priv->base.gl_tex) && glamor_copy_n_to_n_fbo_blit(src, dst, gc, box, nbox, dx, dy)) { ret = TRUE; goto done; } #endif glamor_calculate_boxes_bound(&bound, box, nbox); /* Overlaped indicate the src and dst are the same pixmap. */ if (overlaped || (!GLAMOR_PIXMAP_PRIV_HAS_FBO(src_pixmap_priv) && (((bound.x2 - bound.x1) * (bound.y2 - bound.y1) * 4 > src_pixmap->drawable.width * src_pixmap->drawable.height) || !(glamor_check_fbo_size(glamor_priv, src_pixmap->drawable.width, src_pixmap->drawable.height))))) { temp_pixmap = glamor_create_pixmap(screen, bound.x2 - bound.x1, bound.y2 - bound.y1, src_pixmap-> drawable.depth, overlaped ? 0 : GLAMOR_CREATE_PIXMAP_CPU); assert(bound.x2 - bound.x1 <= glamor_priv->max_fbo_size); assert(bound.y2 - bound.y1 <= glamor_priv->max_fbo_size); if (!temp_pixmap) goto done; glamor_translate_boxes(box, nbox, -bound.x1, -bound.y1); temp_src = &temp_pixmap->drawable; if (overlaped) glamor_copy_n_to_n_textured(src, temp_src, gc, box, nbox, temp_dx + bound.x1, temp_dy + bound.y1); else fbCopyNtoN(src, temp_src, gc, box, nbox, temp_dx + bound.x1, temp_dy + bound.y1, reverse, upsidedown, bitplane, closure); glamor_translate_boxes(box, nbox, bound.x1, bound.y1); temp_dx = -bound.x1; temp_dy = -bound.y1; } else { temp_dx = dx; temp_dy = dy; temp_src = src; } if (glamor_copy_n_to_n_textured (temp_src, dst, gc, box, nbox, temp_dx, temp_dy)) { ret = TRUE; } done: if (temp_src != src) glamor_destroy_pixmap(temp_pixmap); return ret; } static Bool _glamor_copy_n_to_n(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure, Bool fallback) { PixmapPtr dst_pixmap, src_pixmap; glamor_pixmap_private *dst_pixmap_priv, *src_pixmap_priv; glamor_screen_private *glamor_priv; glamor_gl_dispatch *dispatch; BoxPtr extent; RegionRec region; int src_x_off, src_y_off, dst_x_off, dst_y_off; Bool ok = FALSE; int force_clip = 0; if (nbox == 0) return TRUE; dst_pixmap = glamor_get_drawable_pixmap(dst); dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); src_pixmap = glamor_get_drawable_pixmap(src); src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); glamor_priv = glamor_get_screen_private(dst->pScreen); DEBUGF("Copy %d %d %dx%d dx %d dy %d from %p to %p \n", box[0].x1, box[0].y1, box[0].x2 - box[0].x1, box[0].y2 - box[0].y1, dx, dy, src_pixmap, dst_pixmap); if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dst_pixmap_priv)) goto fall_back; if (gc) { if (!glamor_set_planemask(dst_pixmap, gc->planemask)) goto fall_back; dispatch = glamor_get_dispatch(glamor_priv); if (!glamor_set_alu(dispatch, gc->alu)) { glamor_put_dispatch(glamor_priv); goto fail_noregion; } glamor_put_dispatch(glamor_priv); } if (!src_pixmap_priv) { glamor_set_pixmap_type(src_pixmap, GLAMOR_MEMORY); src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); } glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, &src_y_off); glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, &dst_y_off); RegionInitBoxes(®ion, box, nbox); extent = RegionExtents(®ion); if (!glamor_check_fbo_size(glamor_priv, extent->x2 - extent->x1, extent->y2 - extent->y1) && (src_pixmap_priv->type == GLAMOR_MEMORY || (src_pixmap_priv == dst_pixmap_priv))) { force_clip = 1; } if (force_clip || dst_pixmap_priv->type == GLAMOR_TEXTURE_LARGE || src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { glamor_pixmap_clipped_regions *clipped_dst_regions; int n_dst_region, i, j; PixmapPtr temp_source_pixmap; glamor_pixmap_private *temp_source_priv = NULL; RegionTranslate(®ion, dst_x_off, dst_y_off); if (!force_clip) clipped_dst_regions = glamor_compute_clipped_regions(dst_pixmap_priv, ®ion, &n_dst_region, 0, reverse, upsidedown); else clipped_dst_regions = glamor_compute_clipped_regions_ext(dst_pixmap_priv, ®ion, &n_dst_region, glamor_priv->max_fbo_size, glamor_priv->max_fbo_size, reverse, upsidedown); for(i = 0; i < n_dst_region; i++) { int n_src_region; glamor_pixmap_clipped_regions *clipped_src_regions; BoxPtr current_boxes; int n_current_boxes; SET_PIXMAP_FBO_CURRENT(dst_pixmap_priv, clipped_dst_regions[i].block_idx); temp_source_pixmap = NULL; if (src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { RegionTranslate(clipped_dst_regions[i].region, -dst_x_off + src_x_off + dx, -dst_y_off + src_y_off + dy); clipped_src_regions = glamor_compute_clipped_regions(src_pixmap_priv, clipped_dst_regions[i].region, &n_src_region, 0, reverse, upsidedown); DEBUGF("Source is large pixmap.\n"); for (j = 0; j < n_src_region; j++) { if (src_pixmap_priv != dst_pixmap_priv) SET_PIXMAP_FBO_CURRENT(src_pixmap_priv, clipped_src_regions[j].block_idx); else if (src_pixmap_priv == dst_pixmap_priv && clipped_src_regions[j].block_idx != clipped_dst_regions[i].block_idx) { /* source and the dest are the same, but need different block_idx. * we create a empty pixmap and fill the required source fbo and box to * it. It's a little hacky, but avoid extra copy. */ temp_source_pixmap = glamor_create_pixmap(src->pScreen, 0, 0, src->depth, 0); if (!temp_source_pixmap) { ok = FALSE; goto fail; } src->pScreen->ModifyPixmapHeader(temp_source_pixmap, src_pixmap->drawable.width, src_pixmap->drawable.height, 0, 0, src_pixmap->devKind, NULL); temp_source_priv = glamor_get_pixmap_private(temp_source_pixmap); *temp_source_priv = *src_pixmap_priv; temp_source_priv->large.box = src_pixmap_priv->large.box_array[clipped_src_regions[j].block_idx]; temp_source_priv->base.fbo = src_pixmap_priv->large.fbo_array[clipped_src_regions[j].block_idx]; temp_source_priv->base.pixmap = temp_source_pixmap; } assert(temp_source_pixmap || !(src_pixmap_priv == dst_pixmap_priv && (clipped_src_regions[j].block_idx != clipped_dst_regions[i].block_idx))); RegionTranslate(clipped_src_regions[j].region, -src_x_off - dx, -src_y_off - dy); current_boxes = RegionRects(clipped_src_regions[j].region); n_current_boxes = RegionNumRects(clipped_src_regions[j].region); DEBUGF("dst pixmap fbo idx %d src pixmap fbo idx %d \n", clipped_dst_regions[i].block_idx, clipped_src_regions[j].block_idx); DEBUGF("Copy %d %d %d %d dx %d dy %d from %p to %p \n", current_boxes[0].x1, current_boxes[0].y1, current_boxes[0].x2, current_boxes[0].y2, dx, dy, src_pixmap, dst_pixmap); if (!temp_source_pixmap) ok = __glamor_copy_n_to_n(src, dst, gc, current_boxes, n_current_boxes, dx, dy, reverse, upsidedown, bitplane, closure); else { ok = __glamor_copy_n_to_n(&temp_source_pixmap->drawable, dst, gc, current_boxes, n_current_boxes, dx, dy, reverse, upsidedown, bitplane, closure); temp_source_priv->type = GLAMOR_MEMORY; temp_source_priv->base.fbo = NULL; glamor_destroy_pixmap(temp_source_pixmap); temp_source_pixmap = NULL; } RegionDestroy(clipped_src_regions[j].region); if (!ok) { assert(0); goto fail; } } if (n_src_region == 0) ok = TRUE; free(clipped_src_regions); } else { RegionTranslate(clipped_dst_regions[i].region, - dst_x_off, - dst_y_off); current_boxes = RegionRects(clipped_dst_regions[i].region); n_current_boxes = RegionNumRects(clipped_dst_regions[i].region); DEBUGF("dest pixmap fbo idx %d \n", clipped_dst_regions[i].block_idx); DEBUGF("Copy %d %d %d %d dx %d dy %d from %p to %p \n", current_boxes[0].x1, current_boxes[0].y1, current_boxes[0].x2, current_boxes[0].y2, dx, dy, src_pixmap, dst_pixmap); ok = __glamor_copy_n_to_n(src, dst, gc, current_boxes, n_current_boxes, dx, dy, reverse, upsidedown, bitplane, closure); } RegionDestroy(clipped_dst_regions[i].region); } if (n_dst_region == 0) ok = TRUE; free(clipped_dst_regions); } else { ok = __glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure); } fail: RegionUninit(®ion); fail_noregion: dispatch = glamor_get_dispatch(glamor_priv); glamor_set_alu(dispatch, GXcopy); glamor_put_dispatch(glamor_priv); if (ok) return TRUE; fall_back: if (!fallback && glamor_ddx_fallback_check_pixmap(src) && glamor_ddx_fallback_check_pixmap(dst)) goto done; if (src_pixmap_priv->type == GLAMOR_DRM_ONLY || dst_pixmap_priv->type == GLAMOR_DRM_ONLY) { LogMessage(X_WARNING, "Access a DRM only pixmap is not allowed within glamor.\n"); return TRUE; } glamor_report_delayed_fallbacks(src->pScreen); glamor_report_delayed_fallbacks(dst->pScreen); glamor_fallback("from %p to %p (%c,%c)\n", src, dst, glamor_get_drawable_location(src), glamor_get_drawable_location(dst)); if (glamor_prepare_access(dst, GLAMOR_ACCESS_RW) && glamor_prepare_access(src, GLAMOR_ACCESS_RO) && glamor_prepare_access_gc(gc)) { fbCopyNtoN(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure); } glamor_finish_access_gc(gc); glamor_finish_access(src); glamor_finish_access(dst); ok = TRUE; done: glamor_clear_delayed_fallbacks(src->pScreen); glamor_clear_delayed_fallbacks(dst->pScreen); return ok; } RegionPtr glamor_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, int srcx, int srcy, int width, int height, int dstx, int dsty) { RegionPtr region; region = miDoCopy(src, dst, gc, srcx, srcy, width, height, dstx, dsty, glamor_copy_n_to_n, 0, NULL); return region; } void glamor_copy_n_to_n(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure) { _glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure, TRUE); } Bool glamor_copy_n_to_n_nf(DrawablePtr src, DrawablePtr dst, GCPtr gc, BoxPtr box, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure) { return _glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure, FALSE); }