From 3745c38425b3e1da5c94a5f900eb5fdc44da9439 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 18 Sep 2015 10:44:27 -0400 Subject: [PATCH] nir/lower_tex: add support to clamp texture coords Some hardware needs to clamp texture coordinates to [0.0, 1.0] in the shader to emulate GL_CLAMP. This is added to lower_tex_proj since, in the case of projected coords, the clamping needs to happen *after* projection. v2: comments/suggestions from Ilia and Eric, use txs to get texture size and clamp RECT textures to their dimensions rather than [0.0, 1.0] to avoid having to lower RECT textures to 2D. Signed-off-by: Rob Clark Reviewed-by: Kenneth Graunke --- src/glsl/nir/nir.h | 18 ++++++++ src/glsl/nir/nir_lower_tex.c | 86 +++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/glsl/nir/nir.h b/src/glsl/nir/nir.h index 3c908b9f295..255d45585db 100644 --- a/src/glsl/nir/nir.h +++ b/src/glsl/nir/nir.h @@ -1850,6 +1850,24 @@ typedef struct nir_lower_tex_options { * texture dims to normalize. */ bool lower_rect; + + /** + * To emulate certain texture wrap modes, this can be used + * to saturate the specified tex coord to [0.0, 1.0]. The + * bits are according to sampler #, ie. if, for example: + * + * (conf->saturate_s & (1 << n)) + * + * is true, then the s coord for sampler n is saturated. + * + * Note that clamping must happen *after* projector lowering + * so any projected texture sample instruction with a clamped + * coordinate gets automatically lowered, regardless of the + * 'lower_txp' setting. + */ + unsigned saturate_s; + unsigned saturate_t; + unsigned saturate_r; } nir_lower_tex_options; void nir_lower_tex(nir_shader *shader, diff --git a/src/glsl/nir/nir_lower_tex.c b/src/glsl/nir/nir_lower_tex.c index 63f51bcbdc5..e2f095a5532 100644 --- a/src/glsl/nir/nir_lower_tex.c +++ b/src/glsl/nir/nir_lower_tex.c @@ -29,6 +29,10 @@ * asking the texture operation to do so. * + lowering RECT: converts the un-normalized RECT texture coordinates * to normalized coordinates with txs plus ALU instructions + * + saturate s/t/r coords: to emulate certain texture clamp/wrap modes, + * inserts instructions to clamp specified coordinates to [0.0, 1.0]. + * Note that this automatically triggers texture projector lowering if + * needed, since clamping must happen after projector lowering. */ #include "nir.h" @@ -161,6 +165,70 @@ lower_rect(nir_builder *b, nir_tex_instr *tex) tex->sampler_dim = GLSL_SAMPLER_DIM_2D; } +static void +saturate_src(nir_builder *b, nir_tex_instr *tex, unsigned sat_mask) +{ + b->cursor = nir_before_instr(&tex->instr); + + /* Walk through the sources saturating the requested arguments. */ + for (unsigned i = 0; i < tex->num_srcs; i++) { + if (tex->src[i].src_type != nir_tex_src_coord) + continue; + + nir_ssa_def *src = + nir_ssa_for_src(b, tex->src[i].src, tex->coord_components); + + /* split src into components: */ + nir_ssa_def *comp[4]; + + for (unsigned j = 0; j < tex->coord_components; j++) + comp[j] = nir_channel(b, src, j); + + /* clamp requested components, array index does not get clamped: */ + unsigned ncomp = tex->coord_components; + if (tex->is_array) + ncomp--; + + for (unsigned j = 0; j < ncomp; j++) { + if ((1 << j) & sat_mask) { + if (tex->sampler_dim == GLSL_SAMPLER_DIM_RECT) { + /* non-normalized texture coords, so clamp to texture + * size rather than [0.0, 1.0] + */ + nir_ssa_def *txs = get_texture_size(b, tex); + comp[j] = nir_fmax(b, comp[j], nir_imm_float(b, 0.0)); + comp[j] = nir_fmin(b, comp[j], nir_channel(b, txs, j)); + } else { + comp[j] = nir_fsat(b, comp[j]); + } + } + } + + /* and move the result back into a single vecN: */ + switch (tex->coord_components) { + case 4: + src = nir_vec4(b, comp[0], comp[1], comp[2], comp[3]); + break; + case 3: + src = nir_vec3(b, comp[0], comp[1], comp[2]); + break; + case 2: + src = nir_vec2(b, comp[0], comp[1]); + break; + case 1: + src = comp[0]; + break; + default: + unreachable("bad texture coord count"); + break; + } + + nir_instr_rewrite_src(&tex->instr, + &tex->src[i].src, + nir_src_for_ssa(src)); + } +} + static bool nir_lower_tex_block(nir_block *block, void *void_state) { @@ -174,12 +242,28 @@ nir_lower_tex_block(nir_block *block, void *void_state) nir_tex_instr *tex = nir_instr_as_tex(instr); bool lower_txp = !!(state->options->lower_txp & (1 << tex->sampler_dim)); - if (lower_txp) + /* mask of src coords to saturate (clamp): */ + unsigned sat_mask = 0; + + if ((1 << tex->sampler_index) & state->options->saturate_r) + sat_mask |= (1 << 2); /* .z */ + if ((1 << tex->sampler_index) & state->options->saturate_t) + sat_mask |= (1 << 1); /* .y */ + if ((1 << tex->sampler_index) & state->options->saturate_s) + sat_mask |= (1 << 0); /* .x */ + + /* If we are clamping any coords, we must lower projector first + * as clamping happens *after* projection: + */ + if (lower_txp || sat_mask) project_src(b, tex); if ((tex->sampler_dim == GLSL_SAMPLER_DIM_RECT) && state->options->lower_rect) lower_rect(b, tex); + + if (sat_mask) + saturate_src(b, tex, sat_mask); } return true; -- 2.30.2