/* * Copyright © 2001 Keith Packard * Copyright © 2008 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Authors: * Eric Anholt * Zhigang Gong * */ #include #include "glamor_priv.h" /** * Sets the offsets to add to coordinates to make them address the same bits in * the backing drawable. These coordinates are nonzero only for redirected * windows. */ void glamor_get_drawable_deltas(DrawablePtr drawable, PixmapPtr pixmap, int *x, int *y) { #ifdef COMPOSITE if (drawable->type == DRAWABLE_WINDOW) { *x = -pixmap->screen_x; *y = -pixmap->screen_y; return; } #endif *x = 0; *y = 0; } void glamor_pixmap_init(ScreenPtr screen) { } void glamor_pixmap_fini(ScreenPtr screen) { } void glamor_set_destination_pixmap_fbo(glamor_pixmap_fbo * fbo, int x0, int y0, int width, int height) { glamor_gl_dispatch *dispatch = glamor_get_dispatch(fbo->glamor_priv); dispatch->glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb); #ifndef GLAMOR_GLES2 dispatch->glMatrixMode(GL_PROJECTION); dispatch->glLoadIdentity(); dispatch->glMatrixMode(GL_MODELVIEW); dispatch->glLoadIdentity(); #endif dispatch->glViewport(x0, y0, width, height); glamor_put_dispatch(fbo->glamor_priv); } void glamor_set_destination_pixmap_priv_nc(glamor_pixmap_private * pixmap_priv) { int w,h; PIXMAP_PRIV_GET_ACTUAL_SIZE(pixmap_priv, w, h); glamor_set_destination_pixmap_fbo(pixmap_priv->base.fbo, 0, 0, w, h); } int glamor_set_destination_pixmap_priv(glamor_pixmap_private * pixmap_priv) { if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) return -1; glamor_set_destination_pixmap_priv_nc(pixmap_priv); return 0; } int glamor_set_destination_pixmap(PixmapPtr pixmap) { int err; glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); err = glamor_set_destination_pixmap_priv(pixmap_priv); return err; } Bool glamor_set_planemask(PixmapPtr pixmap, unsigned long planemask) { if (glamor_pm_is_solid(&pixmap->drawable, planemask)) { return GL_TRUE; } glamor_fallback("unsupported planemask %lx\n", planemask); return GL_FALSE; } Bool glamor_set_alu(struct glamor_gl_dispatch *dispatch, unsigned char alu) { #ifndef GLAMOR_GLES2 if (alu == GXcopy) { dispatch->glDisable(GL_COLOR_LOGIC_OP); return TRUE; } dispatch->glEnable(GL_COLOR_LOGIC_OP); switch (alu) { case GXclear: dispatch->glLogicOp(GL_CLEAR); break; case GXand: dispatch->glLogicOp(GL_AND); break; case GXandReverse: dispatch->glLogicOp(GL_AND_REVERSE); break; case GXandInverted: dispatch->glLogicOp(GL_AND_INVERTED); break; case GXnoop: dispatch->glLogicOp(GL_NOOP); break; case GXxor: dispatch->glLogicOp(GL_XOR); break; case GXor: dispatch->glLogicOp(GL_OR); break; case GXnor: dispatch->glLogicOp(GL_NOR); break; case GXequiv: dispatch->glLogicOp(GL_EQUIV); break; case GXinvert: dispatch->glLogicOp(GL_INVERT); break; case GXorReverse: dispatch->glLogicOp(GL_OR_REVERSE); break; case GXcopyInverted: dispatch->glLogicOp(GL_COPY_INVERTED); break; case GXorInverted: dispatch->glLogicOp(GL_OR_INVERTED); break; case GXnand: dispatch->glLogicOp(GL_NAND); break; case GXset: dispatch->glLogicOp(GL_SET); break; default: glamor_fallback("unsupported alu %x\n", alu); return FALSE; } #else if (alu != GXcopy) return FALSE; #endif return TRUE; } static void * _glamor_color_convert_a1_a8(void *src_bits, void *dst_bits, int w, int h, int stride, int revert) { PictFormatShort dst_format, src_format; pixman_image_t *dst_image; pixman_image_t *src_image; int src_stride; if (revert == REVERT_UPLOADING_A1) { src_format = PICT_a1; dst_format = PICT_a8; src_stride = PixmapBytePad(w, 1); } else { dst_format = PICT_a1; src_format = PICT_a8; src_stride = (((w * 8 + 7) / 8) + 3) & ~3; } dst_image = pixman_image_create_bits(dst_format, w, h, dst_bits, stride); if (dst_image == NULL) { return NULL; } src_image = pixman_image_create_bits(src_format, w, h, src_bits, src_stride); if (src_image == NULL) { pixman_image_unref(dst_image); return NULL; } pixman_image_composite(PictOpSrc, src_image, NULL, dst_image, 0, 0, 0, 0, 0, 0, w,h); pixman_image_unref(src_image); pixman_image_unref(dst_image); return dst_bits; } #define ADJUST_BITS(d, src_bits, dst_bits) (((dst_bits) == (src_bits)) ? (d) : \ (((dst_bits) > (src_bits)) ? \ (((d) << ((dst_bits) - (src_bits))) \ + (( 1 << ((dst_bits) - (src_bits))) >> 1)) \ : ((d) >> ((src_bits) - (dst_bits))))) #define GLAMOR_DO_CONVERT(src, dst, no_alpha, swap, \ a_shift_src, a_bits_src, \ b_shift_src, b_bits_src, \ g_shift_src, g_bits_src, \ r_shift_src, r_bits_src, \ a_shift, a_bits, \ b_shift, b_bits, \ g_shift, g_bits, \ r_shift, r_bits) \ { \ typeof(src) a,b,g,r; \ typeof(src) a_mask_src, b_mask_src, g_mask_src, r_mask_src;\ a_mask_src = (((1 << (a_bits_src)) - 1) << a_shift_src);\ b_mask_src = (((1 << (b_bits_src)) - 1) << b_shift_src);\ g_mask_src = (((1 << (g_bits_src)) - 1) << g_shift_src);\ r_mask_src = (((1 << (r_bits_src)) - 1) << r_shift_src);\ if (no_alpha) \ a = (a_mask_src) >> (a_shift_src); \ else \ a = ((src) & (a_mask_src)) >> (a_shift_src); \ b = ((src) & (b_mask_src)) >> (b_shift_src); \ g = ((src) & (g_mask_src)) >> (g_shift_src); \ r = ((src) & (r_mask_src)) >> (r_shift_src); \ a = ADJUST_BITS(a, a_bits_src, a_bits); \ b = ADJUST_BITS(b, b_bits_src, b_bits); \ g = ADJUST_BITS(g, g_bits_src, g_bits); \ r = ADJUST_BITS(r, r_bits_src, r_bits); \ if (swap == 0) \ (*dst) = ((a) << (a_shift)) | ((b) << (b_shift)) | ((g) << (g_shift)) | ((r) << (r_shift)); \ else \ (*dst) = ((a) << (a_shift)) | ((r) << (b_shift)) | ((g) << (g_shift)) | ((b) << (r_shift)); \ } static void * _glamor_color_revert_x2b10g10r10(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) { int x,y; unsigned int *words, *saved_words, *source_words; int swap = !(swap_rb == SWAP_NONE_DOWNLOADING || swap_rb == SWAP_NONE_UPLOADING); source_words = src_bits; words = dst_bits; saved_words = words; for (y = 0; y < h; y++) { DEBUGF("Line %d : ", y); for (x = 0; x < w; x++) { unsigned int pixel = source_words[x]; if (revert == REVERT_DOWNLOADING_2_10_10_10) GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, 24, 8, 16, 8, 8, 8, 0, 8, 30, 2, 20, 10, 10, 10, 0, 10) else GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, 30, 2, 20, 10, 10, 10, 0, 10, 24, 8, 16, 8, 8, 8, 0, 8); DEBUGF("%x:%x ", pixel, words[x]); } DEBUGF("\n"); words += stride / sizeof(*words); source_words += stride / sizeof(*words); } DEBUGF("\n"); return saved_words; } static void * _glamor_color_revert_x1b5g5r5(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) { int x,y; unsigned short *words, *saved_words, *source_words; int swap = !(swap_rb == SWAP_NONE_DOWNLOADING || swap_rb == SWAP_NONE_UPLOADING); words = dst_bits; source_words = src_bits; saved_words = words; for (y = 0; y < h; y++) { DEBUGF("Line %d : ", y); for (x = 0; x < w; x++) { unsigned short pixel = source_words[x]; if (revert == REVERT_DOWNLOADING_1_5_5_5) GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, 0, 1, 1, 5, 6, 5, 11, 5, 15, 1, 10, 5, 5, 5, 0, 5) else GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, 15, 1, 10, 5, 5, 5, 0, 5, 0, 1, 1, 5, 6, 5, 11, 5); DEBUGF("%04x:%04x ", pixel, words[x]); } DEBUGF("\n"); words += stride / sizeof(*words); source_words += stride / sizeof(*words); } DEBUGF("\n"); return saved_words; } /* * This function is to convert an unsupported color format to/from a * supported GL format. * Here are the current scenarios: * * @no_alpha: * If it is set, then we need to wire the alpha value to 1. * @revert: REVERT_DOWNLOADING_A1 : convert an Alpha8 buffer to a A1 buffer. REVERT_UPLOADING_A1 : convert an A1 buffer to an Alpha8 buffer REVERT_DOWNLOADING_2_10_10_10 : convert r10G10b10X2 to X2B10G10R10 REVERT_UPLOADING_2_10_10_10 : convert X2B10G10R10 to R10G10B10X2 REVERT_DOWNLOADING_1_5_5_5 : convert B5G5R5X1 to X1R5G5B5 REVERT_UPLOADING_1_5_5_5 : convert X1R5G5B5 to B5G5R5X1 @swap_rb: if we have the swap_rb set, then we need to swap the R and B's position. * */ static void * glamor_color_convert_to_bits(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) { if (revert == REVERT_DOWNLOADING_A1 || revert == REVERT_UPLOADING_A1) { return _glamor_color_convert_a1_a8(src_bits, dst_bits, w, h, stride, revert); } else if (revert == REVERT_DOWNLOADING_2_10_10_10 || revert == REVERT_UPLOADING_2_10_10_10) { return _glamor_color_revert_x2b10g10r10(src_bits, dst_bits, w, h, stride, no_alpha, revert, swap_rb); } else if (revert == REVERT_DOWNLOADING_1_5_5_5 || revert == REVERT_UPLOADING_1_5_5_5) { return _glamor_color_revert_x1b5g5r5(src_bits, dst_bits, w, h, stride, no_alpha, revert, swap_rb); } else ErrorF("convert a non-supported mode %x.\n", revert); return NULL; } /** * Upload pixmap to a specified texture. * This texture may not be the one attached to it. **/ int in_restore = 0; static void __glamor_upload_pixmap_to_texture(PixmapPtr pixmap, unsigned int *tex, GLenum format, GLenum type, int x, int y, int w, int h, void *bits, int pbo) { glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); glamor_gl_dispatch *dispatch; int non_sub = 0; unsigned int iformat = 0; dispatch = glamor_get_dispatch(glamor_priv); if (*tex == 0) { dispatch->glGenTextures(1, tex); if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) gl_iformat_for_depth(pixmap->drawable.depth, &iformat); else iformat = format; non_sub = 1; assert(x == 0 && y == 0); } dispatch->glBindTexture(GL_TEXTURE_2D, *tex); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); dispatch->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); if (bits == NULL) dispatch->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); if (non_sub) dispatch->glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0, format, type, bits); else dispatch->glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, format, type, bits); if (bits == NULL) dispatch->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glamor_put_dispatch(glamor_priv); } static Bool _glamor_upload_bits_to_pixmap_texture(PixmapPtr pixmap, GLenum format, GLenum type, int no_alpha, int revert, int swap_rb, int x, int y, int w, int h, int stride, void* bits, int pbo) { glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); glamor_gl_dispatch *dispatch; static float vertices[8]; static float texcoords[8] = { 0, 1, 1, 1, 1, 0, 0, 0 }; static float texcoords_inv[8] = { 0, 0, 1, 0, 1, 1, 0, 1 }; float *ptexcoords; float dst_xscale, dst_yscale; GLuint tex = 0; int need_flip; int need_free_bits = 0; need_flip = !glamor_priv->yInverted; if (bits == NULL) goto ready_to_upload; if (revert > REVERT_NORMAL) { /* XXX if we are restoring the pixmap, then we may not need to allocate * new buffer */ void *converted_bits; if (pixmap->drawable.depth == 1) stride = (((w * 8 + 7) / 8) + 3) & ~3; converted_bits = malloc(h * stride); if (converted_bits == NULL) return FALSE; bits = glamor_color_convert_to_bits(bits, converted_bits, w, h, stride, no_alpha, revert, swap_rb); if (bits == NULL) { ErrorF("Failed to convert pixmap no_alpha %d," "revert mode %d, swap mode %d\n", no_alpha, revert, swap_rb); return FALSE; } no_alpha = 0; revert = REVERT_NONE; swap_rb = SWAP_NONE_UPLOADING; need_free_bits = TRUE; } ready_to_upload: /* Try fast path firstly, upload the pixmap to the texture attached * to the fbo directly. */ if (no_alpha == 0 && revert == REVERT_NONE && swap_rb == SWAP_NONE_UPLOADING && !need_flip #ifdef WALKAROUND_LARGE_TEXTURE_MAP && pixmap_priv->type != GLAMOR_TEXTURE_LARGE #endif ) { int fbo_x_off, fbo_y_off; assert(pixmap_priv->base.fbo->tex); pixmap_priv_get_fbo_off(pixmap_priv, &fbo_x_off, &fbo_y_off); assert(x + fbo_x_off >= 0 && y + fbo_y_off >= 0); assert(x + fbo_x_off + w <= pixmap_priv->base.fbo->width); assert(y + fbo_y_off + h <= pixmap_priv->base.fbo->height); __glamor_upload_pixmap_to_texture(pixmap, &pixmap_priv->base.fbo->tex, format, type, x + fbo_x_off, y + fbo_y_off, w, h, bits, pbo); return TRUE; } if (need_flip) ptexcoords = texcoords; else ptexcoords = texcoords_inv; pixmap_priv_get_dest_scale(pixmap_priv, &dst_xscale, &dst_yscale); glamor_set_normalize_vcoords(pixmap_priv, dst_xscale, dst_yscale, x, y, x + w, y + h, glamor_priv->yInverted, vertices); /* Slow path, we need to flip y or wire alpha to 1. */ dispatch = glamor_get_dispatch(glamor_priv); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), ptexcoords); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); glamor_set_destination_pixmap_priv_nc(pixmap_priv); __glamor_upload_pixmap_to_texture(pixmap, &tex, format, type, 0, 0, w, h, bits, pbo); dispatch->glActiveTexture(GL_TEXTURE0); dispatch->glBindTexture(GL_TEXTURE_2D, tex); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); #ifndef GLAMOR_GLES2 dispatch->glEnable(GL_TEXTURE_2D); #endif dispatch->glUseProgram(glamor_priv->finish_access_prog[no_alpha]); dispatch->glUniform1i(glamor_priv-> finish_access_revert[no_alpha], revert); dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[no_alpha], swap_rb); dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); #ifndef GLAMOR_GLES2 dispatch->glDisable(GL_TEXTURE_2D); #endif dispatch->glUseProgram(0); dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); dispatch->glDeleteTextures(1, &tex); dispatch->glBindFramebuffer(GL_FRAMEBUFFER, 0); glamor_put_dispatch(glamor_priv); if (need_free_bits) free(bits); return TRUE; } /* * Prepare to upload a pixmap to texture memory. * no_alpha equals 1 means the format needs to wire alpha to 1. * Two condtion need to setup a fbo for a pixmap * 1. !yInverted, we need to do flip if we are not yInverted. * 2. no_alpha != 0, we need to wire the alpha. * */ static int glamor_pixmap_upload_prepare(PixmapPtr pixmap, GLenum format, int no_alpha, int revert, int swap_rb) { int flag = 0; glamor_pixmap_private *pixmap_priv; glamor_screen_private *glamor_priv; glamor_pixmap_fbo *fbo; GLenum iformat; pixmap_priv = glamor_get_pixmap_private(pixmap); glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); if (pixmap_priv->base.gl_fbo) return 0; if (pixmap_priv->base.fbo && (pixmap_priv->base.fbo->width < pixmap->drawable.width || pixmap_priv->base.fbo->height < pixmap->drawable.height)) { fbo = glamor_pixmap_detach_fbo(pixmap_priv); glamor_destroy_fbo(fbo); } if (pixmap_priv->base.fbo && pixmap_priv->base.fbo->fb) return 0; if (!(no_alpha || (revert == REVERT_NORMAL) || (swap_rb != SWAP_NONE_UPLOADING) || !glamor_priv->yInverted)) { /* We don't need a fbo, a simple texture uploading should work. */ flag = GLAMOR_CREATE_FBO_NO_FBO; } if ((flag == GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->base.fbo && pixmap_priv->base.fbo->tex) || (flag == 0 && pixmap_priv->base.fbo && pixmap_priv->base.fbo->fb)) return 0; if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) gl_iformat_for_depth(pixmap->drawable.depth, &iformat); else iformat = format; if (!glamor_pixmap_ensure_fbo(pixmap, iformat, flag)) return -1; return 0; } /* * upload sub region to a large region. * */ static void glamor_put_bits(char *dst_bits, int dst_stride, char *src_bits, int src_stride, int bpp, int x, int y, int w, int h) { int j; int byte_per_pixel; byte_per_pixel = bpp / 8; src_bits += y * src_stride + (x * byte_per_pixel); for(j = y; j < y + h; j++) { memcpy(dst_bits, src_bits, w * byte_per_pixel); src_bits += src_stride; dst_bits += dst_stride; } } /* * download sub region from a large region. */ static void glamor_get_bits(char *dst_bits, int dst_stride, char *src_bits, int src_stride, int bpp, int x, int y, int w, int h) { int j; int byte_per_pixel; byte_per_pixel = bpp / 8; dst_bits += y * dst_stride + x * byte_per_pixel; for(j = y; j < y + h; j++) { memcpy(dst_bits, src_bits, w * byte_per_pixel); src_bits += src_stride; dst_bits += dst_stride; } } Bool glamor_upload_sub_pixmap_to_texture(PixmapPtr pixmap, int x, int y, int w, int h, int stride, void *bits, int pbo) { GLenum format, type; int no_alpha, revert, swap_rb; glamor_pixmap_private *pixmap_priv; Bool force_clip; if (glamor_get_tex_format_type_from_pixmap(pixmap, &format, &type, &no_alpha, &revert, &swap_rb, 1)) { glamor_fallback("Unknown pixmap depth %d.\n", pixmap->drawable.depth); return TRUE; } if (glamor_pixmap_upload_prepare(pixmap, format, no_alpha, revert, swap_rb)) return FALSE; pixmap_priv = glamor_get_pixmap_private(pixmap); force_clip = pixmap_priv->base.glamor_priv->gl_flavor != GLAMOR_GL_DESKTOP && !glamor_check_fbo_size(pixmap_priv->base.glamor_priv, w, h); if (pixmap_priv->type == GLAMOR_TEXTURE_LARGE || force_clip) { RegionRec region; BoxRec box; int n_region; glamor_pixmap_clipped_regions *clipped_regions; void *sub_bits; int i,j; sub_bits = malloc(h * stride); if (sub_bits == NULL) return FALSE; box.x1 = x; box.y1 = y; box.x2 = x + w; box.y2 = y + h; RegionInitBoxes(®ion, &box, 1); if (!force_clip) clipped_regions = glamor_compute_clipped_regions(pixmap_priv, ®ion, &n_region, 0, 0, 0); else clipped_regions = glamor_compute_clipped_regions_ext(pixmap_priv, ®ion, &n_region, pixmap_priv->base.glamor_priv->max_fbo_size, pixmap_priv->base.glamor_priv->max_fbo_size, 0, 0); DEBUGF("prepare upload %dx%d to a large pixmap %p\n", w, h, pixmap); for(i = 0; i < n_region; i++) { BoxPtr boxes; int nbox; int temp_stride; void *temp_bits; assert(pbo == 0); SET_PIXMAP_FBO_CURRENT(pixmap_priv, clipped_regions[i].block_idx); boxes = RegionRects(clipped_regions[i].region); nbox = RegionNumRects(clipped_regions[i].region); DEBUGF("split to %d boxes\n", nbox); for(j = 0; j < nbox; j++) { temp_stride = PixmapBytePad(boxes[j].x2 - boxes[j].x1, pixmap->drawable.depth); if (boxes[j].x1 == x && temp_stride == stride) { temp_bits = (char*)bits + (boxes[j].y1 - y) * stride; } else { temp_bits = sub_bits; glamor_put_bits(temp_bits, temp_stride, bits, stride, pixmap->drawable.bitsPerPixel, boxes[j].x1 - x, boxes[j].y1 - y, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1); } DEBUGF("upload x %d y %d w %d h %d temp stride %d \n", boxes[j].x1 - x, boxes[j].y1 - y, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1, temp_stride); if (_glamor_upload_bits_to_pixmap_texture(pixmap, format, type, no_alpha, revert, swap_rb, boxes[j].x1, boxes[j].y1, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1, temp_stride, temp_bits, pbo) == FALSE) { RegionUninit(®ion); free(sub_bits); assert(0); return FALSE; } } RegionDestroy(clipped_regions[i].region); } free(sub_bits); free(clipped_regions); RegionUninit(®ion); return TRUE; } else return _glamor_upload_bits_to_pixmap_texture(pixmap, format, type, no_alpha, revert, swap_rb, x, y, w, h, stride, bits, pbo); } enum glamor_pixmap_status glamor_upload_pixmap_to_texture(PixmapPtr pixmap) { glamor_pixmap_private *pixmap_priv; void *data; int pbo; int ret; pixmap_priv = glamor_get_pixmap_private(pixmap); if ((pixmap_priv->base.fbo) && (pixmap_priv->base.fbo->pbo_valid)) { data = NULL; pbo = pixmap_priv->base.fbo->pbo; } else { data = pixmap->devPrivate.ptr; pbo = 0; } if (glamor_upload_sub_pixmap_to_texture(pixmap, 0, 0, pixmap->drawable.width, pixmap->drawable.height, pixmap->devKind, data, pbo)) ret = GLAMOR_UPLOAD_DONE; else ret = GLAMOR_UPLOAD_FAILED; return ret; } void glamor_restore_pixmap_to_texture(PixmapPtr pixmap) { if (glamor_upload_pixmap_to_texture(pixmap) != GLAMOR_UPLOAD_DONE) LogMessage(X_WARNING, "Failed to restore pixmap to texture.\n"); } /* * as gles2 only support a very small set of color format and * type when do glReadPixel, * Before we use glReadPixels to get back a textured pixmap, * Use shader to convert it to a supported format and thus * get a new temporary pixmap returned. * */ glamor_pixmap_fbo * glamor_es2_pixmap_read_prepare(PixmapPtr source, int x, int y, int w, int h, GLenum format, GLenum type, int no_alpha, int revert, int swap_rb) { glamor_pixmap_private *source_priv; glamor_screen_private *glamor_priv; ScreenPtr screen; glamor_pixmap_fbo *temp_fbo; glamor_gl_dispatch *dispatch; float temp_xscale, temp_yscale, source_xscale, source_yscale; static float vertices[8]; static float texcoords[8]; screen = source->drawable.pScreen; glamor_priv = glamor_get_screen_private(screen); source_priv = glamor_get_pixmap_private(source); temp_fbo = glamor_create_fbo(glamor_priv, w, h, format, 0); if (temp_fbo == NULL) return NULL; dispatch = glamor_get_dispatch(glamor_priv); temp_xscale = 1.0 / w; temp_yscale = 1.0 / h; glamor_set_normalize_vcoords((struct glamor_pixmap_private*)NULL,temp_xscale, temp_yscale, 0, 0, w, h, glamor_priv->yInverted, vertices); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); pixmap_priv_get_scale(source_priv, &source_xscale, &source_yscale); glamor_set_normalize_tcoords(source_priv, source_xscale, source_yscale, x, y, x + w, y + h, glamor_priv->yInverted, texcoords); dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), texcoords); dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); dispatch->glActiveTexture(GL_TEXTURE0); dispatch->glBindTexture(GL_TEXTURE_2D, source_priv->base.fbo->tex); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glamor_set_destination_pixmap_fbo(temp_fbo, 0, 0, w, h); dispatch->glUseProgram(glamor_priv->finish_access_prog[no_alpha]); dispatch->glUniform1i(glamor_priv-> finish_access_revert[no_alpha], revert); dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[no_alpha], swap_rb); dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); dispatch->glUseProgram(0); glamor_put_dispatch(glamor_priv); return temp_fbo; } /* * Download a sub region of pixmap to a specified memory region. * The pixmap must have a valid FBO, otherwise return a NULL. * */ static void * _glamor_download_sub_pixmap_to_cpu(PixmapPtr pixmap, GLenum format, GLenum type, int no_alpha, int revert, int swap_rb, int x, int y, int w, int h, int stride, void *bits, int pbo, glamor_access_t access) { glamor_pixmap_private *pixmap_priv; GLenum gl_access = 0, gl_usage = 0; void *data, *read; glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); glamor_gl_dispatch *dispatch; glamor_pixmap_fbo *temp_fbo = NULL; int need_post_conversion = 0; int need_free_data = 0; int fbo_x_off, fbo_y_off; data = bits; pixmap_priv = glamor_get_pixmap_private(pixmap); if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) return NULL; switch (access) { case GLAMOR_ACCESS_RO: gl_access = GL_READ_ONLY; gl_usage = GL_STREAM_READ; break; case GLAMOR_ACCESS_WO: return bits; case GLAMOR_ACCESS_RW: gl_access = GL_READ_WRITE; gl_usage = GL_DYNAMIC_DRAW; break; default: ErrorF("Glamor: Invalid access code. %d\n", access); assert(0); } glamor_set_destination_pixmap_priv_nc(pixmap_priv); need_post_conversion = (revert > REVERT_NORMAL); if (need_post_conversion) { if (pixmap->drawable.depth == 1) { int temp_stride; temp_stride = (((w * 8 + 7) / 8) + 3) & ~3; data = malloc(temp_stride * h); if (data == NULL) return NULL; need_free_data = 1; } } pixmap_priv_get_fbo_off(pixmap_priv, &fbo_x_off, &fbo_y_off); if (glamor_priv->gl_flavor == GLAMOR_GL_ES2 && !need_post_conversion && (swap_rb != SWAP_NONE_DOWNLOADING || revert != REVERT_NONE)) { if (!(temp_fbo = glamor_es2_pixmap_read_prepare(pixmap, x, y, w, h, format, type, no_alpha, revert, swap_rb))) { free(data); return NULL; } x = 0; y = 0; fbo_x_off = 0; fbo_y_off = 0; } dispatch = glamor_get_dispatch(glamor_priv); dispatch->glPixelStorei(GL_PACK_ALIGNMENT, 4); if (glamor_priv->has_pack_invert || glamor_priv->yInverted) { if (!glamor_priv->yInverted) { assert(glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP); dispatch->glPixelStorei(GL_PACK_INVERT_MESA, 1); } if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP && data == NULL) { assert(pbo > 0); dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); dispatch->glBufferData(GL_PIXEL_PACK_BUFFER, stride * h, NULL, gl_usage); } dispatch->glReadPixels(x + fbo_x_off, y + fbo_y_off, w, h, format, type, data); if (!glamor_priv->yInverted) { assert(glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP); dispatch->glPixelStorei(GL_PACK_INVERT_MESA, 0); } if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP && bits == NULL) { bits = dispatch->glMapBuffer(GL_PIXEL_PACK_BUFFER, gl_access); dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } } else { unsigned int temp_pbo; int yy; dispatch = glamor_get_dispatch(glamor_priv); dispatch->glGenBuffers(1, &temp_pbo); dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, temp_pbo); dispatch->glBufferData(GL_PIXEL_PACK_BUFFER, stride * h, NULL, GL_STREAM_READ); dispatch->glReadPixels(x + fbo_x_off, y + fbo_y_off, w, h, format, type, 0); read = dispatch->glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); for (yy = 0; yy < pixmap->drawable.height; yy++) memcpy((char*)data + yy * stride, (char*)read + (h - yy - 1) * stride, stride); dispatch->glUnmapBuffer(GL_PIXEL_PACK_BUFFER); dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); dispatch->glDeleteBuffers(1, &temp_pbo); } dispatch->glBindFramebuffer(GL_FRAMEBUFFER, 0); glamor_put_dispatch(glamor_priv); if (need_post_conversion) { /* As OpenGL desktop version never enters here. * Don't need to consider if the pbo is valid.*/ bits = glamor_color_convert_to_bits(data, bits, w, h, stride, no_alpha, revert, swap_rb); } if (temp_fbo != NULL) glamor_destroy_fbo(temp_fbo); if (need_free_data) free(data); return bits; } void * glamor_download_sub_pixmap_to_cpu(PixmapPtr pixmap, int x, int y, int w, int h, int stride, void *bits, int pbo, glamor_access_t access) { GLenum format, type; int no_alpha, revert, swap_rb; glamor_pixmap_private *pixmap_priv; Bool force_clip; if (glamor_get_tex_format_type_from_pixmap(pixmap, &format, &type, &no_alpha, &revert, &swap_rb, 0)) { glamor_fallback("Unknown pixmap depth %d.\n", pixmap->drawable.depth); return NULL; } pixmap_priv = glamor_get_pixmap_private(pixmap); if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) return NULL; force_clip = pixmap_priv->base.glamor_priv->gl_flavor != GLAMOR_GL_DESKTOP && !glamor_check_fbo_size(pixmap_priv->base.glamor_priv, w, h); if (pixmap_priv->type == GLAMOR_TEXTURE_LARGE || force_clip) { RegionRec region; BoxRec box; int n_region; glamor_pixmap_clipped_regions *clipped_regions; void *sub_bits; int i,j; sub_bits = malloc(h * stride); if (sub_bits == NULL) return FALSE; box.x1 = x; box.y1 = y; box.x2 = x + w; box.y2 = y + h; RegionInitBoxes(®ion, &box, 1); if (!force_clip) clipped_regions = glamor_compute_clipped_regions(pixmap_priv, ®ion, &n_region, 0, 0, 0); else clipped_regions = glamor_compute_clipped_regions_ext(pixmap_priv, ®ion, &n_region, pixmap_priv->base.glamor_priv->max_fbo_size, pixmap_priv->base.glamor_priv->max_fbo_size, 0, 0); DEBUGF("start download large pixmap %p %dx%d \n", pixmap, w, h); for(i = 0; i < n_region; i++) { BoxPtr boxes; int nbox; int temp_stride; void *temp_bits; assert(pbo == 0); SET_PIXMAP_FBO_CURRENT(pixmap_priv, clipped_regions[i].block_idx); boxes = RegionRects(clipped_regions[i].region); nbox = RegionNumRects(clipped_regions[i].region); for(j = 0; j < nbox; j++) { temp_stride = PixmapBytePad(boxes[j].x2 - boxes[j].x1, pixmap->drawable.depth); if (boxes[j].x1 == x && temp_stride == stride) { temp_bits = (char*)bits + (boxes[j].y1 - y) * stride; } else { temp_bits = sub_bits; } DEBUGF("download x %d y %d w %d h %d temp stride %d \n", boxes[j].x1, boxes[j].y1, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1, temp_stride); /* For large pixmap, we don't support pbo currently.*/ assert(pbo == 0); if (_glamor_download_sub_pixmap_to_cpu(pixmap, format, type, no_alpha, revert, swap_rb, boxes[j].x1, boxes[j].y1, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1, temp_stride, temp_bits, pbo, access) == FALSE) { RegionUninit(®ion); free(sub_bits); assert(0); return NULL; } if (boxes[j].x1 != x || temp_stride != stride) glamor_get_bits(bits, stride, temp_bits, temp_stride, pixmap->drawable.bitsPerPixel, boxes[j].x1 - x , boxes[j].y1 - y, boxes[j].x2 - boxes[j].x1, boxes[j].y2 - boxes[j].y1); } RegionDestroy(clipped_regions[i].region); } free(sub_bits); free(clipped_regions); RegionUninit(®ion); return bits; } else return _glamor_download_sub_pixmap_to_cpu(pixmap, format, type, no_alpha, revert, swap_rb, x, y, w, h, stride, bits, pbo, access); } /** * Move a pixmap to CPU memory. * The input data is the pixmap's fbo. * The output data is at pixmap->devPrivate.ptr. We always use pbo * to read the fbo and then map it to va. If possible, we will use * it directly as devPrivate.ptr. * If successfully download a fbo to cpu then return TRUE. * Otherwise return FALSE. **/ Bool glamor_download_pixmap_to_cpu(PixmapPtr pixmap, glamor_access_t access) { glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); unsigned int stride; void *data = NULL, *dst; glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); glamor_gl_dispatch *dispatch; int pbo = 0; if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) return TRUE; glamor_debug_output(GLAMOR_DEBUG_TEXTURE_DOWNLOAD, "Downloading pixmap %p %dx%d depth%d\n", pixmap, pixmap->drawable.width, pixmap->drawable.height, pixmap->drawable.depth); stride = pixmap->devKind; if (access == GLAMOR_ACCESS_WO || glamor_priv->gl_flavor == GLAMOR_GL_ES2 || (!glamor_priv->has_pack_invert && !glamor_priv->yInverted) || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { data = malloc(stride * pixmap->drawable.height); } else { dispatch = glamor_get_dispatch(glamor_priv); if (pixmap_priv->base.fbo->pbo == 0) dispatch->glGenBuffers(1, &pixmap_priv->base.fbo->pbo); glamor_put_dispatch(glamor_priv); pbo = pixmap_priv->base.fbo->pbo; } if (pixmap_priv->type == GLAMOR_TEXTURE_DRM) { stride = PixmapBytePad(pixmap->drawable.width, pixmap->drawable.depth); pixmap_priv->base.drm_stride = pixmap->devKind; pixmap->devKind = stride; } dst = glamor_download_sub_pixmap_to_cpu(pixmap, 0, 0, pixmap->drawable.width, pixmap->drawable.height, pixmap->devKind, data, pbo, access); if (!dst) { if (data) free(data); return FALSE; } if (pbo != 0) pixmap_priv->base.fbo->pbo_valid = 1; pixmap_priv->base.gl_fbo = GLAMOR_FBO_DOWNLOADED; pixmap->devPrivate.ptr = dst; return TRUE; } /* fixup a fbo to the exact size as the pixmap. */ /* XXX LARGE pixmap? */ Bool glamor_fixup_pixmap_priv(ScreenPtr screen, glamor_pixmap_private *pixmap_priv) { glamor_pixmap_fbo *old_fbo; glamor_pixmap_fbo *new_fbo = NULL; PixmapPtr scratch = NULL; glamor_pixmap_private *scratch_priv; DrawablePtr drawable; GCPtr gc = NULL; int ret = FALSE; drawable = &pixmap_priv->base.pixmap->drawable; if (!GLAMOR_PIXMAP_FBO_NOT_EAXCT_SIZE(pixmap_priv)) return TRUE; old_fbo = pixmap_priv->base.fbo; if (!old_fbo) return FALSE; gc = GetScratchGC(drawable->depth, screen); if (!gc) goto fail; scratch = glamor_create_pixmap(screen, drawable->width, drawable->height, drawable->depth, GLAMOR_CREATE_PIXMAP_FIXUP); scratch_priv = glamor_get_pixmap_private(scratch); if (!scratch_priv->base.fbo) goto fail; ValidateGC(&scratch->drawable, gc); glamor_copy_area(drawable, &scratch->drawable, gc, 0, 0, drawable->width, drawable->height, 0, 0); old_fbo = glamor_pixmap_detach_fbo(pixmap_priv); new_fbo = glamor_pixmap_detach_fbo(scratch_priv); glamor_pixmap_attach_fbo(pixmap_priv->base.pixmap, new_fbo); glamor_pixmap_attach_fbo(scratch, old_fbo); DEBUGF("old %dx%d type %d\n", drawable->width, drawable->height, pixmap_priv->type); DEBUGF("copy tex %d %dx%d to tex %d %dx%d \n", old_fbo->tex, old_fbo->width, old_fbo->height, new_fbo->tex, new_fbo->width, new_fbo->height); ret = TRUE; fail: if (gc) FreeScratchGC(gc); if (scratch) glamor_destroy_pixmap(scratch); return ret; } /* * We may use this function to reduce a large pixmap to a small sub * pixmap. Two scenarios currently: * 1. When fallback a large textured pixmap to CPU but we do need to * do rendering within a small sub region, then we can just get a * sub region. * * 2. When uploading a large pixmap to texture but we only need to * use part of the source/mask picture. As glTexImage2D will be more * efficient to upload a contingent region rather than a sub block * in a large buffer. We use this function to gather the sub region * to a contingent sub pixmap. * * The sub-pixmap must have the same format as the source pixmap. * * */ PixmapPtr glamor_get_sub_pixmap(PixmapPtr pixmap, int x, int y, int w, int h, glamor_access_t access) { glamor_screen_private *glamor_priv; PixmapPtr sub_pixmap; glamor_pixmap_private *sub_pixmap_priv, *pixmap_priv; void *data; int pbo; int flag; if (x < 0 || y < 0) return NULL; w = (x + w) > pixmap->drawable.width ? (pixmap->drawable.width - x) : w; h = (y + h) > pixmap->drawable.height ? (pixmap->drawable.height - y) : h; if (access == GLAMOR_ACCESS_WO) { sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, pixmap->drawable.depth, GLAMOR_CREATE_PIXMAP_CPU); return sub_pixmap; } glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); pixmap_priv = glamor_get_pixmap_private(pixmap); if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) return NULL; if (glamor_priv->gl_flavor == GLAMOR_GL_ES2 || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) flag = GLAMOR_CREATE_PIXMAP_CPU; else flag = GLAMOR_CREATE_PIXMAP_MAP; sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, pixmap->drawable.depth, flag); if (sub_pixmap == NULL) return NULL; sub_pixmap_priv = glamor_get_pixmap_private(sub_pixmap); pbo = sub_pixmap_priv ? (sub_pixmap_priv->base.fbo ? sub_pixmap_priv->base.fbo->pbo : 0): 0; if (pixmap_priv->base.is_picture) { sub_pixmap_priv->base.picture = pixmap_priv->base.picture; sub_pixmap_priv->base.is_picture = pixmap_priv->base.is_picture; } if (pbo) data = NULL; else data = sub_pixmap->devPrivate.ptr; data = glamor_download_sub_pixmap_to_cpu(pixmap, x, y, w, h, sub_pixmap->devKind, data, pbo, access); if (pbo) { assert(sub_pixmap->devPrivate.ptr == NULL); sub_pixmap->devPrivate.ptr = data; sub_pixmap_priv->base.fbo->pbo_valid = 1; } #if 0 struct pixman_box16 box; PixmapPtr new_sub_pixmap; int dx, dy; box.x1 = 0; box.y1 = 0; box.x2 = w; box.y2 = h; dx = x; dy = y; new_sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, pixmap->drawable.depth, GLAMOR_CREATE_PIXMAP_CPU); glamor_copy_n_to_n(&pixmap->drawable, &new_sub_pixmap->drawable, NULL, &box, 1, dx, dy, 0, 0, 0, NULL); glamor_compare_pixmaps(new_sub_pixmap, sub_pixmap, 0, 0, w, h, 1, 1); #endif return sub_pixmap; } void glamor_put_sub_pixmap(PixmapPtr sub_pixmap, PixmapPtr pixmap, int x, int y, int w, int h, glamor_access_t access) { void *bits; int pbo; glamor_pixmap_private *sub_pixmap_priv; if (access != GLAMOR_ACCESS_RO) { sub_pixmap_priv = glamor_get_pixmap_private(sub_pixmap); if (sub_pixmap_priv->base.fbo && sub_pixmap_priv->base.fbo->pbo_valid) { bits = NULL; pbo = sub_pixmap_priv->base.fbo->pbo; } else { bits = sub_pixmap->devPrivate.ptr; pbo = 0; } assert(x >= 0 && y >= 0); w = (w > sub_pixmap->drawable.width) ? sub_pixmap->drawable.width : w; h = (h > sub_pixmap->drawable.height) ? sub_pixmap->drawable.height : h; glamor_upload_sub_pixmap_to_texture(pixmap, x, y, w, h, sub_pixmap->devKind, bits, pbo); } glamor_destroy_pixmap(sub_pixmap); }