intel/isl: Add format conversion code

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>
This commit is contained in:
Jason Ekstrand 2017-06-22 18:45:24 -07:00
parent 8152c60e01
commit 09ced65420
5 changed files with 224 additions and 2 deletions

View 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)

View 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
# ----------------------------------------------------------------------------

View 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

View File

@ -24,8 +24,17 @@
#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);
}

View 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