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

Iago Toral itoral at igalia.com
Thu Jan 8 23:48:00 PST 2015


On Thu, 2015-01-08 at 10:38 -0800, Jason Ekstrand wrote:
> 
> 
> On Wed, Jan 7, 2015 at 11:21 PM, 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.
>         
>         Reviewed-by: Jason Ekstrand <jason.ekstrand at intel.com>
>         ---
>          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 a6b8883..6467307 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"
>         +
>         +const mesa_array_format RGBA8888_FLOAT =
>         +   MESA_ARRAY_FORMAT(4, 1, 1, 1, 4, 0, 1, 2, 3);
>         +
>         +const mesa_array_format RGBA8888_UBYTE =
>         +   MESA_ARRAY_FORMAT(1, 0, 0, 1, 4, 0, 1, 2, 3);
>         +
>         +const mesa_array_format RGBA8888_UINT =
>         +   MESA_ARRAY_FORMAT(4, 0, 0, 0, 4, 0, 1, 2, 3);
>         +
>         +const mesa_array_format RGBA8888_INT =
>         +   MESA_ARRAY_FORMAT(4, 1, 0, 0, 4, 0, 1, 2, 3);
> 
> 
> I'm sorry I didn't notice this before, but 8888_FLOAT doesn't make
> sense.  We need to either drop the 8888 from each of these or maybe
> call them RGBA32_FLOAT, RGBA32_UINT, RGBA8_UBYTE, etc.  That said,
> let's do that as a fixup patch later and not hold up the series for
> it.

Right, somehow I missed this too... I'll send a patch to fix it as soon
as this series gets pushed. I think I'll go with the RGBA32_FLOAT style
naming.

>  
>         +
>         +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 4e63200..f006e18 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 const mesa_array_format RGBA8888_FLOAT;
>         +extern const mesa_array_format RGBA8888_UBYTE;
>         +extern const mesa_array_format RGBA8888_UINT;
>         +extern const mesa_array_format RGBA8888_INT;
>         +
>          /* 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))
>         @@ -207,4 +212,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
> 
> 




More information about the mesa-dev mailing list