+ /* Possibly swap the front and back attrib values,
+ *
+ * Prefer select to if so we don't have to worry about phis or
+ * allocas.
+ */
+ attribv[0] = LLVMBuildSelect(b, front_facing, a0_back, attribv[0], "");
+ attribv[1] = LLVMBuildSelect(b, front_facing, a1_back, attribv[1], "");
+ attribv[2] = LLVMBuildSelect(b, front_facing, a2_back, attribv[2], "");
+
+}
+
+static void
+lp_do_offset_tri(struct gallivm_state *gallivm,
+ struct lp_setup_args *args,
+ const struct lp_setup_variant_key *key,
+ LLVMValueRef inv_det,
+ LLVMValueRef dxyz01,
+ LLVMValueRef dxyz20,
+ LLVMValueRef attribv[3])
+{
+ LLVMBuilderRef b = gallivm->builder;
+ struct lp_build_context flt_scalar_bld;
+ struct lp_build_context int_scalar_bld;
+ struct lp_build_context *bld = &args->bld;
+ LLVMValueRef zoffset, mult;
+ LLVMValueRef z0_new, z1_new, z2_new;
+ LLVMValueRef dzdxdzdy, dzdx, dzdy, dzxyz20, dyzzx01, dyzzx01_dzxyz20, dzx01_dyz20;
+ LLVMValueRef z0z1, z0z1z2;
+ LLVMValueRef max, max_value, res12;
+ LLVMValueRef shuffles[4];
+ LLVMTypeRef shuf_type = LLVMInt32TypeInContext(gallivm->context);
+ LLVMValueRef onei = lp_build_const_int32(gallivm, 1);
+ LLVMValueRef zeroi = lp_build_const_int32(gallivm, 0);
+ LLVMValueRef twoi = lp_build_const_int32(gallivm, 2);
+ LLVMValueRef threei = lp_build_const_int32(gallivm, 3);
+
+ /* (res12) = cross(e,f).xy */
+ shuffles[0] = twoi;
+ shuffles[1] = zeroi;
+ shuffles[2] = onei;
+ shuffles[3] = twoi;
+ dzxyz20 = LLVMBuildShuffleVector(b, dxyz20, dxyz20, LLVMConstVector(shuffles, 4), "");
+
+ shuffles[0] = onei;
+ shuffles[1] = twoi;
+ shuffles[2] = twoi;
+ shuffles[3] = zeroi;
+ dyzzx01 = LLVMBuildShuffleVector(b, dxyz01, dxyz01, LLVMConstVector(shuffles, 4), "");
+
+ dyzzx01_dzxyz20 = LLVMBuildFMul(b, dzxyz20, dyzzx01, "dyzzx01_dzxyz20");
+
+ shuffles[0] = twoi;
+ shuffles[1] = threei;
+ shuffles[2] = LLVMGetUndef(shuf_type);
+ shuffles[3] = LLVMGetUndef(shuf_type);
+ dzx01_dyz20 = LLVMBuildShuffleVector(b, dyzzx01_dzxyz20, dyzzx01_dzxyz20,
+ LLVMConstVector(shuffles, 4), "");
+
+ res12 = LLVMBuildFSub(b, dyzzx01_dzxyz20, dzx01_dyz20, "res12");
+
+ /* dzdx = fabsf(res1 * inv_det), dydx = fabsf(res2 * inv_det)*/
+ dzdxdzdy = LLVMBuildFMul(b, res12, inv_det, "dzdxdzdy");
+ dzdxdzdy = lp_build_abs(bld, dzdxdzdy);
+
+ dzdx = LLVMBuildExtractElement(b, dzdxdzdy, zeroi, "");
+ dzdy = LLVMBuildExtractElement(b, dzdxdzdy, onei, "");
+
+ /* mult = MAX2(dzdx, dzdy) * pgon_offset_scale */
+ max = LLVMBuildFCmp(b, LLVMRealUGT, dzdx, dzdy, "");
+ max_value = LLVMBuildSelect(b, max, dzdx, dzdy, "max");
+
+ mult = LLVMBuildFMul(b, max_value,
+ lp_build_const_float(gallivm, key->pgon_offset_scale), "");
+
+ lp_build_context_init(&flt_scalar_bld, gallivm, lp_type_float_vec(32, 32));
+
+ if (key->floating_point_depth) {
+ /*
+ * bias = pgon_offset_units * 2^(exponent(max(z0, z1, z2)) - mantissa_bits) +
+ * MAX2(dzdx, dzdy) * pgon_offset_scale
+ *
+ * NOTE: Assumes IEEE float32.
+ */
+ LLVMValueRef c23_shifted, exp_mask, bias, exp;
+ LLVMValueRef maxz_value, maxz0z1_value;
+
+ lp_build_context_init(&int_scalar_bld, gallivm, lp_type_int_vec(32, 32));
+
+ c23_shifted = lp_build_const_int32(gallivm, 23 << 23);
+ exp_mask = lp_build_const_int32(gallivm, 0xff << 23);
+
+ maxz0z1_value = lp_build_max(&flt_scalar_bld,
+ LLVMBuildExtractElement(b, attribv[0], twoi, ""),
+ LLVMBuildExtractElement(b, attribv[1], twoi, ""));
+
+ maxz_value = lp_build_max(&flt_scalar_bld,
+ LLVMBuildExtractElement(b, attribv[2], twoi, ""),
+ maxz0z1_value);
+
+ exp = LLVMBuildBitCast(b, maxz_value, int_scalar_bld.vec_type, "");
+ exp = lp_build_and(&int_scalar_bld, exp, exp_mask);
+ exp = lp_build_sub(&int_scalar_bld, exp, c23_shifted);
+ /* Clamping to zero means mrd will be zero for very small numbers,
+ * but specs do not indicate this should be prevented by clamping
+ * mrd to smallest normal number instead. */
+ exp = lp_build_max(&int_scalar_bld, exp, int_scalar_bld.zero);
+ exp = LLVMBuildBitCast(b, exp, flt_scalar_bld.vec_type, "");
+
+ bias = LLVMBuildFMul(b, exp,
+ lp_build_const_float(gallivm, key->pgon_offset_units),
+ "bias");
+
+ zoffset = LLVMBuildFAdd(b, bias, mult, "zoffset");
+ } else {
+ /*
+ * bias = pgon_offset_units + MAX2(dzdx, dzdy) * pgon_offset_scale
+ */
+ zoffset = LLVMBuildFAdd(b,
+ lp_build_const_float(gallivm, key->pgon_offset_units),
+ mult, "zoffset");
+ }
+
+ if (key->pgon_offset_clamp > 0) {
+ zoffset = lp_build_min(&flt_scalar_bld,
+ lp_build_const_float(gallivm, key->pgon_offset_clamp),
+ zoffset);
+ }
+ else if (key->pgon_offset_clamp < 0) {
+ zoffset = lp_build_max(&flt_scalar_bld,
+ lp_build_const_float(gallivm, key->pgon_offset_clamp),
+ zoffset);
+ }
+
+ /* yuck */
+ shuffles[0] = twoi;
+ shuffles[1] = lp_build_const_int32(gallivm, 6);
+ shuffles[2] = LLVMGetUndef(shuf_type);
+ shuffles[3] = LLVMGetUndef(shuf_type);
+ z0z1 = LLVMBuildShuffleVector(b, attribv[0], attribv[1], LLVMConstVector(shuffles, 4), "");
+ shuffles[0] = zeroi;
+ shuffles[1] = onei;
+ shuffles[2] = lp_build_const_int32(gallivm, 6);
+ shuffles[3] = LLVMGetUndef(shuf_type);
+ z0z1z2 = LLVMBuildShuffleVector(b, z0z1, attribv[2], LLVMConstVector(shuffles, 4), "");
+ zoffset = lp_build_broadcast_scalar(bld, zoffset);
+
+ /* clamp and do offset */
+ /*
+ * FIXME I suspect the clamp (is that even right to always clamp to fixed
+ * 0.0/1.0?) should really be per fragment?
+ */
+ z0z1z2 = lp_build_clamp(bld, LLVMBuildFAdd(b, z0z1z2, zoffset, ""), bld->zero, bld->one);
+
+ /* insert into args->a0.z, a1.z, a2.z:
+ */
+ z0_new = LLVMBuildExtractElement(b, z0z1z2, zeroi, "");
+ z1_new = LLVMBuildExtractElement(b, z0z1z2, onei, "");
+ z2_new = LLVMBuildExtractElement(b, z0z1z2, twoi, "");
+ attribv[0] = LLVMBuildInsertElement(b, attribv[0], z0_new, twoi, "");
+ attribv[1] = LLVMBuildInsertElement(b, attribv[1], z1_new, twoi, "");
+ attribv[2] = LLVMBuildInsertElement(b, attribv[2], z2_new, twoi, "");
+}
+
+static void
+load_attribute(struct gallivm_state *gallivm,
+ struct lp_setup_args *args,
+ const struct lp_setup_variant_key *key,
+ unsigned vert_attr,
+ LLVMValueRef attribv[3])
+{
+ LLVMBuilderRef b = gallivm->builder;
+ LLVMValueRef idx = lp_build_const_int32(gallivm, vert_attr);
+
+ /* Load the vertex data
+ */
+ attribv[0] = LLVMBuildLoad(b, LLVMBuildGEP(b, args->v0, &idx, 1, ""), "v0a");
+ attribv[1] = LLVMBuildLoad(b, LLVMBuildGEP(b, args->v1, &idx, 1, ""), "v1a");
+ attribv[2] = LLVMBuildLoad(b, LLVMBuildGEP(b, args->v2, &idx, 1, ""), "v2a");
+
+
+ /* Potentially modify it according to twoside, etc:
+ */
+ if (key->twoside) {
+ if (vert_attr == key->color_slot && key->bcolor_slot >= 0)
+ lp_twoside(gallivm, args, key, key->bcolor_slot, attribv);
+ else if (vert_attr == key->spec_slot && key->bspec_slot >= 0)
+ lp_twoside(gallivm, args, key, key->bspec_slot, attribv);
+ }
+}
+
+/*
+ * FIXME: interpolation is always done wrt fb origin (0/0).
+ * However, if some (small) tri is far away from the origin and gradients
+ * are large, this can lead to HUGE errors, since the a0 value calculated
+ * here can get very large (with the actual values inside the triangle way
+ * smaller), leading to complete loss of accuracy. This could be prevented
+ * by using some point inside (or at corner) of the tri as interpolation
+ * origin, or just use barycentric interpolation (which GL suggests and is
+ * what real hw does - you can get the barycentric coordinates from the
+ * edge functions in rasterization in principle (though we skip these
+ * sometimes completely in case of tris covering a block fully,
+ * which obviously wouldn't work)).
+ */