summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZack Rusin <zackr@vmware.com>2013-07-16 15:57:11 -0400
committerZack Rusin <zackr@vmware.com>2013-07-19 16:29:17 -0400
commit13e2cd2f2ccb06cd6dc9acda0b6bbe268ee37879 (patch)
treec05af0756b62860ecda3a8b97ab9b7d33c072cba
parent7b672c1503abffcc83a30a9731419539f52ac73f (diff)
gallivm: add a version of log2 which handles edge cases
That means that if input is: * - less than zero (to and including -inf) then NaN will be returned * - equal to zero (-denorm, -0, +0 or +denorm), then -inf will be returned * - +infinity, then +infinity will be returned * - NaN, then NaN will be returned It's a separate function because the checks are a little bit costly and in most cases are likely unnecessary. Signed-off-by: Zack Rusin <zackr@vmware.com> Reviewed-by: Jose Fonseca <jfonseca@vmware.com> Reviewed-by: Roland Scheidegger <sroland@vmware.com>
-rw-r--r--src/gallium/auxiliary/gallivm/lp_bld_arit.c58
-rw-r--r--src/gallium/auxiliary/gallivm/lp_bld_arit.h7
-rw-r--r--src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c6
3 files changed, 65 insertions, 6 deletions
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_arit.c b/src/gallium/auxiliary/gallivm/lp_bld_arit.c
index 34e3ed94542..b3b3c925808 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_arit.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_arit.c
@@ -3349,13 +3349,25 @@ const double lp_build_log2_polynomial[] = {
* See http://www.devmaster.net/forums/showthread.php?p=43580
* http://en.wikipedia.org/wiki/Logarithm#Calculation
* http://www.nezumi.demon.co.uk/consult/logx.htm
+ *
+ * If handle_edge_cases is true the function will perform computations
+ * to match the required D3D10+ behavior for each of the edge cases.
+ * That means that if input is:
+ * - less than zero (to and including -inf) then NaN will be returned
+ * - equal to zero (-denorm, -0, +0 or +denorm), then -inf will be returned
+ * - +infinity, then +infinity will be returned
+ * - NaN, then NaN will be returned
+ *
+ * Those checks are fairly expensive so if you don't need them make sure
+ * handle_edge_cases is false.
*/
void
lp_build_log2_approx(struct lp_build_context *bld,
LLVMValueRef x,
LLVMValueRef *p_exp,
LLVMValueRef *p_floor_log2,
- LLVMValueRef *p_log2)
+ LLVMValueRef *p_log2,
+ boolean handle_edge_cases)
{
LLVMBuilderRef builder = bld->gallivm->builder;
const struct lp_type type = bld->type;
@@ -3428,6 +3440,29 @@ lp_build_log2_approx(struct lp_build_context *bld,
logmant = lp_build_mul(bld, y, logmant);
res = lp_build_add(bld, logmant, logexp);
+
+ if (type.floating && handle_edge_cases) {
+ LLVMValueRef negmask, infmask, zmask;
+ negmask = lp_build_cmp(bld, PIPE_FUNC_LESS, x,
+ lp_build_const_vec(bld->gallivm, type, 0.0f));
+ zmask = lp_build_cmp(bld, PIPE_FUNC_EQUAL, x,
+ lp_build_const_vec(bld->gallivm, type, 0.0f));
+ infmask = lp_build_cmp(bld, PIPE_FUNC_GEQUAL, x,
+ lp_build_const_vec(bld->gallivm, type, INFINITY));
+
+ /* If x is qual to inf make sure we return inf */
+ res = lp_build_select(bld, infmask,
+ lp_build_const_vec(bld->gallivm, type, INFINITY),
+ res);
+ /* If x is qual to 0, return -inf */
+ res = lp_build_select(bld, zmask,
+ lp_build_const_vec(bld->gallivm, type, -INFINITY),
+ res);
+ /* If x is nan or less than 0, return nan */
+ res = lp_build_select(bld, negmask,
+ lp_build_const_vec(bld->gallivm, type, NAN),
+ res);
+ }
}
if(p_exp) {
@@ -3443,12 +3478,31 @@ lp_build_log2_approx(struct lp_build_context *bld,
}
+/*
+ * log2 implementation which doesn't have special code to
+ * handle edge cases (-inf, 0, inf, NaN). It's faster but
+ * the results for those cases are undefined.
+ */
LLVMValueRef
lp_build_log2(struct lp_build_context *bld,
LLVMValueRef x)
{
LLVMValueRef res;
- lp_build_log2_approx(bld, x, NULL, NULL, &res);
+ lp_build_log2_approx(bld, x, NULL, NULL, &res, FALSE);
+ return res;
+}
+
+/*
+ * Version of log2 which handles all edge cases.
+ * Look at documentation of lp_build_log2_approx for
+ * description of the behavior for each of the edge cases.
+ */
+LLVMValueRef
+lp_build_log2_safe(struct lp_build_context *bld,
+ LLVMValueRef x)
+{
+ LLVMValueRef res;
+ lp_build_log2_approx(bld, x, NULL, NULL, &res, TRUE);
return res;
}
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_arit.h b/src/gallium/auxiliary/gallivm/lp_bld_arit.h
index 14b3a164faa..ac06a2c0971 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_arit.h
+++ b/src/gallium/auxiliary/gallivm/lp_bld_arit.h
@@ -309,6 +309,10 @@ lp_build_log2(struct lp_build_context *bld,
LLVMValueRef a);
LLVMValueRef
+lp_build_log2_safe(struct lp_build_context *bld,
+ LLVMValueRef a);
+
+LLVMValueRef
lp_build_fast_log2(struct lp_build_context *bld,
LLVMValueRef a);
@@ -328,7 +332,8 @@ lp_build_log2_approx(struct lp_build_context *bld,
LLVMValueRef x,
LLVMValueRef *p_exp,
LLVMValueRef *p_floor_log2,
- LLVMValueRef *p_log2);
+ LLVMValueRef *p_log2,
+ boolean handle_nans);
LLVMValueRef
lp_build_mod(struct lp_build_context *bld,
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c
index f23e08b77fb..d16ccae37fb 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c
@@ -1236,8 +1236,8 @@ lg2_emit_cpu(
struct lp_build_tgsi_context * bld_base,
struct lp_build_emit_data * emit_data)
{
- emit_data->output[emit_data->chan] = lp_build_log2(&bld_base->base,
- emit_data->args[0]);
+ emit_data->output[emit_data->chan] = lp_build_log2_safe(&bld_base->base,
+ emit_data->args[0]);
}
/* TGSI_OPCODE_LOG (CPU Only) */
@@ -1253,7 +1253,7 @@ log_emit_cpu(
LLVMValueRef src0 = emit_data->args[0];
lp_build_log2_approx(&bld_base->base, src0,
- &p_exp, &p_floor_log2, &p_log2);
+ &p_exp, &p_floor_log2, &p_log2, FALSE);
emit_data->output[TGSI_CHAN_X] = p_floor_log2;