gallivm: consolidate some half-to-float and r11g11b10-to-float code
Similar enough that we can try to use shared code. v2: fix a stupid bug using wrong variable causing mayhem with Inf and NaNs. Reviewed-by: Jose Fonseca <jfonseca@vmware.com
This commit is contained in:
@@ -75,6 +75,7 @@
|
|||||||
#include "lp_bld_logic.h"
|
#include "lp_bld_logic.h"
|
||||||
#include "lp_bld_intr.h"
|
#include "lp_bld_intr.h"
|
||||||
#include "lp_bld_printf.h"
|
#include "lp_bld_printf.h"
|
||||||
|
#include "lp_bld_format.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -156,61 +157,28 @@ lp_build_bswap_vec(struct gallivm_state *gallivm,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts int16 half-float to float32
|
* Converts int16 half-float to float32
|
||||||
* Note this can be performed in 1 instruction if vcvtph2ps exists (sse5 i think?)
|
* Note this can be performed in 1 instruction if vcvtph2ps exists (f16c/cvt16)
|
||||||
* [llvm.x86.vcvtph2ps / _mm_cvtph_ps]
|
* [llvm.x86.vcvtph2ps / _mm_cvtph_ps]
|
||||||
*
|
*
|
||||||
* @param src value to convert
|
* @param src value to convert
|
||||||
*
|
*
|
||||||
* ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
|
||||||
* ref https://gist.github.com/2144712
|
|
||||||
*/
|
*/
|
||||||
LLVMValueRef
|
LLVMValueRef
|
||||||
lp_build_half_to_float(struct gallivm_state *gallivm,
|
lp_build_half_to_float(struct gallivm_state *gallivm,
|
||||||
LLVMValueRef src)
|
LLVMValueRef src)
|
||||||
{
|
{
|
||||||
int src_length = LLVMGetVectorSize(LLVMTypeOf(src));
|
LLVMBuilderRef builder = gallivm->builder;
|
||||||
|
LLVMTypeRef src_type = LLVMTypeOf(src);
|
||||||
|
unsigned src_length = LLVMGetTypeKind(src_type) == LLVMVectorTypeKind ?
|
||||||
|
LLVMGetVectorSize(src_type) : 1;
|
||||||
|
|
||||||
struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
|
struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
|
||||||
struct lp_type i32_type = lp_type_int_vec(32, 32 * src_length);
|
struct lp_type i32_type = lp_type_int_vec(32, 32 * src_length);
|
||||||
|
|
||||||
LLVMBuilderRef builder = gallivm->builder;
|
|
||||||
LLVMTypeRef int_vec_type = lp_build_vec_type(gallivm, i32_type);
|
LLVMTypeRef int_vec_type = lp_build_vec_type(gallivm, i32_type);
|
||||||
LLVMTypeRef float_vec_type = lp_build_vec_type(gallivm, f32_type);
|
|
||||||
|
|
||||||
/* Constants */
|
|
||||||
LLVMValueRef i32_13 = lp_build_const_int_vec(gallivm, i32_type, 13);
|
|
||||||
LLVMValueRef i32_16 = lp_build_const_int_vec(gallivm, i32_type, 16);
|
|
||||||
LLVMValueRef i32_mask_nosign = lp_build_const_int_vec(gallivm, i32_type, 0x7fff);
|
|
||||||
LLVMValueRef i32_was_infnan = lp_build_const_int_vec(gallivm, i32_type, 0x7bff);
|
|
||||||
LLVMValueRef i32_exp_infnan = lp_build_const_int_vec(gallivm, i32_type, 0xff << 23);
|
|
||||||
LLVMValueRef f32_magic = LLVMBuildBitCast(builder,
|
|
||||||
lp_build_const_int_vec(gallivm, i32_type, (254 - 15) << 23),
|
|
||||||
float_vec_type, "");
|
|
||||||
|
|
||||||
/* Convert int16 vector to int32 vector by zero ext */
|
/* Convert int16 vector to int32 vector by zero ext */
|
||||||
LLVMValueRef h = LLVMBuildZExt(builder, src, int_vec_type, "");
|
LLVMValueRef h = LLVMBuildZExt(builder, src, int_vec_type, "");
|
||||||
|
return lp_build_smallfloat_to_float(gallivm, f32_type, h, 10, 5, 0, true);
|
||||||
/* Exponent / mantissa bits */
|
|
||||||
LLVMValueRef expmant = LLVMBuildAnd(builder, i32_mask_nosign, h, "");
|
|
||||||
LLVMValueRef shifted = LLVMBuildBitCast(builder, LLVMBuildShl(builder, expmant, i32_13, ""), float_vec_type, "");
|
|
||||||
|
|
||||||
/* Exponent adjust */
|
|
||||||
LLVMValueRef scaled = LLVMBuildBitCast(builder, LLVMBuildFMul(builder, shifted, f32_magic, ""), int_vec_type, "");
|
|
||||||
|
|
||||||
/* Make sure Inf/NaN survive */
|
|
||||||
LLVMValueRef b_wasinfnan = lp_build_compare(gallivm, i32_type, PIPE_FUNC_GREATER, expmant, i32_was_infnan);
|
|
||||||
LLVMValueRef infnanexp = LLVMBuildAnd(builder, b_wasinfnan, i32_exp_infnan, "");
|
|
||||||
|
|
||||||
/* Sign bit */
|
|
||||||
LLVMValueRef justsign = LLVMBuildXor(builder, h, expmant, "");
|
|
||||||
LLVMValueRef sign = LLVMBuildShl(builder, justsign, i32_16, "");
|
|
||||||
|
|
||||||
/* Combine result */
|
|
||||||
LLVMValueRef sign_inf = LLVMBuildOr(builder, sign, infnanexp, "");
|
|
||||||
LLVMValueRef final = LLVMBuildOr(builder, scaled, sign_inf, "");
|
|
||||||
|
|
||||||
/* Cast from int32 vector to float32 vector */
|
|
||||||
return LLVMBuildBitCast(builder, final, float_vec_type, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -126,6 +126,15 @@ lp_build_fetch_subsampled_rgba_aos(struct gallivm_state *gallivm,
|
|||||||
* special float formats
|
* special float formats
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
LLVMValueRef
|
||||||
|
lp_build_smallfloat_to_float(struct gallivm_state *gallivm,
|
||||||
|
struct lp_type f32_type,
|
||||||
|
LLVMValueRef src,
|
||||||
|
unsigned mantissa_bits,
|
||||||
|
unsigned exponent_bits,
|
||||||
|
unsigned mantissa_start,
|
||||||
|
boolean has_sign);
|
||||||
|
|
||||||
LLVMValueRef
|
LLVMValueRef
|
||||||
lp_build_float_to_r11g11b10(struct gallivm_state *gallivm,
|
lp_build_float_to_r11g11b10(struct gallivm_state *gallivm,
|
||||||
LLVMValueRef *src);
|
LLVMValueRef *src);
|
||||||
|
@@ -186,30 +186,34 @@ lp_build_float_to_r11g11b10(struct gallivm_state *gallivm,
|
|||||||
* Convert a float-like value with less exponent and mantissa
|
* Convert a float-like value with less exponent and mantissa
|
||||||
* bits than a normal float32 to a float32. The mantissa of
|
* bits than a normal float32 to a float32. The mantissa of
|
||||||
* the source value is assumed to have an implied 1, and the exponent
|
* the source value is assumed to have an implied 1, and the exponent
|
||||||
* is biased. There are no negative values.
|
* is biased. There may be a sign bit.
|
||||||
* The source value to extract must be in a 32bit int.
|
* The source value to extract must be in a 32bit int (bits not part of
|
||||||
* While this helper is generic, it is only ever going to be useful for
|
* the value to convert will be masked off).
|
||||||
* r11g11b10 (no other common format exists with the same properties).
|
* This works for things like 11-bit floats or half-floats,
|
||||||
|
* mantissa, exponent (and sign if present) must be packed
|
||||||
|
* the same as they are in a ordinary float.
|
||||||
*
|
*
|
||||||
* @param src (vector) value to convert
|
* @param src (vector) value to convert
|
||||||
* @param mantissa_bits the number of mantissa bits
|
* @param mantissa_bits the number of mantissa bits
|
||||||
* @param exponent_bits the number of exponent bits
|
* @param exponent_bits the number of exponent bits
|
||||||
* @param mantissa_start the bit start position of the packed component
|
* @param mantissa_start the bit start position of the packed component
|
||||||
|
* @param has_sign if the small float has a sign bit
|
||||||
*
|
*
|
||||||
* ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
* ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
||||||
* ref https://gist.github.com/rygorous/2156668
|
* ref https://gist.github.com/rygorous/2156668
|
||||||
*/
|
*/
|
||||||
static LLVMValueRef
|
LLVMValueRef
|
||||||
lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
|
lp_build_smallfloat_to_float(struct gallivm_state *gallivm,
|
||||||
struct lp_type f32_type,
|
struct lp_type f32_type,
|
||||||
LLVMValueRef src,
|
LLVMValueRef src,
|
||||||
unsigned mantissa_bits,
|
unsigned mantissa_bits,
|
||||||
unsigned exponent_bits,
|
unsigned exponent_bits,
|
||||||
unsigned mantissa_start)
|
unsigned mantissa_start,
|
||||||
|
boolean has_sign)
|
||||||
{
|
{
|
||||||
LLVMBuilderRef builder = gallivm->builder;
|
LLVMBuilderRef builder = gallivm->builder;
|
||||||
LLVMValueRef smallexpmask, i32_floatexpmask, magic;
|
LLVMValueRef smallexpmask, i32_floatexpmask, magic;
|
||||||
LLVMValueRef wasinfnan, tmp, res, shift, mask;
|
LLVMValueRef wasinfnan, tmp, res, shift, maskabs, srcabs, sign;
|
||||||
unsigned exponent_start = mantissa_start + mantissa_bits;
|
unsigned exponent_start = mantissa_start + mantissa_bits;
|
||||||
struct lp_type i32_type = lp_type_int_vec(32, 32 * f32_type.length);
|
struct lp_type i32_type = lp_type_int_vec(32, 32 * f32_type.length);
|
||||||
struct lp_build_context f32_bld, i32_bld;
|
struct lp_build_context f32_bld, i32_bld;
|
||||||
@@ -226,11 +230,11 @@ lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
|
|||||||
shift = lp_build_const_int_vec(gallivm, i32_type, exponent_start - 23);
|
shift = lp_build_const_int_vec(gallivm, i32_type, exponent_start - 23);
|
||||||
src = lp_build_shr(&i32_bld, src, shift);
|
src = lp_build_shr(&i32_bld, src, shift);
|
||||||
}
|
}
|
||||||
mask = lp_build_const_int_vec(gallivm, i32_type,
|
maskabs = lp_build_const_int_vec(gallivm, i32_type,
|
||||||
((1 << (mantissa_bits + exponent_bits)) - 1) <<
|
((1 << (mantissa_bits + exponent_bits)) - 1)
|
||||||
(23 - mantissa_bits));
|
<< (23 - mantissa_bits));
|
||||||
src = lp_build_and(&i32_bld, src, mask);
|
srcabs = lp_build_and(&i32_bld, src, maskabs);
|
||||||
src = LLVMBuildBitCast(builder, src, f32_bld.vec_type, "");
|
srcabs = LLVMBuildBitCast(builder, srcabs, f32_bld.vec_type, "");
|
||||||
|
|
||||||
/* now do the actual scaling */
|
/* now do the actual scaling */
|
||||||
smallexpmask = lp_build_const_int_vec(gallivm, i32_type,
|
smallexpmask = lp_build_const_int_vec(gallivm, i32_type,
|
||||||
@@ -245,19 +249,27 @@ lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
|
|||||||
magic = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, "");
|
magic = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, "");
|
||||||
|
|
||||||
/* adjust exponent and fix denorms */
|
/* adjust exponent and fix denorms */
|
||||||
res = lp_build_mul(&f32_bld, src, magic);
|
res = lp_build_mul(&f32_bld, srcabs, magic);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if exp was max (== NaN or Inf) set new exp to max (keep mantissa),
|
* if exp was max (== NaN or Inf) set new exp to max (keep mantissa),
|
||||||
* so a simple "or" will do (because exp adjust will leave mantissa intact)
|
* so a simple "or" will do (because exp adjust will leave mantissa intact)
|
||||||
*/
|
*/
|
||||||
/* use float compare (better for AVX 8-wide / no AVX2 though otherwise should use int) */
|
/* use float compare (better for AVX 8-wide / no AVX2 but else should use int) */
|
||||||
smallexpmask = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, "");
|
smallexpmask = LLVMBuildBitCast(builder, smallexpmask, f32_bld.vec_type, "");
|
||||||
wasinfnan = lp_build_compare(gallivm, f32_type, PIPE_FUNC_GEQUAL, src, smallexpmask);
|
wasinfnan = lp_build_compare(gallivm, f32_type, PIPE_FUNC_GEQUAL, srcabs, smallexpmask);
|
||||||
res = LLVMBuildBitCast(builder, res, i32_bld.vec_type, "");
|
res = LLVMBuildBitCast(builder, res, i32_bld.vec_type, "");
|
||||||
tmp = lp_build_and(&i32_bld, i32_floatexpmask, wasinfnan);
|
tmp = lp_build_and(&i32_bld, i32_floatexpmask, wasinfnan);
|
||||||
res = lp_build_or(&i32_bld, tmp, res);
|
res = lp_build_or(&i32_bld, tmp, res);
|
||||||
|
|
||||||
|
if (has_sign) {
|
||||||
|
LLVMValueRef signmask = lp_build_const_int_vec(gallivm, i32_type, 0x80000000);
|
||||||
|
shift = lp_build_const_int_vec(gallivm, i32_type, 8 - exponent_bits);
|
||||||
|
sign = lp_build_shl(&i32_bld, src, shift);
|
||||||
|
sign = lp_build_and(&i32_bld, signmask, src);
|
||||||
|
res = lp_build_or(&i32_bld, res, sign);
|
||||||
|
}
|
||||||
|
|
||||||
return LLVMBuildBitCast(builder, res, f32_bld.vec_type, "");
|
return LLVMBuildBitCast(builder, res, f32_bld.vec_type, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,9 +290,9 @@ lp_build_r11g11b10_to_float(struct gallivm_state *gallivm,
|
|||||||
LLVMGetVectorSize(src_type) : 1;
|
LLVMGetVectorSize(src_type) : 1;
|
||||||
struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
|
struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
|
||||||
|
|
||||||
dst[0] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 0);
|
dst[0] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 6, 5, 0, false);
|
||||||
dst[1] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 11);
|
dst[1] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 6, 5, 11, false);
|
||||||
dst[2] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 5, 5, 22);
|
dst[2] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 5, 5, 22, false);
|
||||||
|
|
||||||
/* Just set alpha to one */
|
/* Just set alpha to one */
|
||||||
dst[3] = lp_build_one(gallivm, f32_type);
|
dst[3] = lp_build_one(gallivm, f32_type);
|
||||||
|
Reference in New Issue
Block a user