llvmpipe: Fix perspective divide interpolation.

Intuition != mathematics, so this time I actually worked out the right
formula for first order approximation of perspective interpolation.

Ironically, per quad divide actually makes things slower when compared
with per pixel divide -- probably because the divide hardware unit is
rarely used, whereas the multiply unit is typically already saturated
and the first order approximation imply more multiplications.
This commit is contained in:
José Fonseca
2010-09-04 19:51:54 +01:00
parent b8684b2458
commit d278ddc009
2 changed files with 76 additions and 9 deletions
+74 -9
View File
@@ -75,6 +75,33 @@
*/
/**
* Do one perspective divide per quad.
*
* For perspective interpolation, the final attribute value is given
*
* a' = a/w = a * oow
*
* where
*
* a = a0 + dadx*x + dady*y
* w = w0 + dwdx*x + dwdy*y
* oow = 1/w = 1/(w0 + dwdx*x + dwdy*y)
*
* Instead of computing the division per pixel, with this macro we compute the
* division on the upper left pixel of each quad, and use a linear
* approximation in the remaining pixels, given by:
*
* da'dx = (dadx - dwdx*a)*oow
* da'dy = (dady - dwdy*a)*oow
*
* Ironically, this actually makes things slower -- probably because the
* divide hardware unit is rarely used, whereas the multiply unit is typically
* already saturated.
*/
#define PERSPECTIVE_DIVIDE_PER_QUAD 0
static const unsigned char quad_offset_x[4] = {0, 1, 0, 1};
static const unsigned char quad_offset_y[4] = {0, 0, 1, 1};
@@ -107,7 +134,6 @@ coeffs_init(struct lp_build_interp_soa_context *bld,
LLVMValueRef i1 = LLVMConstInt(LLVMInt32Type(), 1, 0);
LLVMValueRef i2 = LLVMConstInt(LLVMInt32Type(), 2, 0);
LLVMValueRef i3 = LLVMConstInt(LLVMInt32Type(), 3, 0);
LLVMValueRef oow = NULL;
unsigned attrib;
unsigned chan;
@@ -213,22 +239,22 @@ coeffs_init(struct lp_build_interp_soa_context *bld,
a = LLVMBuildFAdd(builder, a, dadq2, "");
#if PERSPECTIVE_DIVIDE_PER_QUAD
/*
* a *= 1 / w
* dadq *= 1 / w
* a *= 1 / w
*/
if (interp == LP_INTERP_PERSPECTIVE) {
LLVMValueRef w = bld->a[0][3];
assert(attrib != 0);
assert(bld->mask[0] & TGSI_WRITEMASK_W);
if (!oow) {
oow = lp_build_rcp(coeff_bld, w);
lp_build_name(oow, "oow");
if (!bld->oow) {
bld->oow = lp_build_rcp(coeff_bld, w);
lp_build_name(bld->oow, "oow");
}
a = lp_build_mul(coeff_bld, a, oow);
dadq = lp_build_mul(coeff_bld, dadq, oow);
a = lp_build_mul(coeff_bld, a, bld->oow);
}
#endif
attrib_name(a, attrib, chan, ".a");
attrib_name(dadq, attrib, chan, ".dadq");
@@ -250,6 +276,7 @@ attribs_update(struct lp_build_interp_soa_context *bld, int quad_index)
{
struct lp_build_context *coeff_bld = &bld->coeff_bld;
LLVMValueRef shuffle = lp_build_const_int_vec(coeff_bld->type, quad_index);
LLVMValueRef oow = NULL;
unsigned attrib;
unsigned chan;
@@ -270,6 +297,8 @@ attribs_update(struct lp_build_interp_soa_context *bld, int quad_index)
a = bld->attribs[0][chan];
}
else {
LLVMValueRef dadq;
a = bld->a[attrib][chan];
/*
@@ -279,11 +308,47 @@ attribs_update(struct lp_build_interp_soa_context *bld, int quad_index)
a = LLVMBuildShuffleVector(coeff_bld->builder,
a, coeff_bld->undef, shuffle, "");
/*
* Get the derivatives.
*/
dadq = bld->dadq[attrib][chan];
#if PERSPECTIVE_DIVIDE_PER_QUAD
if (interp == LP_INTERP_PERSPECTIVE) {
LLVMValueRef dwdq = bld->dadq[0][3];
if (oow == NULL) {
assert(bld->oow);
oow = LLVMBuildShuffleVector(coeff_bld->builder,
bld->oow, coeff_bld->undef,
shuffle, "");
}
dadq = lp_build_sub(coeff_bld,
dadq,
lp_build_mul(coeff_bld, a, dwdq));
dadq = lp_build_mul(coeff_bld, dadq, oow);
}
#endif
/*
* Add the derivatives
*/
a = lp_build_add(coeff_bld, a, bld->dadq[attrib][chan]);
a = lp_build_add(coeff_bld, a, dadq);
#if !PERSPECTIVE_DIVIDE_PER_QUAD
if (interp == LP_INTERP_PERSPECTIVE) {
if (oow == NULL) {
LLVMValueRef w = bld->attribs[0][3];
assert(attrib != 0);
assert(bld->mask[0] & TGSI_WRITEMASK_W);
oow = lp_build_rcp(coeff_bld, w);
}
a = lp_build_mul(coeff_bld, a, oow);
}
#endif
attrib_name(a, attrib, chan, "");
}
@@ -64,6 +64,8 @@ struct lp_build_interp_soa_context
LLVMValueRef a [1 + PIPE_MAX_SHADER_INPUTS][NUM_CHANNELS];
LLVMValueRef dadq[1 + PIPE_MAX_SHADER_INPUTS][NUM_CHANNELS];
LLVMValueRef oow;
LLVMValueRef attribs[1 + PIPE_MAX_SHADER_INPUTS][NUM_CHANNELS];
/*