diff options
-rw-r--r-- | src/gallium/auxiliary/gallivm/lp_bld_conv.c | 243 | ||||
-rw-r--r-- | src/gallium/auxiliary/gallivm/lp_bld_conv.h | 9 | ||||
-rw-r--r-- | src/gallium/drivers/llvmpipe/lp_screen.c | 6 | ||||
-rw-r--r-- | src/gallium/drivers/llvmpipe/lp_state_fs.c | 126 |
4 files changed, 382 insertions, 2 deletions
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_conv.c b/src/gallium/auxiliary/gallivm/lp_bld_conv.c index dc3649db363..053f4132080 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_conv.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_conv.c @@ -155,6 +155,249 @@ lp_build_bswap_vec(struct gallivm_state *gallivm, /** + * Convert float32 to a float-like value with less exponent and mantissa + * bits. The mantissa is still biased, and the mantissa still has an implied 1, + * but there's no sign bit. + * + * @param src (vector) float value to convert + * @param mantissa_bits the number of mantissa bits + * @param exponent_bits the number of exponent bits + * + * Unlike float_to_half using accurate method here. + * This implements round-towards-zero (trunc) hence too large numbers get + * converted to largest representable number, not infinity. + * Small numbers may get converted to denorms, depending on normal + * float denorm handling of the cpu. + * Note that compared to the references, below, we skip any rounding bias + * since we do rounding towards zero - OpenGL allows rounding towards zero + * (though not preferred) and DX10 even seems to require it. + * Note that this will not do any packing - the value will + * look like a "rescaled float" (except for Inf/NaN) but be returned + * as int32. + * + * ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ + * ref https://gist.github.com/rygorous/2156668 + */ +static LLVMValueRef +lp_build_float_to_smallfloat_nosign(struct gallivm_state *gallivm, + struct lp_type i32_type, + LLVMValueRef src, + unsigned mantissa_bits, + unsigned exponent_bits) +{ + LLVMBuilderRef builder = gallivm->builder; + LLVMValueRef i32_floatexpmask, i32_smallexpmask, magic, normal; + LLVMValueRef clamped, tmp, i32_roundmask, small_max, src_abs; + LLVMValueRef is_nan, is_posinf, is_nan_or_posinf, i32_qnanbit, nan_or_posinf; + struct lp_type f32_type = lp_type_float_vec(32, 32 * i32_type.length); + struct lp_build_context f32_bld, i32_bld; + LLVMValueRef zero = lp_build_const_vec(gallivm, f32_type, 0.0f); + + lp_build_context_init(&f32_bld, gallivm, f32_type); + lp_build_context_init(&i32_bld, gallivm, i32_type); + + i32_smallexpmask = lp_build_const_int_vec(gallivm, i32_type, + ((1 << exponent_bits) - 1) << 23); + i32_floatexpmask = lp_build_const_int_vec(gallivm, i32_type, 0xff << 23); + + /* "ordinary" number */ + /* clamp to pos range (can still have sign bit if NaN or negative zero) */ + clamped = lp_build_max(&f32_bld, src, zero); + clamped = LLVMBuildBitCast(builder, clamped, i32_bld.vec_type, ""); + /* get rid of excess mantissa bits, and while here also potential sign bit */ + i32_roundmask = lp_build_const_int_vec(gallivm, i32_type, + ~((1 << (23 - mantissa_bits)) - 1) | + 0x7fffffff); + + tmp = lp_build_and(&i32_bld, clamped, i32_roundmask); + tmp = LLVMBuildBitCast(builder, tmp, f32_bld.vec_type, ""); + /* bias exponent (and denormalize if necessary) */ + magic = lp_build_const_int_vec(gallivm, i32_type, + ((1 << (exponent_bits - 1)) - 1) << 23); + magic = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, ""); + normal = lp_build_mul(&f32_bld, tmp, magic); + + /* clamp to max value */ + small_max = lp_build_const_int_vec(gallivm, i32_type, + (((1 << exponent_bits) - 2) << 23) | + (((1 << mantissa_bits) - 1) << (23 - mantissa_bits))); + small_max = LLVMBuildBitCast(builder, small_max, f32_bld.vec_type, ""); + normal = lp_build_min(&f32_bld, normal, small_max); + normal = LLVMBuildBitCast(builder, normal, i32_bld.vec_type, ""); + + /* + * handle nan/inf cases + * a little bit tricky since -Inf -> 0, +Inf -> +Inf, +-Nan -> +Nan + * Note that on a lucky day, we could simplify this a bit, + * by just using the max(src, zero) result - this will have -Inf + * clamped to 0, and MIGHT preserve the NaNs. + */ + src_abs = lp_build_abs(&f32_bld, src); + src_abs = LLVMBuildBitCast(builder, src_abs, i32_bld.vec_type, ""); + src = LLVMBuildBitCast(builder, src, i32_bld.vec_type, ""); + is_nan = lp_build_compare(gallivm, i32_type, PIPE_FUNC_GREATER, + src_abs, i32_floatexpmask); + is_posinf = lp_build_compare(gallivm, i32_type, PIPE_FUNC_EQUAL, + src, i32_floatexpmask); + is_nan_or_posinf = lp_build_and(&i32_bld, is_nan, is_posinf); + /* could also set more mantissa bits but need at least the highest mantissa bit */ + i32_qnanbit = lp_build_const_vec(gallivm, i32_type, 1 << 22); + /* combine maxexp with qnanbit */ + nan_or_posinf = lp_build_or(&i32_bld, i32_smallexpmask, + lp_build_and(&i32_bld, is_nan, i32_qnanbit)); + + return lp_build_select(&i32_bld, is_nan_or_posinf, nan_or_posinf, normal); +} + + +/** + * Convert rgba float SoA values to packed r11g11b10 values. + * + * @param src SoA float (vector) values to convert. + */ +LLVMValueRef +lp_build_float_to_r11g11b10(struct gallivm_state *gallivm, + LLVMValueRef *src) +{ + LLVMValueRef dst, rcomp, bcomp, gcomp, shift, mask; + struct lp_build_context i32_bld; + LLVMTypeRef src_type = LLVMTypeOf(*src); + unsigned src_length = LLVMGetTypeKind(src_type) == LLVMVectorTypeKind ? + LLVMGetVectorSize(src_type) : 1; + struct lp_type i32_type = lp_type_int_vec(32, 32 * src_length); + + lp_build_context_init(&i32_bld, gallivm, i32_type); + + /* "rescale" - this does the actual conversion except the packing */ + rcomp = lp_build_float_to_smallfloat_nosign(gallivm, i32_type, src[0], 6, 5); + gcomp = lp_build_float_to_smallfloat_nosign(gallivm, i32_type, src[1], 6, 5); + bcomp = lp_build_float_to_smallfloat_nosign(gallivm, i32_type, src[2], 5, 5); + + /* pack rescaled SoA floats to r11g11b10 AoS values */ + shift = lp_build_const_int_vec(gallivm, i32_type, 23 - 6); + rcomp = lp_build_shr(&i32_bld, rcomp, shift); + + shift = lp_build_const_int_vec(gallivm, i32_type, 23 - 17); + mask = lp_build_const_int_vec(gallivm, i32_type, 0x7ff << 11); + gcomp = lp_build_shr(&i32_bld, gcomp, shift); + gcomp = lp_build_and(&i32_bld, gcomp, mask); + + shift = lp_build_const_int_vec(gallivm, i32_type, 27 - 23); + mask = lp_build_const_int_vec(gallivm, i32_type, 0x3ff << 22); + bcomp = lp_build_shl(&i32_bld, bcomp, shift); + bcomp = lp_build_and(&i32_bld, bcomp, mask); + + dst = lp_build_or(&i32_bld, rcomp, gcomp); + return lp_build_or(&i32_bld, dst, bcomp); +} + + +/** + * Convert a float-like value with less exponent and mantissa + * bits than a normal float32 to a float32. The mantissa of + * the source value is assumed to have an implied 1, and the exponent + * is biased. There are no negative values. + * The source value to extract must be in a 32bit int. + * While this helper is generic, it is only ever going to be useful for + * r11g11b10 (no other common format exists with the same properties). + * + * @param src (vector) value to convert + * @param mantissa_bits the number of mantissa bits + * @param exponent_bits the number of exponent bits + * @param mantissa_start the bit start position of the packed component + * + * ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ + * ref https://gist.github.com/rygorous/2156668 + */ +static LLVMValueRef +lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm, + struct lp_type f32_type, + LLVMValueRef src, + unsigned mantissa_bits, + unsigned exponent_bits, + unsigned mantissa_start) +{ + LLVMBuilderRef builder = gallivm->builder; + LLVMValueRef smallexpmask, i32_floatexpmask, magic; + LLVMValueRef wasinfnan, tmp, res, shift, mask; + unsigned exponent_start = mantissa_start + mantissa_bits; + struct lp_type i32_type = lp_type_int_vec(32, 32 * f32_type.length); + struct lp_build_context f32_bld, i32_bld; + + lp_build_context_init(&f32_bld, gallivm, f32_type); + lp_build_context_init(&i32_bld, gallivm, i32_type); + + /* extract the component to "float position" */ + if (exponent_start < 23) { + shift = lp_build_const_int_vec(gallivm, i32_type, 23 - exponent_start); + src = lp_build_shl(&i32_bld, src, shift); + } + else { + shift = lp_build_const_int_vec(gallivm, i32_type, exponent_start - 23); + src = lp_build_shr(&i32_bld, src, shift); + } + mask = lp_build_const_int_vec(gallivm, i32_type, + ((1 << (mantissa_bits + exponent_bits)) - 1) << + (23 - mantissa_bits)); + src = lp_build_and(&i32_bld, src, mask); + src = LLVMBuildBitCast(builder, src, f32_bld.vec_type, ""); + + /* now do the actual scaling */ + smallexpmask = lp_build_const_int_vec(gallivm, i32_type, + ((1 << exponent_bits) - 1) << 23); + i32_floatexpmask = lp_build_const_int_vec(gallivm, i32_type, 0xff << 23); + /* + * magic number has exponent new exp bias + (new exp bias - old exp bias), + * mantissa is 0. + */ + magic = lp_build_const_int_vec(gallivm, i32_type, + (255 - (1 << (exponent_bits - 1))) << 23); + magic = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, ""); + + /* adjust exponent and fix denorms */ + res = lp_build_mul(&f32_bld, src, magic); + + /* + * 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) + */ + /* use float compare (better for AVX 8-wide / no AVX2 though otherwise should use int) */ + smallexpmask = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, ""); + wasinfnan = lp_build_compare(gallivm, f32_type, PIPE_FUNC_GEQUAL, src, smallexpmask); + res = LLVMBuildBitCast(builder, res, i32_bld.vec_type, ""); + tmp = lp_build_and(&i32_bld, i32_floatexpmask, wasinfnan); + res = lp_build_or(&i32_bld, tmp, res); + + return LLVMBuildBitCast(builder, res, f32_bld.vec_type, ""); +} + + +/** + * Convert packed float format (r11g11b10) value(s) to rgba float SoA values. + * + * @param src packed AoS r11g11b10 values (as (vector) int32) + * @param dst pointer to the SoA result values + */ +void +lp_build_r11g11b10_to_float(struct gallivm_state *gallivm, + LLVMValueRef src, + LLVMValueRef *dst) +{ + 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); + + dst[0] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 0); + dst[1] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 11); + dst[2] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 5, 5, 22); + + /* Just set alpha to one */ + dst[3] = lp_build_one(gallivm, f32_type); +} + + +/** * Converts int16 half-float to float32 * Note this can be performed in 1 instruction if vcvtph2ps exists (sse5 i think?) * [llvm.x86.vcvtph2ps / _mm_cvtph_ps] diff --git a/src/gallium/auxiliary/gallivm/lp_bld_conv.h b/src/gallium/auxiliary/gallivm/lp_bld_conv.h index d7dfed85187..5bd6f4f1d75 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_conv.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_conv.h @@ -62,6 +62,15 @@ lp_build_float_to_half(struct gallivm_state *gallivm, LLVMValueRef src); LLVMValueRef +lp_build_float_to_r11g11b10(struct gallivm_state *gallivm, + LLVMValueRef *src); + +void +lp_build_r11g11b10_to_float(struct gallivm_state *gallivm, + LLVMValueRef src, + LLVMValueRef *dst); + +LLVMValueRef lp_build_clamped_float_to_unsigned_norm(struct gallivm_state *gallivm, struct lp_type src_type, unsigned dst_width, diff --git a/src/gallium/drivers/llvmpipe/lp_screen.c b/src/gallium/drivers/llvmpipe/lp_screen.c index 5ec1df659b8..6760db0b38f 100644 --- a/src/gallium/drivers/llvmpipe/lp_screen.c +++ b/src/gallium/drivers/llvmpipe/lp_screen.c @@ -322,7 +322,8 @@ llvmpipe_is_format_supported( struct pipe_screen *_screen, if (format_desc->colorspace != UTIL_FORMAT_COLORSPACE_RGB) return FALSE; - if (format_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN) + if (format_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN && + format != PIPE_FORMAT_R11G11B10_FLOAT) return FALSE; assert(format_desc->block.width == 1); assert(format_desc->block.height == 1); @@ -330,7 +331,8 @@ llvmpipe_is_format_supported( struct pipe_screen *_screen, if (format_desc->is_mixed) return FALSE; - if (!format_desc->is_array && !format_desc->is_bitmask) + if (!format_desc->is_array && !format_desc->is_bitmask && + format != PIPE_FORMAT_R11G11B10_FLOAT) return FALSE; /* diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.c b/src/gallium/drivers/llvmpipe/lp_state_fs.c index d8369b4d807..953a5c1aa44 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.c @@ -972,6 +972,17 @@ lp_mem_type_from_format_desc(const struct util_format_description *format_desc, unsigned i; unsigned chan; + if (format_desc->format == PIPE_FORMAT_R11G11B10_FLOAT) { + /* just make this a 32bit uint */ + type->floating = false; + type->fixed = false; + type->sign = false; + type->norm = false; + type->width = 32; + type->length = 1; + return; + } + for (i = 0; i < 4; i++) if (format_desc->channel[i].type != UTIL_FORMAT_TYPE_VOID) break; @@ -1009,6 +1020,17 @@ lp_blend_type_from_format_desc(const struct util_format_description *format_desc unsigned i; unsigned chan; + if (format_desc->format == PIPE_FORMAT_R11G11B10_FLOAT) { + /* always use ordinary floats for blending */ + type->floating = true; + type->fixed = false; + type->sign = true; + type->norm = false; + type->width = 32; + type->length = 4; + return; + } + for (i = 0; i < 4; i++) if (format_desc->channel[i].type != UTIL_FORMAT_TYPE_VOID) break; @@ -1122,6 +1144,48 @@ convert_to_blend_type(struct gallivm_state *gallivm, unsigned pixels = 16 / num_srcs; bool is_arith; + /* + * full custom path for packed floats - none of the later functions would do + * anything useful, and given the lp_type representation they can't be fixed. + */ + if (src_fmt->format == PIPE_FORMAT_R11G11B10_FLOAT) { + LLVMValueRef tmpsrc[4]; + /* + * This is pretty suboptimal for this case blending in SoA would be much + * better, since conversion gets us SoA values so need to convert back. + */ + assert(src_type.width == 32); + assert(dst_type.floating); + assert(dst_type.width = 32); + assert(dst_type.length % 4 == 0); + for (i = 0; i < 4; i++) { + tmpsrc[i] = src[i]; + } + for (i = 0; i < num_srcs / 4; i++) { + LLVMValueRef tmpsoa[4]; + LLVMValueRef tmps = tmpsrc[i]; + if (num_srcs == 8) { + LLVMValueRef shuffles[8]; + unsigned j; + /* fetch was 4 values but need 8-wide output values */ + tmps = lp_build_concat(gallivm, &tmpsrc[i * 2], src_type, 2); + /* + * for 8-wide aos transpose would give us wrong order not matching + * incoming converted fs values and mask. ARGH. + */ + for (j = 0; j < 4; j++) { + shuffles[j] = lp_build_const_int32(gallivm, j * 2); + shuffles[j + 4] = lp_build_const_int32(gallivm, j * 2 + 1); + } + tmps = LLVMBuildShuffleVector(builder, tmps, tmps, + LLVMConstVector(shuffles, 8), ""); + } + lp_build_r11g11b10_to_float(gallivm, tmps, tmpsoa); + lp_build_transpose_aos(gallivm, dst_type, tmpsoa, &src[i * 4]); + } + return; + } + lp_mem_type_from_format_desc(src_fmt, &mem_type); lp_blend_type_from_format_desc(src_fmt, &blend_type); @@ -1225,6 +1289,47 @@ convert_from_blend_type(struct gallivm_state *gallivm, unsigned pixels = 16 / num_srcs; bool is_arith; + /* + * full custom path for packed floats - none of the later functions would do + * anything useful, and given the lp_type representation they can't be fixed. + */ + if (src_fmt->format == PIPE_FORMAT_R11G11B10_FLOAT) { + /* + * This is pretty suboptimal for this case blending in SoA would be much + * better - we need to transpose the AoS values back to SoA values for + * conversion/packing. + */ + assert(src_type.floating); + assert(src_type.width = 32); + assert(src_type.length % 4 == 0); + assert(dst_type.width == 32); + for (i = 0; i < num_srcs / 4; i++) { + LLVMValueRef tmpsoa[4], tmpdst; + lp_build_transpose_aos(gallivm, src_type, &src[i * 4], tmpsoa); + tmpdst = lp_build_float_to_r11g11b10(gallivm, tmpsoa); + if (num_srcs == 8) { + LLVMValueRef tmpaos, shuffles[8]; + unsigned j; + /* + * for 8-wide aos transpose has given us wrong order not matching + * output order. HMPF. Also need to split the output values manually. + */ + for (j = 0; j < 4; j++) { + shuffles[j * 2] = lp_build_const_int32(gallivm, j); + shuffles[j * 2 + 1] = lp_build_const_int32(gallivm, j + 4); + } + tmpaos = LLVMBuildShuffleVector(builder, tmpdst, tmpdst, + LLVMConstVector(shuffles, 8), ""); + src[i * 2] = lp_build_extract_range(gallivm, tmpaos, 0, 4); + src[i * 2 + 1] = lp_build_extract_range(gallivm, tmpaos, 4, 4); + } + else { + src[i] = tmpdst; + } + } + return; + } + lp_mem_type_from_format_desc(src_fmt, &mem_type); lp_blend_type_from_format_desc(src_fmt, &blend_type); @@ -1532,6 +1637,17 @@ generate_unswizzled_blend(struct gallivm_state *gallivm, } } + if (out_format == PIPE_FORMAT_R11G11B10_FLOAT) { + /* the code above can't work for layout_other */ + dst_channels = 4; /* HACK: this is fake 4 really but need it due to transpose stuff later */ + has_alpha = true; + swizzle[0] = 0; + swizzle[1] = 1; + swizzle[2] = 2; + swizzle[3] = 3; + pad_inline = true; /* HACK: prevent rgbxrgbx->rgbrgbxx conversion later */ + } + /* If 3 channels then pad to include alpha for 4 element transpose */ if (dst_channels == 3 && !has_alpha) { for (i = 0; i < TGSI_NUM_CHANNELS; i++) { @@ -1756,6 +1872,16 @@ generate_unswizzled_blend(struct gallivm_state *gallivm, dst_type.length *= 16 / dst_count; + if (out_format == PIPE_FORMAT_R11G11B10_FLOAT) { + /* + * we need multiple values at once for the conversion, so can as well + * load them vectorized here too instead of concatenating later. + * (Still need concatenation later for 8-wide vectors). + */ + dst_count = block_height; + dst_type.length = block_width; + } + load_unswizzled_block(gallivm, color_ptr, stride, block_width, block_height, dst, dst_type, dst_count, dst_alignment); |