llvmpipe: Fix rendering to PIPE_FORMAT_R10G10B10A2_UNORM.

We must take rounding in consideration when re-scaling to narrow
normalized channels, such as 2-bit normalized alpha.

Reviewed-by: Roland Scheidegger <sroland@vmware.com>
This commit is contained in:
José Fonseca 2013-09-20 12:58:59 +01:00
parent 2ab4e1d1e6
commit 1569b3e536

View File

@ -876,7 +876,22 @@ lp_blend_type_from_format_desc(const struct util_format_description *format_desc
/**
* Scale a normalized value from src_bits to dst_bits
* Scale a normalized value from src_bits to dst_bits.
*
* The exact calculation is
*
* dst = iround(src * dst_mask / src_mask)
*
* or with integer rounding
*
* dst = src * (2*dst_mask + sign(src)*src_mask) / (2*src_mask)
*
* where
*
* src_mask = (1 << src_bits) - 1
* dst_mask = (1 << dst_bits) - 1
*
* but we try to avoid division and multiplication through shifts.
*/
static INLINE LLVMValueRef
scale_bits(struct gallivm_state *gallivm,
@ -889,11 +904,68 @@ scale_bits(struct gallivm_state *gallivm,
LLVMValueRef result = src;
if (dst_bits < src_bits) {
/* Scale down by LShr */
result = LLVMBuildLShr(builder,
src,
lp_build_const_int_vec(gallivm, src_type, src_bits - dst_bits),
"");
int delta_bits = src_bits - dst_bits;
if (delta_bits <= dst_bits) {
/*
* Approximate the rescaling with a single shift.
*
* This gives the wrong rounding.
*/
result = LLVMBuildLShr(builder,
src,
lp_build_const_int_vec(gallivm, src_type, delta_bits),
"");
} else {
/*
* Try more accurate rescaling.
*/
/*
* Drop the least significant bits to make space for the multiplication.
*
* XXX: A better approach would be to use a wider integer type as intermediate. But
* this is enough to convert alpha from 16bits -> 2 when rendering to
* PIPE_FORMAT_R10G10B10A2_UNORM.
*/
result = LLVMBuildLShr(builder,
src,
lp_build_const_int_vec(gallivm, src_type, dst_bits),
"");
result = LLVMBuildMul(builder,
result,
lp_build_const_int_vec(gallivm, src_type, (1LL << dst_bits) - 1),
"");
/*
* Add a rounding term before the division.
*
* TODO: Handle signed integers too.
*/
if (!src_type.sign) {
result = LLVMBuildAdd(builder,
result,
lp_build_const_int_vec(gallivm, src_type, (1LL << (delta_bits - 1))),
"");
}
/*
* Approximate the division by src_mask with a src_bits shift.
*
* Given the src has already been shifted by dst_bits, all we need
* to do is to shift by the difference.
*/
result = LLVMBuildLShr(builder,
result,
lp_build_const_int_vec(gallivm, src_type, delta_bits),
"");
}
} else if (dst_bits > src_bits) {
/* Scale up bits */
int db = dst_bits - src_bits;