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:
parent
8152c60e01
commit
09ced65420
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue