[Mesa-dev] [PATCH 5/6] nir/lower_tex: add support to clamp texture coords
Rob Clark
robdclark at gmail.com
Fri Sep 18 09:24:37 PDT 2015
On Fri, Sep 18, 2015 at 11:55 AM, Kenneth Graunke <kenneth at whitecape.org> wrote:
> On Friday, September 18, 2015 10:55:08 AM Rob Clark wrote:
>> From: Rob Clark <robclark at freedesktop.org>
>>
>> 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 <robclark at freedesktop.org>
>> ---
>> 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 3c908b9..255d455 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 a71a5c5..7c7a077 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"
>> @@ -164,6 +168,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]);
>> + }
>> + }
>> + }
>
> You might be able to do this with vector operations instead of
> scalarizing...but I'm not sure if it would actually be better. *shrug*
the problem is that sat_mask might not have all bits set.. I suppose
with a bit more logic I could handle the case that all components are
clamped, but I think everyone is converting to scalar (and then
possibly converting back), so I wasn't really thinking that it was
worth the extra logic..
>> +
>> + /* 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;
>> + }
>
> A nir_vec() helper that takes the array and num_components would make a
> nice addition to nir_builder. You could then do src = nir_vec4(b, comp,
> tex->coord_components).
hmm, looks like we could use that in nir_lower_load_const_to_scalar.c
too.. would be nice if we could get a helper that would deal w/ the
other switch(num_components) (where we re-assemble projected and
unprojected), and maybe the vaguely similar thing in
nir_normalize_cubemap_coords.c (re-assembling normalized and
orig_coord).. although not quite sure what that would look like.
I guess I'll put together a patch on top of this which handles the
first two cases at least..
BR,
-R
> Without any changes, this is:
> Reviewed-by: Kenneth Graunke <kenneth at whitecape.org>
>
>> +
>> + 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)
>> {
>> @@ -177,12 +245,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;
>>
More information about the mesa-dev
mailing list