summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIago Toral Quiroga <itoral@igalia.com>2016-11-30 09:40:43 +0100
committerIago Toral Quiroga <itoral@igalia.com>2016-12-13 10:32:00 +0100
commita8e740c3547b0209d04b505d95a79794db31ce0b (patch)
tree010a776eb5d4f28abc6ebd422bafa39d76e56a54
parentbac303c286de2a07912333a775bf4af18cd04e7a (diff)
nir/lower_tex: add lowering for texture gradient on cube maps
This is ported from the Intel lowering pass that we use with GLSL IR. The NIR pass only handles cube maps, not shadow samplers, which are also lowered for gen < 8 on Intel hardware. We will add support for that in a later patch, at which point we should be able to remove the GLSL IR lowering pass. v2: - added a helper to retrieve ddx/ddy parameters (Ken) - No need to make size.z=1.0, we are only using component x anyway (Iago) v3: - Get rid of the ddx/ddy helper and use nir_tex_instr_src_index instead (Ken, Eric) v4: - When emitting the textureLod operation, copy all texture parameters from the original textureGrad() (except for ddx/ddy) using a loop - Add a 'continue' statement if the lowering makes progress because it replaces the original texture instruction Reviewed-by: Kenneth Graunke <kenneth@whitecape.org> (v3)
-rw-r--r--src/compiler/nir/nir.h5
-rw-r--r--src/compiler/nir/nir_lower_tex.c208
2 files changed, 213 insertions, 0 deletions
diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h
index 3040cbd2c3..826410d5ac 100644
--- a/src/compiler/nir/nir.h
+++ b/src/compiler/nir/nir.h
@@ -2394,6 +2394,11 @@ typedef struct nir_lower_tex_options {
* of the texture are lowered to linear.
*/
unsigned lower_srgb;
+
+ /**
+ * If true, lower nir_texop_txd on cube maps with nir_texop_txl.
+ */
+ bool lower_txd_cube_map;
} nir_lower_tex_options;
bool nir_lower_tex(nir_shader *shader,
diff --git a/src/compiler/nir/nir_lower_tex.c b/src/compiler/nir/nir_lower_tex.c
index 08cb6681ac..11773cbee5 100644
--- a/src/compiler/nir/nir_lower_tex.c
+++ b/src/compiler/nir/nir_lower_tex.c
@@ -304,6 +304,207 @@ lower_yx_xuxv_external(nir_builder *b, nir_tex_instr *tex)
nir_channel(b, xuxv, 3));
}
+/*
+ * Emits a textureLod operation used to replace an existing
+ * textureGrad instruction.
+ */
+static void
+replace_gradient_with_lod(nir_builder *b, nir_ssa_def *lod, nir_tex_instr *tex)
+{
+ /* We are going to emit a textureLod() with the same parameters except that
+ * we replace ddx/ddy with lod.
+ */
+ int num_srcs = tex->num_srcs - 1;
+ nir_tex_instr *txl = nir_tex_instr_create(b->shader, num_srcs);
+
+ txl->op = nir_texop_txl;
+ txl->sampler_dim = tex->sampler_dim;
+ txl->texture_index = tex->texture_index;
+ txl->dest_type = tex->dest_type;
+ txl->is_array = tex->is_array;
+ txl->is_shadow = tex->is_shadow;
+ txl->is_new_style_shadow = tex->is_new_style_shadow;
+ txl->sampler_index = tex->sampler_index;
+ txl->texture = (nir_deref_var *)
+ nir_copy_deref(txl, &tex->texture->deref);
+ txl->sampler = (nir_deref_var *)
+ nir_copy_deref(txl, &tex->sampler->deref);
+ txl->coord_components = tex->coord_components;
+
+ nir_ssa_dest_init(&txl->instr, &txl->dest, 4, 32, NULL);
+
+ int src_num = 0;
+ for (int i = 0; i < tex->num_srcs; i++) {
+ if (tex->src[i].src_type == nir_tex_src_ddx ||
+ tex->src[i].src_type == nir_tex_src_ddy)
+ continue;
+ nir_src_copy(&txl->src[src_num].src, &tex->src[i].src, txl);
+ txl->src[src_num].src_type = tex->src[i].src_type;
+ src_num++;
+ }
+
+ txl->src[src_num].src = nir_src_for_ssa(lod);
+ txl->src[src_num].src_type = nir_tex_src_lod;
+ src_num++;
+
+ assert(src_num == num_srcs);
+
+ nir_ssa_dest_init(&txl->instr, &txl->dest, 4, 32, NULL);
+ nir_builder_instr_insert(b, &txl->instr);
+
+ nir_ssa_def_rewrite_uses(&tex->dest.ssa, nir_src_for_ssa(&txl->dest.ssa));
+
+ nir_instr_remove(&tex->instr);
+}
+
+static void
+lower_gradient_cube_map(nir_builder *b, nir_tex_instr *tex)
+{
+ assert(tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE);
+ assert(tex->op == nir_texop_txd);
+ assert(tex->dest.is_ssa);
+
+ /* Use textureSize() to get the width and height of LOD 0 */
+ nir_ssa_def *size = get_texture_size(b, tex);
+
+ /* Cubemap texture lookups first generate a texture coordinate normalized
+ * to [-1, 1] on the appropiate face. The appropiate face is determined
+ * by which component has largest magnitude and its sign. The texture
+ * coordinate is the quotient of the remaining texture coordinates against
+ * that absolute value of the component of largest magnitude. This
+ * division requires that the computing of the derivative of the texel
+ * coordinate must use the quotient rule. The high level GLSL code is as
+ * follows:
+ *
+ * Step 1: selection
+ *
+ * vec3 abs_p, Q, dQdx, dQdy;
+ * abs_p = abs(ir->coordinate);
+ * if (abs_p.x >= max(abs_p.y, abs_p.z)) {
+ * Q = ir->coordinate.yzx;
+ * dQdx = ir->lod_info.grad.dPdx.yzx;
+ * dQdy = ir->lod_info.grad.dPdy.yzx;
+ * }
+ * if (abs_p.y >= max(abs_p.x, abs_p.z)) {
+ * Q = ir->coordinate.xzy;
+ * dQdx = ir->lod_info.grad.dPdx.xzy;
+ * dQdy = ir->lod_info.grad.dPdy.xzy;
+ * }
+ * if (abs_p.z >= max(abs_p.x, abs_p.y)) {
+ * Q = ir->coordinate;
+ * dQdx = ir->lod_info.grad.dPdx;
+ * dQdy = ir->lod_info.grad.dPdy;
+ * }
+ *
+ * Step 2: use quotient rule to compute derivative. The normalized to
+ * [-1, 1] texel coordinate is given by Q.xy / (sign(Q.z) * Q.z). We are
+ * only concerned with the magnitudes of the derivatives whose values are
+ * not affected by the sign. We drop the sign from the computation.
+ *
+ * vec2 dx, dy;
+ * float recip;
+ *
+ * recip = 1.0 / Q.z;
+ * dx = recip * ( dQdx.xy - Q.xy * (dQdx.z * recip) );
+ * dy = recip * ( dQdy.xy - Q.xy * (dQdy.z * recip) );
+ *
+ * Step 3: compute LOD. At this point we have the derivatives of the
+ * texture coordinates normalized to [-1,1]. We take the LOD to be
+ * result = log2(max(sqrt(dot(dx, dx)), sqrt(dy, dy)) * 0.5 * L)
+ * = -1.0 + log2(max(sqrt(dot(dx, dx)), sqrt(dy, dy)) * L)
+ * = -1.0 + log2(sqrt(max(dot(dx, dx), dot(dy,dy))) * L)
+ * = -1.0 + log2(sqrt(L * L * max(dot(dx, dx), dot(dy,dy))))
+ * = -1.0 + 0.5 * log2(L * L * max(dot(dx, dx), dot(dy,dy)))
+ * where L is the dimension of the cubemap. The code is:
+ *
+ * float M, result;
+ * M = max(dot(dx, dx), dot(dy, dy));
+ * L = textureSize(sampler, 0).x;
+ * result = -1.0 + 0.5 * log2(L * L * M);
+ */
+
+ /* coordinate */
+ nir_ssa_def *p =
+ tex->src[nir_tex_instr_src_index(tex, nir_tex_src_coord)].src.ssa;
+
+ /* unmodified dPdx, dPdy values */
+ nir_ssa_def *dPdx =
+ tex->src[nir_tex_instr_src_index(tex, nir_tex_src_ddx)].src.ssa;
+ nir_ssa_def *dPdy =
+ tex->src[nir_tex_instr_src_index(tex, nir_tex_src_ddy)].src.ssa;
+
+ nir_ssa_def *abs_p = nir_fabs(b, p);
+ nir_ssa_def *abs_p_x = nir_channel(b, abs_p, 0);
+ nir_ssa_def *abs_p_y = nir_channel(b, abs_p, 1);
+ nir_ssa_def *abs_p_z = nir_channel(b, abs_p, 2);
+
+ /* 1. compute selector */
+ nir_ssa_def *Q, *dQdx, *dQdy;
+
+ nir_ssa_def *cond_z = nir_fge(b, abs_p_z, nir_fmax(b, abs_p_x, abs_p_y));
+ nir_ssa_def *cond_y = nir_fge(b, abs_p_y, nir_fmax(b, abs_p_x, abs_p_z));
+
+ unsigned yzx[4] = { 1, 2, 0, 0 };
+ unsigned xzy[4] = { 0, 2, 1, 0 };
+
+ Q = nir_bcsel(b, cond_z,
+ p,
+ nir_bcsel(b, cond_y,
+ nir_swizzle(b, p, xzy, 3, false),
+ nir_swizzle(b, p, yzx, 3, false)));
+
+ dQdx = nir_bcsel(b, cond_z,
+ dPdx,
+ nir_bcsel(b, cond_y,
+ nir_swizzle(b, dPdx, xzy, 3, false),
+ nir_swizzle(b, dPdx, yzx, 3, false)));
+
+ dQdy = nir_bcsel(b, cond_z,
+ dPdy,
+ nir_bcsel(b, cond_y,
+ nir_swizzle(b, dPdy, xzy, 3, false),
+ nir_swizzle(b, dPdy, yzx, 3, false)));
+
+ /* 2. quotient rule */
+
+ /* tmp = Q.xy * recip;
+ * dx = recip * ( dQdx.xy - (tmp * dQdx.z) );
+ * dy = recip * ( dQdy.xy - (tmp * dQdy.z) );
+ */
+ nir_ssa_def *rcp_Q_z = nir_frcp(b, nir_channel(b, Q, 2));
+
+ unsigned xy[4] = { 0, 1, 0, 0 };
+ nir_ssa_def *Q_xy = nir_swizzle(b, Q, xy, 2, false);
+ nir_ssa_def *tmp = nir_fmul(b, Q_xy, rcp_Q_z);
+
+ nir_ssa_def *dQdx_xy = nir_swizzle(b, dQdx, xy, 2, false);
+ nir_ssa_def *dQdx_z = nir_channel(b, dQdx, 2);
+ nir_ssa_def *dx =
+ nir_fmul(b, rcp_Q_z, nir_fsub(b, dQdx_xy, nir_fmul(b, tmp, dQdx_z)));
+
+ nir_ssa_def *dQdy_xy = nir_swizzle(b, dQdy, xy, 2, false);
+ nir_ssa_def *dQdy_z = nir_channel(b, dQdy, 2);
+ nir_ssa_def *dy =
+ nir_fmul(b, rcp_Q_z, nir_fsub(b, dQdy_xy, nir_fmul(b, tmp, dQdy_z)));
+
+ /* M = max(dot(dx, dx), dot(dy, dy)); */
+ nir_ssa_def *M = nir_fmax(b, nir_fdot(b, dx, dx), nir_fdot(b, dy, dy));
+
+ /* size has textureSize() of LOD 0 */
+ nir_ssa_def *L = nir_channel(b, size, 0);
+
+ /* lod = -1.0 + 0.5 * log2(L * L * M); */
+ nir_ssa_def *lod =
+ nir_fadd(b,
+ nir_imm_float(b, -1.0f),
+ nir_fmul(b,
+ nir_imm_float(b, 0.5f),
+ nir_flog2(b, nir_fmul(b, L, nir_fmul(b, L, M)))));
+
+ /* 3. Replace the gradient instruction with an equivalent lod instruction */
+ replace_gradient_with_lod(b, lod, tex);
+}
+
static void
saturate_src(nir_builder *b, nir_tex_instr *tex, unsigned sat_mask)
{
@@ -529,6 +730,13 @@ nir_lower_tex_block(nir_block *block, nir_builder *b,
linearize_srgb_result(b, tex);
progress = true;
}
+
+ if (tex->op == nir_texop_txd && options->lower_txd_cube_map &&
+ tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE) {
+ lower_gradient_cube_map(b, tex);
+ progress = true;
+ continue;
+ }
}
return progress;