[Mesa-dev] [PATCH v3 01/28] mesa: Add an implementation of a master convert function.
Jason Ekstrand
jason at jlekstrand.net
Fri Dec 12 10:36:37 PST 2014
On Tue, Dec 9, 2014 at 4:07 AM, 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.
> ---
> 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 201231e..541d19a 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"
> +
> +mesa_array_format RGBA8888_FLOAT =
> + MESA_ARRAY_FORMAT(4, 1, 1, 1, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_UBYTE =
> + MESA_ARRAY_FORMAT(1, 0, 0, 1, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_UINT =
> + MESA_ARRAY_FORMAT(4, 0, 0, 0, 4, 0, 1, 2, 3);
> +
> +mesa_array_format RGBA8888_INT =
> + MESA_ARRAY_FORMAT(4, 1, 0, 0, 4, 0, 1, 2, 3);
> +
> +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 fb00f55..8d25e51 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 mesa_array_format RGBA8888_FLOAT;
> +extern mesa_array_format RGBA8888_UBYTE;
> +extern mesa_array_format RGBA8888_UINT;
> +extern mesa_array_format RGBA8888_INT;
>
Make these const. Otherwise, looks good.
Reviewed-by: Jason Ekstrand <jason.ekstrand at intel.com>
> +
> /* 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))
> @@ -173,4 +178,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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20141212/28c7c769/attachment-0001.html>
More information about the mesa-dev
mailing list