379 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2016 Broadcom
 | |
|  * Copyright © 2009 Intel Corporation
 | |
|  * Copyright © 1998 Keith Packard
 | |
|  *
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file glamor_picture.c
 | |
|  *
 | |
|  * Implements temporary uploads of GL_MEMORY Pixmaps to a texture that
 | |
|  * is swizzled appropriately for a given Render picture format.
 | |
|  * laid *
 | |
|  *
 | |
|  * This is important because GTK likes to use SHM Pixmaps for Render
 | |
|  * blending operations, and we don't want a blend operation to fall
 | |
|  * back to software (readback is more expensive than the upload we do
 | |
|  * here, and you'd have to re-upload the fallback output anyway).
 | |
|  */
 | |
| #include <dix-config.h>
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "glamor_priv.h"
 | |
| #include "mipict.h"
 | |
| 
 | |
| static void byte_swap_swizzle(GLenum *swizzle)
 | |
| {
 | |
|     GLenum temp;
 | |
| 
 | |
|     temp = swizzle[0];
 | |
|     swizzle[0] = swizzle[3];
 | |
|     swizzle[3] = temp;
 | |
| 
 | |
|     temp = swizzle[1];
 | |
|     swizzle[1] = swizzle[2];
 | |
|     swizzle[2] = temp;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the GL format and type for uploading our bits to a given PictFormat.
 | |
|  *
 | |
|  * We may need to tell the caller to translate the bits to another
 | |
|  * format, as in PICT_a1 (which GL doesn't support).  We may also need
 | |
|  * to tell the GL to swizzle the texture on sampling, because GLES3
 | |
|  * doesn't support the GL_UNSIGNED_INT_8_8_8_8{,_REV} types, so we
 | |
|  * don't have enough channel reordering options at upload time without
 | |
|  * it.
 | |
|  */
 | |
| static Bool
 | |
| glamor_get_tex_format_type_from_pictformat(ScreenPtr pScreen,
 | |
|                                            PictFormatShort format,
 | |
|                                            PictFormatShort *temp_format,
 | |
|                                            GLenum *tex_format,
 | |
|                                            GLenum *tex_type,
 | |
|                                            GLenum *swizzle)
 | |
| {
 | |
|     glamor_screen_private *glamor_priv = glamor_get_screen_private(pScreen);
 | |
|     Bool is_little_endian = IMAGE_BYTE_ORDER == LSBFirst;
 | |
| 
 | |
|     *temp_format = format;
 | |
|     swizzle[0] = GL_RED;
 | |
|     swizzle[1] = GL_GREEN;
 | |
|     swizzle[2] = GL_BLUE;
 | |
|     swizzle[3] = GL_ALPHA;
 | |
| 
 | |
|     switch (format) {
 | |
|     case PICT_a1:
 | |
|         *tex_format = glamor_priv->formats[1].format;
 | |
|         *tex_type = GL_UNSIGNED_BYTE;
 | |
|         *temp_format = PICT_a8;
 | |
|         break;
 | |
| 
 | |
|     case PICT_b8g8r8x8:
 | |
|     case PICT_b8g8r8a8:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_INT_8_8_8_8;
 | |
|         } else {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_BYTE;
 | |
| 
 | |
|             swizzle[0] = GL_GREEN;
 | |
|             swizzle[1] = GL_BLUE;
 | |
|             swizzle[2] = GL_ALPHA;
 | |
|             swizzle[3] = GL_RED;
 | |
| 
 | |
|             if (!is_little_endian)
 | |
|                 byte_swap_swizzle(swizzle);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x8r8g8b8:
 | |
|     case PICT_a8r8g8b8:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV;
 | |
|         } else {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_BYTE;
 | |
| 
 | |
|             if (!is_little_endian)
 | |
|                 byte_swap_swizzle(swizzle);
 | |
|             break;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x8b8g8r8:
 | |
|     case PICT_a8b8g8r8:
 | |
|         *tex_format = GL_RGBA;
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV;
 | |
|         } else {
 | |
|             *tex_format = GL_RGBA;
 | |
|             *tex_type = GL_UNSIGNED_BYTE;
 | |
| 
 | |
|             if (!is_little_endian)
 | |
|                 byte_swap_swizzle(swizzle);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x2r10g10b10:
 | |
|     case PICT_a2r10g10b10:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV;
 | |
|         } else {
 | |
|             return FALSE;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x2b10g10r10:
 | |
|     case PICT_a2b10g10r10:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_RGBA;
 | |
|             *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV;
 | |
|         } else {
 | |
|             return FALSE;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_r5g6b5:
 | |
|         *tex_format = GL_RGB;
 | |
|         *tex_type = GL_UNSIGNED_SHORT_5_6_5;
 | |
|         break;
 | |
|     case PICT_b5g6r5:
 | |
|         *tex_format = GL_RGB;
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_type = GL_UNSIGNED_SHORT_5_6_5_REV;
 | |
|         } else {
 | |
|             *tex_type = GL_UNSIGNED_SHORT_5_6_5;
 | |
|             swizzle[0] = GL_BLUE;
 | |
|             swizzle[2] = GL_RED;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x1b5g5r5:
 | |
|     case PICT_a1b5g5r5:
 | |
|         *tex_format = GL_RGBA;
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
 | |
|         } else {
 | |
|             return FALSE;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x1r5g5b5:
 | |
|     case PICT_a1r5g5b5:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
 | |
|         } else {
 | |
|             return FALSE;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_a8:
 | |
|         *tex_format = glamor_priv->formats[8].format;
 | |
|         *tex_type = GL_UNSIGNED_BYTE;
 | |
|         break;
 | |
| 
 | |
|     case PICT_x4r4g4b4:
 | |
|     case PICT_a4r4g4b4:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_BGRA;
 | |
|             *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
 | |
|         } else {
 | |
|             /* XXX */
 | |
|             *tex_format = GL_RGBA;
 | |
|             *tex_type = GL_UNSIGNED_SHORT_4_4_4_4;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PICT_x4b4g4r4:
 | |
|     case PICT_a4b4g4r4:
 | |
|         if (!glamor_priv->is_gles) {
 | |
|             *tex_format = GL_RGBA;
 | |
|             *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
 | |
|         } else {
 | |
|             /* XXX */
 | |
|             *tex_format = GL_RGBA;
 | |
|             *tex_type = GL_UNSIGNED_SHORT_4_4_4_4;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (!PICT_FORMAT_A(format))
 | |
|         swizzle[3] = GL_ONE;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Takes a set of source bits with a given format and returns an
 | |
|  * in-memory pixman image of those bits in a destination format.
 | |
|  */
 | |
| static pixman_image_t *
 | |
| glamor_get_converted_image(PictFormatShort dst_format,
 | |
|                            PictFormatShort src_format,
 | |
|                            void *src_bits,
 | |
|                            int src_stride,
 | |
|                            int w, int h)
 | |
| {
 | |
|     pixman_image_t *dst_image;
 | |
|     pixman_image_t *src_image;
 | |
| 
 | |
|     dst_image = pixman_image_create_bits(dst_format, w, h, NULL, 0);
 | |
|     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);
 | |
|     return dst_image;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Uploads a picture based on a GLAMOR_MEMORY pixmap to a texture in a
 | |
|  * temporary FBO.
 | |
|  */
 | |
| Bool
 | |
| glamor_upload_picture_to_texture(PicturePtr picture)
 | |
| {
 | |
|     PixmapPtr pixmap = glamor_get_drawable_pixmap(picture->pDrawable);
 | |
|     ScreenPtr screen = pixmap->drawable.pScreen;
 | |
|     glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
 | |
|     glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
 | |
|     PictFormatShort converted_format;
 | |
|     void *bits = pixmap->devPrivate.ptr;
 | |
|     int stride = pixmap->devKind;
 | |
|     GLenum format, type;
 | |
|     GLenum swizzle[4];
 | |
|     GLenum iformat;
 | |
|     Bool ret = TRUE;
 | |
|     Bool needs_swizzle;
 | |
|     pixman_image_t *converted_image = NULL;
 | |
|     const struct glamor_format *f = glamor_format_for_pixmap(pixmap);
 | |
| 
 | |
|     assert(glamor_pixmap_is_memory(pixmap));
 | |
|     assert(pixmap_priv);
 | |
|     assert(!pixmap_priv->fbo);
 | |
| 
 | |
|     glamor_make_current(glamor_priv);
 | |
| 
 | |
|     /* No handling of large pixmap pictures here (would need to make
 | |
|      * an FBO array and split the uploads across it).
 | |
|      */
 | |
|     if (!glamor_check_fbo_size(glamor_priv,
 | |
|                                pixmap->drawable.width,
 | |
|                                pixmap->drawable.height)) {
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (!glamor_get_tex_format_type_from_pictformat(screen,
 | |
|                                                     picture->format,
 | |
|                                                     &converted_format,
 | |
|                                                     &format,
 | |
|                                                     &type,
 | |
|                                                     swizzle)) {
 | |
|         glamor_fallback("Unknown pixmap depth %d.\n", pixmap->drawable.depth);
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     needs_swizzle = (swizzle[0] != GL_RED ||
 | |
|                      swizzle[1] != GL_GREEN ||
 | |
|                      swizzle[2] != GL_BLUE ||
 | |
|                      swizzle[3] != GL_ALPHA);
 | |
| 
 | |
|     if (!glamor_priv->has_texture_swizzle && needs_swizzle) {
 | |
|         glamor_fallback("Couldn't upload temporary picture due to missing "
 | |
|                         "GL_ARB_texture_swizzle.\n");
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (converted_format != picture->format) {
 | |
|         converted_image = glamor_get_converted_image(converted_format,
 | |
|                                                      picture->format,
 | |
|                                                      bits, stride,
 | |
|                                                      pixmap->drawable.width,
 | |
|                                                      pixmap->drawable.height);
 | |
|         if (!converted_image)
 | |
|             return FALSE;
 | |
| 
 | |
|         bits = pixman_image_get_data(converted_image);
 | |
|         stride = pixman_image_get_stride(converted_image);
 | |
|     }
 | |
| 
 | |
|     if (!glamor_priv->is_gles)
 | |
|         iformat = f->internalformat;
 | |
|     else
 | |
|         iformat = format;
 | |
| 
 | |
|     if (!glamor_pixmap_ensure_fbo(pixmap, GLAMOR_CREATE_FBO_NO_FBO)) {
 | |
|         ret = FALSE;
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 | |
| 
 | |
|     glamor_priv->suppress_gl_out_of_memory_logging = true;
 | |
| 
 | |
|     /* We can't use glamor_pixmap_loop() because GLAMOR_MEMORY pixmaps
 | |
|      * don't have initialized boxes.
 | |
|      */
 | |
|     glBindTexture(GL_TEXTURE_2D, pixmap_priv->fbo->tex);
 | |
|     glTexImage2D(GL_TEXTURE_2D, 0, iformat,
 | |
|                  pixmap->drawable.width, pixmap->drawable.height, 0,
 | |
|                  format, type, bits);
 | |
| 
 | |
|     if (needs_swizzle) {
 | |
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swizzle[0]);
 | |
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swizzle[1]);
 | |
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swizzle[2]);
 | |
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, swizzle[3]);
 | |
|     }
 | |
| 
 | |
|     glamor_priv->suppress_gl_out_of_memory_logging = false;
 | |
|     if (glGetError() == GL_OUT_OF_MEMORY) {
 | |
|         ret = FALSE;
 | |
|     }
 | |
| 
 | |
| fail:
 | |
|     if (converted_image)
 | |
|         pixman_image_unref(converted_image);
 | |
| 
 | |
|     return ret;
 | |
| }
 |