Most format conversion operations required by GL can be performed by converting one channel at a time, shuffling the channels around, and optionally filling missing channels with zeros and ones. This adds a function to do just that in a general, yet efficient, way.
Signed-off-by: Jason Ekstrand <jason.ekstr...@intel.com> --- src/mesa/main/format_utils.c | 566 +++++++++++++++++++++++++++++++++++++++++++ src/mesa/main/format_utils.h | 18 ++ 2 files changed, 584 insertions(+) diff --git a/src/mesa/main/format_utils.c b/src/mesa/main/format_utils.c index 241c158..0cb3eae 100644 --- a/src/mesa/main/format_utils.c +++ b/src/mesa/main/format_utils.c @@ -54,3 +54,569 @@ _mesa_srgb_ubyte_to_linear_float(uint8_t cl) return lut[cl]; } + +static bool +swizzle_convert_try_memcpy(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) +{ + int i; + + if (src_type != dst_type) + return false; + if (num_src_channels != num_dst_channels) + return false; + + for (i = 0; i < num_dst_channels; ++i) + if (swizzle[i] != i && swizzle[i] != MESA_FORMAT_SWIZZLE_NONE) + return false; + + memcpy(dst, src, count * num_src_channels * _mesa_sizeof_type(src_type)); + + return true; +} + +/* Note: This loop is carefully crafted for performance. Be careful when + * changing it and run some benchmarks to ensure no performance regressions + * if you do. + */ +#define SWIZZLE_CONVERT_LOOP(DST_TYPE, SRC_TYPE, CONV) \ + do { \ + const SRC_TYPE *typed_src = void_src; \ + DST_TYPE *typed_dst = void_dst; \ + DST_TYPE tmp[7]; \ + tmp[4] = 0; \ + tmp[5] = one; \ + for (s = 0; s < count; ++s) { \ + for (j = 0; j < num_src_channels; ++j) { \ + SRC_TYPE src = typed_src[j]; \ + tmp[j] = CONV; \ + } \ + \ + typed_dst[0] = tmp[swizzle_x]; \ + if (num_dst_channels > 1) { \ + typed_dst[1] = tmp[swizzle_y]; \ + if (num_dst_channels > 2) { \ + typed_dst[2] = tmp[swizzle_z]; \ + if (num_dst_channels > 3) { \ + typed_dst[3] = tmp[swizzle_w]; \ + } \ + } \ + } \ + typed_src += num_src_channels; \ + typed_dst += num_dst_channels; \ + } \ + } while (0); + +/** + * Convert between array-based color formats. + * + * Most format conversion operations required by GL can be performed by + * converting one channel at a time, shuffling the channels around, and + * optionally filling missing channels with zeros and ones. This function + * does just that in a general, yet efficient, way. + * + * Most of the parameters are self-explanitory. The swizzle parameter is + * an array of 4 numbers (see _mesa_get_format_swizzle) that describes + * where each channel in the destination should come from in the source. + * + * Under most circumstances, the source and destination images must be + * different as no care is taken not to clobber one with the other. + * However, if they have the same number of bits per pixel, it is safe to + * do an in-place conversion. + */ +void +_mesa_swizzle_and_convert(void *void_dst, GLenum dst_type, int num_dst_channels, + const void *void_src, GLenum src_type, int num_src_channels, + const uint8_t swizzle[4], bool normalized, int count) +{ + int s, j; + register uint8_t swizzle_x, swizzle_y, swizzle_z, swizzle_w; + + if (swizzle_convert_try_memcpy(void_dst, dst_type, num_dst_channels, + void_src, src_type, num_src_channels, + swizzle, normalized, count)) + return; + + swizzle_x = swizzle[0]; + swizzle_y = swizzle[1]; + swizzle_z = swizzle[2]; + swizzle_w = swizzle[3]; + + switch (dst_type) { + case GL_FLOAT: + { + const float one = 1.0f; + switch (src_type) { + case GL_FLOAT: + SWIZZLE_CONVERT_LOOP(float, float, src) + break; + case GL_HALF_FLOAT: + SWIZZLE_CONVERT_LOOP(float, uint16_t, _mesa_half_to_float(src)) + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, uint8_t, UBYTE_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, uint8_t, src) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, int8_t, BYTE_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, int8_t, src) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, uint16_t, USHORT_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, uint16_t, src) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, int16_t, SHORT_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, int16_t, src) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, uint32_t, UINT_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, uint32_t, src) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(float, int32_t, INT_TO_FLOAT(src)) + } else { + SWIZZLE_CONVERT_LOOP(float, int32_t, src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_HALF_FLOAT: + { + const uint16_t one = _mesa_float_to_half(1.0f); + switch (src_type) { + case GL_FLOAT: + SWIZZLE_CONVERT_LOOP(uint16_t, float, _mesa_float_to_half(src)) + break; + case GL_HALF_FLOAT: + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, src) + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint8_t, _mesa_float_to_half(UBYTE_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint8_t, _mesa_float_to_half(src)) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int8_t, _mesa_float_to_half(BYTE_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int8_t, _mesa_float_to_half(src)) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, _mesa_float_to_half(USHORT_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, _mesa_float_to_half(src)) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int16_t, _mesa_float_to_half(SHORT_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int16_t, _mesa_float_to_half(src)) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint32_t, _mesa_float_to_half(UINT_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint32_t, _mesa_float_to_half(src)) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int32_t, _mesa_float_to_half(INT_TO_FLOAT(src))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int32_t, _mesa_float_to_half(src)) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_UNSIGNED_BYTE: + { + const uint8_t one = normalized ? UINT8_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, float, FLOAT_TO_UBYTE(CLAMP(src, 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, float, (src < 0) ? 0 : src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, FLOAT_TO_UBYTE(CLAMP(_mesa_half_to_float(src), 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, (src & 0x8000) ? 0 : _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + SWIZZLE_CONVERT_LOOP(uint8_t, uint8_t, src) + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, int8_t, (src < 0) ? 0 : ((uint8_t)src * 2) + ((uint8_t)src >> 6)) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, int8_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, src >> 8) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, src) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, int16_t, (src < 0) ? 0 : (uint16_t)src >> 7) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, int16_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, uint32_t, src >> 24) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, uint32_t, src) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, int32_t, (src < 0) ? 0 : (uint32_t)src >> 23) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, int32_t, (src < 0) ? 0 : src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_BYTE: + { + const int8_t one = normalized ? INT8_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, float, FLOAT_TO_BYTE(CLAMP(src, -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, float, src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, FLOAT_TO_BYTE(CLAMP(_mesa_half_to_float(src), -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint8_t, uint16_t, _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int8_t, uint8_t, src >> 1) + } else { + SWIZZLE_CONVERT_LOOP(int8_t, uint8_t, src) + } + break; + case GL_BYTE: + SWIZZLE_CONVERT_LOOP(int8_t, int8_t, src) + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int8_t, uint16_t, src >> 9) + } else { + SWIZZLE_CONVERT_LOOP(int8_t, uint16_t, src) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int8_t, int16_t, src >> 8) + } else { + SWIZZLE_CONVERT_LOOP(int8_t, int16_t, src) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int8_t, uint32_t, src >> 25) + } else { + SWIZZLE_CONVERT_LOOP(int8_t, uint32_t, src) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int8_t, int32_t, src >> 24) + } else { + SWIZZLE_CONVERT_LOOP(int8_t, int32_t, src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_UNSIGNED_SHORT: + { + const uint16_t one = normalized ? UINT16_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, float, FLOAT_TO_USHORT(CLAMP(src, 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, float, (src < 0) ? 0 : src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, FLOAT_TO_USHORT(CLAMP(_mesa_half_to_float(src), 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, (src & 0x8000) ? 0 : _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint8_t, EXTEND_NORMALIZED_UINT((uint16_t)src, 8, 16)) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint8_t, src) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int8_t, (src < 0) ? 0 : EXTEND_NORMALIZED_UINT((uint16_t)src, 7, 16)) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int8_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_SHORT: + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, src) + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int16_t, (src < 0) ? 0 : EXTEND_NORMALIZED_UINT((uint16_t)src, 15, 16)) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int16_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint32_t, src >> 16) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint32_t, src) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, int32_t, (src < 0) ? 0 : (uint32_t)src >> 15) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, int32_t, (src < 0) ? 0 : src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_SHORT: + { + const int16_t one = normalized ? INT16_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, float, FLOAT_TO_SHORT(CLAMP(src, -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, float, src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, FLOAT_TO_SHORT(CLAMP(_mesa_half_to_float(src), -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint16_t, uint16_t, _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int16_t, uint8_t, EXTEND_NORMALIZED_UINT((int16_t)src, 8, 15)) + } else { + SWIZZLE_CONVERT_LOOP(int16_t, uint8_t, src) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int16_t, int8_t, EXTEND_NORMALIZED_INT((int16_t)src, 7, 15)) + } else { + SWIZZLE_CONVERT_LOOP(int16_t, int8_t, src) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int16_t, uint16_t, src >> 1) + } else { + SWIZZLE_CONVERT_LOOP(int16_t, uint16_t, src) + } + break; + case GL_SHORT: + SWIZZLE_CONVERT_LOOP(int16_t, int16_t, src) + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int16_t, uint32_t, src >> 17) + } else { + SWIZZLE_CONVERT_LOOP(int16_t, uint32_t, src) + } + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int16_t, int32_t, src >> 16) + } else { + SWIZZLE_CONVERT_LOOP(int16_t, int32_t, src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_UNSIGNED_INT: + { + const uint32_t one = normalized ? UINT32_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, float, FLOAT_TO_UINT(CLAMP(src, 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, float, (src < 0) ? 0 : src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, FLOAT_TO_UINT(CLAMP(_mesa_half_to_float(src), 0.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, (src & 0x8000) ? 0 : _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, uint8_t, EXTEND_NORMALIZED_UINT((uint32_t)src, 8, 32)) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, uint8_t, src) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, int8_t, (src < 0) ? 0 : EXTEND_NORMALIZED_UINT((uint32_t)src, 7, 32)) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, int8_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, EXTEND_NORMALIZED_UINT((uint32_t)src, 16, 32)) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, src) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, int16_t, (src < 0) ? 0 : EXTEND_NORMALIZED_UINT((uint32_t)src, 15, 32)) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, int16_t, (src < 0) ? 0 : src) + } + break; + case GL_UNSIGNED_INT: + SWIZZLE_CONVERT_LOOP(uint32_t, uint32_t, src) + break; + case GL_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, int32_t, (src < 0) ? 0 : EXTEND_NORMALIZED_UINT((uint32_t)src, 31, 32)) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, int32_t, (src < 0) ? 0 : src) + } + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + case GL_INT: + { + const int32_t one = normalized ? INT32_MAX : 1; + switch (src_type) { + case GL_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, float, FLOAT_TO_INT(CLAMP(src, -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, float, src) + } + break; + case GL_HALF_FLOAT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, FLOAT_TO_INT(CLAMP(_mesa_half_to_float(src), -1.0f, 1.0f))) + } else { + SWIZZLE_CONVERT_LOOP(uint32_t, uint16_t, _mesa_half_to_float(src)) + } + break; + case GL_UNSIGNED_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int32_t, uint8_t, EXTEND_NORMALIZED_UINT((int32_t)src, 8, 31)) + } else { + SWIZZLE_CONVERT_LOOP(int32_t, uint8_t, src) + } + break; + case GL_BYTE: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int32_t, int8_t, EXTEND_NORMALIZED_INT((int32_t)src, 7, 31)) + } else { + SWIZZLE_CONVERT_LOOP(int32_t, int8_t, src) + } + break; + case GL_UNSIGNED_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int32_t, uint16_t, EXTEND_NORMALIZED_UINT((int32_t)src, 16, 31)) + } else { + SWIZZLE_CONVERT_LOOP(int32_t, uint16_t, src) + } + break; + case GL_SHORT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int32_t, int16_t, EXTEND_NORMALIZED_INT((int32_t)src, 15, 31)) + } else { + SWIZZLE_CONVERT_LOOP(int32_t, int16_t, src) + } + break; + case GL_UNSIGNED_INT: + if (normalized) { + SWIZZLE_CONVERT_LOOP(int32_t, uint32_t, src >> 1) + } else { + SWIZZLE_CONVERT_LOOP(int32_t, uint32_t, src) + } + break; + case GL_INT: + SWIZZLE_CONVERT_LOOP(int32_t, int32_t, src) + break; + default: + assert(!"Invalid channel type combination"); + } + } break; + default: + assert(!"Invalid channel type"); + } +} diff --git a/src/mesa/main/format_utils.h b/src/mesa/main/format_utils.h index 6af3aa5..c5dab7b 100644 --- a/src/mesa/main/format_utils.h +++ b/src/mesa/main/format_utils.h @@ -33,6 +33,19 @@ #include "macros.h" +/* Only guaranteed to work for BITS <= 32 */ +#define MAX_UINT(BITS) ((BITS) == 32 ? UINT32_MAX : ((1u << BITS) - 1)) + +/* Extends an integer of size Sb to one of size Db in a linear way */ +#define EXTEND_NORMALIZED_UINT(X, Sb, Db) \ + (((X) * (__typeof__(X))(MAX_UINT(Db) / MAX_UINT(Sb))) + \ + ((Db % Sb) ? ((X) >> (Sb - Db % Sb)) : (__typeof__(X))0)) + +/* This is almost the same as extending unsigned int except that we have to + * handle the case of -MAX(Sb) */ +#define EXTEND_NORMALIZED_INT(X, Sb, Db) (((X) < -(__typeof__(X))MAX_UINT(Sb)) \ + ? -(__typeof__(X))MAX_UINT(Db) : EXTEND_NORMALIZED_UINT(X, Sb, Db)) + /* RGB to sRGB conversion functions */ static inline float @@ -65,4 +78,9 @@ _mesa_srgb_to_linear(float cs) float _mesa_srgb_ubyte_to_linear_float(uint8_t cl); +void +_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); + #endif -- 2.0.1 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev