[Mesa-dev] [PATCH v3 01/28] mesa: Add an implementation of a master convert function.

Jason Ekstrand jason at jlekstrand.net
Fri Dec 12 10:36:37 PST 2014


On Tue, Dec 9, 2014 at 4:07 AM, Iago Toral Quiroga <itoral at igalia.com>
wrote:
>
> From: Jason Ekstrand <jason.ekstrand at intel.com>
>
> v2 by Iago Toral <itoral at igalia.com>:
>
> - When testing if we can directly pack we should use the src format to
> check
>   if we are packing from an RGBA format. The original code used the dst
> format
>   for the ubyte case by mistake.
> - Fixed incorrect number of bits for dst, it was computed using the src
> format
>   instead of the dst format.
> - If the dst format is an array format, check if it is signed. We were only
>   checking this for the case where it was not an array format, but we need
>   to know this in both scenarios.
> - Fixed incorrect swizzle transform for the cases where we convert between
>   array formats.
> - Compute is_signed and bits only once and for the dst format. We were
>   computing these for the src format too but they were overwritten by the
>   dst values immediately after.
> - Be more careful when selecting the integer path. Specifically, check that
>   both src and dst are integer types. Checking only one of them should
> suffice
>   since OpenGL does not allow conversions between normalized and integer
> types,
>   but putting extra care here makes sense and also makes the actual
> requirements
>   for this path more clear.
> - The format argument for pack functions is the destination format we are
>   packing to, not the source format (which has to be RGBA).
> - Expose RGBA8888_* to other files. These will come in handy when in need
> to
>   test if a given array format is RGBA or in need to pass RGBA formats to
>   mesa_format_convert.
>
> v3 by Samuel Iglesias <siglesias at igalia.com>:
>
> - Add an RGBA8888_INT definition.
>
> v4 by Iago Toral <itoral at igalia.com> after review by Jason Ekstrand:
>
> - Added documentation for _mesa_format_convert.
> - Added additional explanatory comments for integer conversions.
> - Ensure that we use _messa_swizzle_and_convert for all signed source
> formats.
> - Squashed: do not directly (un)pack to RGBA UINT if the source is not
> unsigned.
>
> v5 by Iago Toral <itoral at igalia.com>:
>
> - Adapted to the new implementation of mesa_array_format as a plain
> uint32_t
>   bitfield.
> ---
>  src/mesa/main/format_utils.c | 408
> +++++++++++++++++++++++++++++++++++++++++++
>  src/mesa/main/format_utils.h |  10 ++
>  2 files changed, 418 insertions(+)
>
> diff --git a/src/mesa/main/format_utils.c b/src/mesa/main/format_utils.c
> index 201231e..541d19a 100644
> --- a/src/mesa/main/format_utils.c
> +++ b/src/mesa/main/format_utils.c
> @@ -24,6 +24,414 @@
>
>  #include "format_utils.h"
>  #include "glformats.h"
> +#include "format_pack.h"
> +#include "format_unpack.h"
> +
> +mesa_array_format RGBA8888_FLOAT =
> +   MESA_ARRAY_FORMAT(4, 1, 1, 1, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_UBYTE =
> +   MESA_ARRAY_FORMAT(1, 0, 0, 1, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_UINT =
> +   MESA_ARRAY_FORMAT(4, 0, 0, 0, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_INT =
> +   MESA_ARRAY_FORMAT(4, 1, 0, 0, 4, 0, 1, 2, 3);
> +
> +static void
> +invert_swizzle(uint8_t dst[4], const uint8_t src[4])
> +{
> +   int i, j;
> +
> +   dst[0] = MESA_FORMAT_SWIZZLE_NONE;
> +   dst[1] = MESA_FORMAT_SWIZZLE_NONE;
> +   dst[2] = MESA_FORMAT_SWIZZLE_NONE;
> +   dst[3] = MESA_FORMAT_SWIZZLE_NONE;
> +
> +   for (i = 0; i < 4; ++i)
> +      for (j = 0; j < 4; ++j)
> +         if (src[j] == i && dst[i] == MESA_FORMAT_SWIZZLE_NONE)
> +            dst[i] = j;
> +}
> +
> +static GLenum
> +gl_type_for_array_format_datatype(enum mesa_array_format_datatype type)
> +{
> +   switch (type) {
> +   case MESA_ARRAY_FORMAT_TYPE_UBYTE:
> +      return GL_UNSIGNED_BYTE;
> +   case MESA_ARRAY_FORMAT_TYPE_USHORT:
> +      return GL_UNSIGNED_SHORT;
> +   case MESA_ARRAY_FORMAT_TYPE_UINT:
> +      return GL_UNSIGNED_INT;
> +   case MESA_ARRAY_FORMAT_TYPE_BYTE:
> +      return GL_BYTE;
> +   case MESA_ARRAY_FORMAT_TYPE_SHORT:
> +      return GL_SHORT;
> +   case MESA_ARRAY_FORMAT_TYPE_INT:
> +      return GL_INT;
> +   case MESA_ARRAY_FORMAT_TYPE_HALF:
> +      return GL_HALF_FLOAT;
> +   case MESA_ARRAY_FORMAT_TYPE_FLOAT:
> +      return GL_FLOAT;
> +   default:
> +      assert(!"Invalid datatype");
> +      return GL_NONE;
> +   }
> +}
> +
> +/**
> + * This can be used to convert between most color formats.
> + *
> + * Limitations:
> + * - This function doesn't handle GL_COLOR_INDEX or YCBCR formats.
> + * - This function doesn't handle byte-swapping or transferOps, these
> should
> + *   be handled by the caller.
> + *
> + * \param void_dst  The address where converted color data will be stored.
> + *                  The caller must ensure that the buffer is large enough
> + *                  to hold the converted pixel data.
> + * \param dst_format  The destination color format. It can be a
> mesa_format
> + *                    or a mesa_array_format represented as an uint32_t.
> + * \param dst_stride  The stride of the destination format in bytes.
> + * \param void_src  The address of the source color data to convert.
> + * \param src_format  The source color format. It can be a mesa_format
> + *                    or a mesa_array_format represented as an uint32_t.
> + * \param src_stride  The stride of the source format in bytes.
> + * \param width  The width, in pixels, of the source image to convert.
> + * \param height  The height, in pixels, of the source image to convert.
> + */
> +void
> +_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t
> dst_stride,
> +                     void *void_src, uint32_t src_format, size_t
> src_stride,
> +                     size_t width, size_t height)
> +{
> +   uint8_t *dst = (uint8_t *)void_dst;
> +   uint8_t *src = (uint8_t *)void_src;
> +   mesa_array_format src_array_format, dst_array_format;
> +   bool src_format_is_mesa_array_format, dst_format_is_mesa_array_format;
> +   uint8_t src2dst[4], src2rgba[4], rgba2dst[4], dst2rgba[4];
> +   GLenum src_gl_type, dst_gl_type, common_gl_type;
> +   bool normalized, dst_integer, src_integer, is_signed;
> +   int src_num_channels = 0, dst_num_channels = 0;
> +   uint8_t (*tmp_ubyte)[4];
> +   float (*tmp_float)[4];
> +   uint32_t (*tmp_uint)[4];
> +   int i, bits;
> +   size_t row;
> +
> +   if (_mesa_format_is_mesa_array_format(src_format)) {
> +      src_format_is_mesa_array_format = true;
> +      src_array_format = src_format;
> +   } else {
> +      assert(_mesa_is_format_color_format(src_format));
> +      src_format_is_mesa_array_format = false;
> +      src_array_format = _mesa_format_to_array_format(src_format);
> +   }
> +
> +   if (_mesa_format_is_mesa_array_format(dst_format)) {
> +      dst_format_is_mesa_array_format = true;
> +      dst_array_format = dst_format;
> +   } else {
> +      assert(_mesa_is_format_color_format(dst_format));
> +      dst_format_is_mesa_array_format = false;
> +      dst_array_format = _mesa_format_to_array_format(dst_format);
> +   }
> +
> +   /* Handle the cases where we can directly unpack */
> +   if (!src_format_is_mesa_array_format) {
> +      if (dst_array_format == RGBA8888_FLOAT) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_rgba_row(src_format, width,
> +                                  src, (float (*)[4])dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      } else if (dst_array_format == RGBA8888_UBYTE) {
> +         assert(!_mesa_is_format_integer_color(src_format));
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_ubyte_rgba_row(src_format, width,
> +                                        src, (uint8_t (*)[4])dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      } else if (dst_array_format == RGBA8888_UINT &&
> +                 _mesa_is_format_unsigned(src_format)) {
> +         assert(_mesa_is_format_integer_color(src_format));
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_uint_rgba_row(src_format, width,
> +                                       src, (uint32_t (*)[4])dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      }
> +   }
> +
> +   /* Handle the cases where we can directly pack */
> +   if (!dst_format_is_mesa_array_format) {
> +      if (src_array_format == RGBA8888_FLOAT) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_float_rgba_row(dst_format, width,
> +                                      (const float (*)[4])src, dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      } else if (src_array_format == RGBA8888_UBYTE) {
> +         assert(!_mesa_is_format_integer_color(dst_format));
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_ubyte_rgba_row(dst_format, width,
> +                                      (const uint8_t (*)[4])src, dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      } else if (src_array_format == RGBA8888_UINT &&
> +                 _mesa_is_format_unsigned(dst_format)) {
> +         assert(_mesa_is_format_integer_color(dst_format));
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_uint_rgba_row(dst_format, width,
> +                                     (const uint32_t (*)[4])src, dst);
> +            src += src_stride;
> +            dst += dst_stride;
> +         }
> +         return;
> +      }
> +   }
> +
> +   /* Handle conversions between array formats */
> +   normalized = false;
> +   if (src_array_format) {
> +      enum mesa_array_format_datatype datatype =
> +         _mesa_array_format_get_datatype(src_array_format);
> +      src_gl_type = gl_type_for_array_format_datatype(datatype);
> +
> +      src_num_channels =
> _mesa_array_format_get_num_channels(src_array_format);
> +
> +      _mesa_array_format_get_swizzle(src_array_format, src2rgba);
> +
> +      normalized = _mesa_array_format_is_normalized(src_array_format);
> +   }
> +
> +   if (dst_array_format) {
> +      enum mesa_array_format_datatype datatype =
> +         _mesa_array_format_get_datatype(dst_array_format);
> +      dst_gl_type = gl_type_for_array_format_datatype(datatype);
> +
> +      dst_num_channels =
> _mesa_array_format_get_num_channels(dst_array_format);
> +
> +      _mesa_array_format_get_swizzle(dst_array_format, dst2rgba);
> +      invert_swizzle(rgba2dst, dst2rgba);
> +
> +      normalized |= _mesa_array_format_is_normalized(dst_array_format);
> +   }
> +
> +   if (src_array_format && dst_array_format) {
> +      assert(_mesa_array_format_is_normalized(src_array_format) ==
> +             _mesa_array_format_is_normalized(dst_array_format));
> +
> +      for (i = 0; i < 4; i++) {
> +         if (rgba2dst[i] > MESA_FORMAT_SWIZZLE_W) {
> +            src2dst[i] = rgba2dst[i];
> +         } else {
> +            src2dst[i] = src2rgba[rgba2dst[i]];
> +         }
> +      }
> +
> +      for (row = 0; row < height; ++row) {
> +         _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,
> +                                   src, src_gl_type, src_num_channels,
> +                                   src2dst, normalized, width);
> +         src += src_stride;
> +         dst += dst_stride;
> +      }
> +      return;
> +   }
> +
> +   /* At this point, we're fresh out of fast-paths and we need to convert
> +    * to float, uint32, or, if we're lucky, uint8.
> +    */
> +   dst_integer = false;
> +   src_integer = false;
> +
> +   if (src_array_format) {
> +      if (!_mesa_array_format_is_float(src_array_format) &&
> +          !_mesa_array_format_is_normalized(src_array_format))
> +         src_integer = true;
> +   } else {
> +      switch (_mesa_get_format_datatype(src_format)) {
> +      case GL_UNSIGNED_INT:
> +      case GL_INT:
> +         src_integer = true;
> +         break;
> +      }
> +   }
> +
> +   /* If the destination format is signed but the source is unsigned,
> then we
> +    * don't loose any data by converting to a signed intermediate format
> above
> +    * and beyond the precision that we loose in the conversion itself. If
> the
> +    * destination is unsigned then, by using an unsigned intermediate
> format,
> +    * we make the conversion function that converts from the source to the
> +    * intermediate format take care of truncating at zero. The exception
> here
> +    * is if the intermediate format is float, in which case the first
> +    * conversion will leave it signed and the second conversion will
> truncate
> +    * at zero.
> +    */
> +   is_signed = false;
> +   if (dst_array_format) {
> +      if (!_mesa_array_format_is_float(dst_array_format) &&
> +          !_mesa_array_format_is_normalized(dst_array_format))
> +         dst_integer = true;
> +      is_signed = _mesa_array_format_is_signed(dst_array_format);
> +      bits = 8 * _mesa_array_format_get_type_size(dst_array_format);
> +   } else {
> +      switch (_mesa_get_format_datatype(dst_format)) {
> +      case GL_UNSIGNED_NORMALIZED:
> +         is_signed = false;
> +         break;
> +      case GL_SIGNED_NORMALIZED:
> +         is_signed = true;
> +         break;
> +      case GL_FLOAT:
> +         is_signed = true;
> +         break;
> +      case GL_UNSIGNED_INT:
> +         is_signed = false;
> +         dst_integer = true;
> +         break;
> +      case GL_INT:
> +         is_signed = true;
> +         dst_integer = true;
> +         break;
> +      }
> +      bits = _mesa_get_format_max_bits(dst_format);
> +   }
> +
> +   assert(src_integer == dst_integer);
> +
> +   if (src_integer && dst_integer) {
> +      tmp_uint = malloc(width * height * sizeof(*tmp_uint));
> +
> +      /* The [un]packing functions for unsigned datatypes treat the 32-bit
> +       * integer array as signed for signed formats and as unsigned for
> +       * unsigned formats. This is a bit of a problem if we ever convert
> from
> +       * a signed to an unsigned format because the unsigned packing
> function
> +       * doesn't know that the input is signed and will treat it as
> unsigned
> +       * and not do the trunctation. The thing that saves us here is that
> all
> +       * of the packed formats are unsigned, so we can just always use
> +       * _mesa_swizzle_and_convert for signed formats, which is aware of
> the
> +       * truncation problem.
> +       */
> +      common_gl_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
> +      if (src_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(tmp_uint + row * width,
> common_gl_type, 4,
> +                                      src, src_gl_type, src_num_channels,
> +                                      src2rgba, normalized, width);
> +            src += src_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_uint_rgba_row(src_format, width,
> +                                       src, tmp_uint + row * width);
> +            src += src_stride;
> +         }
> +      }
> +
> +      /* At this point, we have already done the truncation if the source
> is
> +       * signed but the destination is unsigned, so no need to force the
> +       * _mesa_swizzle_and_convert path.
> +       */
> +      if (dst_format_is_mesa_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,
> +                                      tmp_uint + row * width,
> common_gl_type, 4,
> +                                      rgba2dst, normalized, width);
> +            dst += dst_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_uint_rgba_row(dst_format, width,
> +                                     (const uint32_t (*)[4])tmp_uint +
> row * width, dst);
> +            dst += dst_stride;
> +         }
> +      }
> +
> +      free(tmp_uint);
> +   } else if (is_signed || bits > 8) {
> +      tmp_float = malloc(width * height * sizeof(*tmp_float));
> +
> +      if (src_format_is_mesa_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(tmp_float + row * width, GL_FLOAT,
> 4,
> +                                      src, src_gl_type, src_num_channels,
> +                                      src2rgba, normalized, width);
> +            src += src_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_rgba_row(src_format, width,
> +                                  src, tmp_float + row * width);
> +            src += src_stride;
> +         }
> +      }
> +
> +      if (dst_format_is_mesa_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,
> +                                      tmp_float + row * width, GL_FLOAT,
> 4,
> +                                      rgba2dst, normalized, width);
> +            dst += dst_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_float_rgba_row(dst_format, width,
> +                                      (const float (*)[4])tmp_float + row
> * width, dst);
> +            dst += dst_stride;
> +         }
> +      }
> +
> +      free(tmp_float);
> +   } else {
> +      tmp_ubyte = malloc(width * height * sizeof(*tmp_ubyte));
> +
> +      if (src_format_is_mesa_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(tmp_ubyte + row * width,
> GL_UNSIGNED_BYTE, 4,
> +                                      src, src_gl_type, src_num_channels,
> +                                      src2rgba, normalized, width);
> +            src += src_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_unpack_ubyte_rgba_row(src_format, width,
> +                                        src, tmp_ubyte + row * width);
> +            src += src_stride;
> +         }
> +      }
> +
> +      if (dst_format_is_mesa_array_format) {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_swizzle_and_convert(dst, dst_gl_type, dst_num_channels,
> +                                      tmp_ubyte + row * width,
> GL_UNSIGNED_BYTE, 4,
> +                                      rgba2dst, normalized, width);
> +            dst += dst_stride;
> +         }
> +      } else {
> +         for (row = 0; row < height; ++row) {
> +            _mesa_pack_ubyte_rgba_row(dst_format, width,
> +                                      (const uint8_t (*)[4])tmp_ubyte +
> row * width, dst);
> +            dst += dst_stride;
> +         }
> +      }
> +
> +      free(tmp_ubyte);
> +   }
> +}
>
>  static const uint8_t map_identity[7] = { 0, 1, 2, 3, 4, 5, 6 };
>  static const uint8_t map_3210[7] = { 3, 2, 1, 0, 4, 5, 6 };
> diff --git a/src/mesa/main/format_utils.h b/src/mesa/main/format_utils.h
> index fb00f55..8d25e51 100644
> --- a/src/mesa/main/format_utils.h
> +++ b/src/mesa/main/format_utils.h
> @@ -34,6 +34,11 @@
>  #include "imports.h"
>  #include "macros.h"
>
> +extern mesa_array_format RGBA8888_FLOAT;
> +extern mesa_array_format RGBA8888_UBYTE;
> +extern mesa_array_format RGBA8888_UINT;
> +extern mesa_array_format RGBA8888_INT;
>

Make these const.  Otherwise, looks good.

Reviewed-by: Jason Ekstrand <jason.ekstrand at intel.com>


> +
>  /* Only guaranteed to work for BITS <= 32 */
>  #define MAX_UINT(BITS) ((BITS) == 32 ? UINT32_MAX : ((1u << (BITS)) - 1))
>  #define MAX_INT(BITS) ((int)MAX_UINT((BITS) - 1))
> @@ -173,4 +178,9 @@ _mesa_swizzle_and_convert(void *dst, GLenum dst_type,
> int num_dst_channels,
>                            const void *src, GLenum src_type, int
> num_src_channels,
>                            const uint8_t swizzle[4], bool normalized, int
> count);
>
> +void
> +_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t
> dst_stride,
> +                     void *void_src, uint32_t src_format, size_t
> src_stride,
> +                     size_t width, size_t height);
> +
>  #endif
> --
> 1.9.1
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20141212/28c7c769/attachment-0001.html>


More information about the mesa-dev mailing list