intel/isl: Add format conversion code
authorJason Ekstrand <jason.ekstrand@intel.com>
Fri, 23 Jun 2017 01:45:24 +0000 (18:45 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Wed, 9 May 2018 18:16:33 +0000 (11:16 -0700)
This adds helpers to ISL to convert an isl_color_value to and from
binary data encoded with a given isl_format.  The conversion is done
using ISL's built-in format introspection so it's fairly slow as format
conversions go but it should be fine for a single pixel value.  In
particular, we can use this to convert clear colors.

As a side-effect, we now rely on the sRGB helpers in libmesautil so we
need to tweak the build system a bit.  All prior uses of src/util in ISL
were header-only.

Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
src/intel/Makefile.compiler.am
src/intel/Makefile.isl.am
src/intel/isl/isl.h
src/intel/isl/isl_format.c
src/intel/isl/meson.build

index af30a58a1d6219df40b170f0d2a73bce977620fb..46711fe71b76c25d613234c965dff09f7d984a4d 100644 (file)
@@ -50,8 +50,8 @@ TEST_LIBS = \
        common/libintel_common.la \
        dev/libintel_dev.la \
        $(top_builddir)/src/compiler/nir/libnir.la \
-       $(top_builddir)/src/util/libmesautil.la \
        $(top_builddir)/src/intel/isl/libisl.la \
+       $(top_builddir)/src/util/libmesautil.la \
        $(PTHREAD_LIBS) \
        $(DLOPEN_LIBS)
 
index 52a71cffd4bfc0ef4da7e1c2102bc9723184b62e..f51294468cd3a112329cce2523c1c1aa806fc895 100644 (file)
@@ -82,6 +82,7 @@ TESTS += $(check_PROGRAMS)
 isl_tests_isl_surf_get_image_offset_test_LDADD = \
        dev/libintel_dev.la \
        isl/libisl.la \
+       $(top_builddir)/src/util/libmesautil.la \
        -lm
 
 # ----------------------------------------------------------------------------
index b6fd7edeb18eb7ef61dbc19d04346aae536a614c..00cfe31fc0491c4724fa79334772912a8924026b 100644 (file)
@@ -1576,6 +1576,13 @@ enum isl_format isl_format_rgb_to_rgba(enum isl_format rgb) ATTRIBUTE_CONST;
 enum isl_format isl_format_rgb_to_rgbx(enum isl_format rgb) ATTRIBUTE_CONST;
 enum isl_format isl_format_rgbx_to_rgba(enum isl_format rgb) ATTRIBUTE_CONST;
 
+void isl_color_value_pack(const union isl_color_value *value,
+                          enum isl_format format,
+                          uint32_t *data_out);
+void isl_color_value_unpack(union isl_color_value *value,
+                            enum isl_format format,
+                            const uint32_t *data_in);
+
 bool isl_is_storage_image_format(enum isl_format fmt);
 
 enum isl_format
index 6de681d5081d972f0725c60a1dfc04fc08c4c0ee..3cdf676c74b1bec97be760d46c2b394fc35af661 100644 (file)
 #include <assert.h>
 
 #include "isl.h"
+#include "isl_priv.h"
 #include "dev/gen_device_info.h"
 
+#include "main/macros.h" /* Needed for MAX3 and MAX2 for format_rgb9e5 */
+#include "util/format_srgb.h"
+#include "util/format_rgb9e5.h"
+#include "util/format_r11g11b10f.h"
+
+/* Header-only format conversion include */
+#include "main/format_utils.h"
+
 struct surface_format_info {
    bool exists;
    uint8_t sampling;
@@ -806,3 +815,208 @@ isl_format_rgbx_to_rgba(enum isl_format rgbx)
       return rgbx;
    }
 }
+
+static inline void
+pack_channel(const union isl_color_value *value, unsigned i,
+             const struct isl_channel_layout *layout,
+             enum isl_colorspace colorspace,
+             uint32_t data_out[4])
+{
+   if (layout->type == ISL_VOID)
+      return;
+
+   if (colorspace == ISL_COLORSPACE_SRGB)
+      assert(layout->type == ISL_UNORM);
+
+   uint32_t packed;
+   switch (layout->type) {
+   case ISL_UNORM:
+      if (colorspace == ISL_COLORSPACE_SRGB) {
+         if (layout->bits == 8) {
+            packed = util_format_linear_float_to_srgb_8unorm(value->f32[i]);
+         } else {
+            float srgb = util_format_linear_to_srgb_float(value->f32[i]);
+            packed = _mesa_float_to_unorm(srgb, layout->bits);
+         }
+      } else {
+         packed = _mesa_float_to_unorm(value->f32[i], layout->bits);
+      }
+      break;
+   case ISL_SNORM:
+      packed = _mesa_float_to_snorm(value->f32[i], layout->bits);
+      break;
+   case ISL_SFLOAT:
+      assert(layout->bits == 16 || layout->bits == 32);
+      if (layout->bits == 16) {
+         packed = _mesa_float_to_half(value->f32[i]);
+      } else {
+         packed = value->u32[i];
+      }
+      break;
+   case ISL_UINT:
+      packed = MIN(value->u32[i], MAX_UINT(layout->bits));
+      break;
+   case ISL_SINT:
+      packed = MIN(MAX(value->u32[i], MIN_INT(layout->bits)),
+                   MAX_INT(layout->bits));
+      break;
+
+   default:
+      unreachable("Invalid channel type");
+   }
+
+   unsigned dword = layout->start_bit / 32;
+   unsigned bit = layout->start_bit % 32;
+   assert(bit + layout->bits <= 32);
+   data_out[dword] |= (packed & MAX_UINT(layout->bits)) << bit;
+}
+
+/**
+ * Take an isl_color_value and pack it into the actual bits as specified by
+ * the isl_format.  This function is very slow for a format conversion
+ * function but should be fine for a single pixel worth of data.
+ */
+void
+isl_color_value_pack(const union isl_color_value *value,
+                     enum isl_format format,
+                     uint32_t *data_out)
+{
+   const struct isl_format_layout *fmtl = isl_format_get_layout(format);
+   assert(fmtl->colorspace == ISL_COLORSPACE_LINEAR ||
+          fmtl->colorspace == ISL_COLORSPACE_SRGB);
+   assert(!isl_format_is_compressed(format));
+
+   memset(data_out, 0, isl_align(fmtl->bpb, 32) / 8);
+
+   if (format == ISL_FORMAT_R9G9B9E5_SHAREDEXP) {
+      data_out[0] = float3_to_rgb9e5(value->f32);
+      return;
+   } else if (format == ISL_FORMAT_R11G11B10_FLOAT) {
+      data_out[0] = float3_to_r11g11b10f(value->f32);
+      return;
+   }
+
+   pack_channel(value, 0, &fmtl->channels.r, fmtl->colorspace, data_out);
+   pack_channel(value, 1, &fmtl->channels.g, fmtl->colorspace, data_out);
+   pack_channel(value, 2, &fmtl->channels.b, fmtl->colorspace, data_out);
+   pack_channel(value, 3, &fmtl->channels.a, ISL_COLORSPACE_LINEAR, data_out);
+   pack_channel(value, 0, &fmtl->channels.l, fmtl->colorspace, data_out);
+   pack_channel(value, 0, &fmtl->channels.i, ISL_COLORSPACE_LINEAR, data_out);
+   assert(fmtl->channels.p.bits == 0);
+}
+
+/** Extend an N-bit signed integer to 32 bits */
+static inline int32_t
+sign_extend(int32_t x, unsigned bits)
+{
+   if (bits < 32) {
+      unsigned shift = 32 - bits;
+      return (x << shift) >> shift;
+   } else {
+      return x;
+   }
+}
+
+static inline void
+unpack_channel(union isl_color_value *value,
+               unsigned start, unsigned count,
+               const struct isl_channel_layout *layout,
+               enum isl_colorspace colorspace,
+               const uint32_t *data_in)
+{
+   if (layout->type == ISL_VOID)
+      return;
+
+   unsigned dword = layout->start_bit / 32;
+   unsigned bit = layout->start_bit % 32;
+   assert(bit + layout->bits <= 32);
+   uint32_t packed = (data_in[dword] >> bit) & MAX_UINT(layout->bits);
+
+   union {
+      uint32_t u32;
+      float f32;
+   } unpacked;
+
+   if (colorspace == ISL_COLORSPACE_SRGB)
+      assert(layout->type == ISL_UNORM);
+
+   switch (layout->type) {
+   case ISL_UNORM:
+      unpacked.f32 = _mesa_unorm_to_float(packed, layout->bits);
+      if (colorspace == ISL_COLORSPACE_SRGB) {
+         if (layout->bits == 8) {
+            unpacked.f32 = util_format_srgb_8unorm_to_linear_float(packed);
+         } else {
+            float srgb = _mesa_unorm_to_float(packed, layout->bits);
+            unpacked.f32 = util_format_srgb_to_linear_float(srgb);
+         }
+      } else {
+         unpacked.f32 = _mesa_unorm_to_float(packed, layout->bits);
+      }
+      break;
+   case ISL_SNORM:
+      unpacked.f32 = _mesa_snorm_to_float(sign_extend(packed, layout->bits),
+                                          layout->bits);
+      break;
+   case ISL_SFLOAT:
+      assert(layout->bits == 16 || layout->bits == 32);
+      if (layout->bits == 16) {
+         unpacked.f32 = _mesa_half_to_float(packed);
+      } else {
+         unpacked.u32 = packed;
+      }
+      break;
+   case ISL_UINT:
+      unpacked.u32 = packed;
+      break;
+   case ISL_SINT:
+      unpacked.u32 = sign_extend(packed, layout->bits);
+      break;
+
+   default:
+      unreachable("Invalid channel type");
+   }
+
+   for (unsigned i = 0; i < count; i++)
+      value->u32[start + i] = unpacked.u32;
+}
+
+/**
+ * Take unpack an isl_color_value from the actual bits as specified by
+ * the isl_format.  This function is very slow for a format conversion
+ * function but should be fine for a single pixel worth of data.
+ */
+void
+isl_color_value_unpack(union isl_color_value *value,
+                       enum isl_format format,
+                       const uint32_t data_in[4])
+{
+   const struct isl_format_layout *fmtl = isl_format_get_layout(format);
+   assert(fmtl->colorspace == ISL_COLORSPACE_LINEAR ||
+          fmtl->colorspace == ISL_COLORSPACE_SRGB);
+   assert(!isl_format_is_compressed(format));
+
+   /* Default to opaque black. */
+   memset(value, 0, sizeof(*value));
+   if (isl_format_has_int_channel(format)) {
+      value->u32[3] = 1u;
+   } else {
+      value->f32[3] = 1.0f;
+   }
+
+   if (format == ISL_FORMAT_R9G9B9E5_SHAREDEXP) {
+      rgb9e5_to_float3(data_in[0], value->f32);
+      return;
+   } else if (format == ISL_FORMAT_R11G11B10_FLOAT) {
+      r11g11b10f_to_float3(data_in[0], value->f32);
+      return;
+   }
+
+   unpack_channel(value, 0, 1, &fmtl->channels.r, fmtl->colorspace, data_in);
+   unpack_channel(value, 1, 1, &fmtl->channels.g, fmtl->colorspace, data_in);
+   unpack_channel(value, 2, 1, &fmtl->channels.b, fmtl->colorspace, data_in);
+   unpack_channel(value, 3, 1, &fmtl->channels.a, ISL_COLORSPACE_LINEAR, data_in);
+   unpack_channel(value, 0, 3, &fmtl->channels.l, fmtl->colorspace, data_in);
+   unpack_channel(value, 0, 4, &fmtl->channels.i, ISL_COLORSPACE_LINEAR, data_in);
+   assert(fmtl->channels.p.bits == 0);
+}
index 73a8837f2ad6067dc09ee85ad9f2d187366d648c..c70c5c05fcd1adb4c63bd2b73e8894abddd037fc 100644 (file)
@@ -95,7 +95,7 @@ if with_tests
       'tests/isl_surf_get_image_offset_test.c',
       dependencies : dep_m,
       include_directories : [inc_common, inc_intel],
-      link_with : [libisl, libintel_dev],
+      link_with : [libisl, libintel_dev, libmesa_util],
     )
   )
 endif