[Mesa-dev] [PATCH] softpipe: implement seamless cubemap support.

Roland Scheidegger sroland at vmware.com
Tue Dec 11 06:20:24 PST 2012


Am 11.12.2012 10:52, schrieb Dave Airlie:
> This adds seamless sampling for cubemap boundaries if requested.
> 
> The corner case averaging is messy but seems like it should be spec
> compliant.
> 
> The face direction stuff is also a bit messy, I've no idea if that could
> or should be simpler, or even if all my directions are fully correct!
> 
> Signed-off-by: Dave Airlie <airlied at redhat.com>
> ---
>  src/gallium/drivers/softpipe/sp_screen.c     |   2 +-
>  src/gallium/drivers/softpipe/sp_tex_sample.c | 143 +++++++++++++++++++++++++--
>  2 files changed, 135 insertions(+), 10 deletions(-)
> 
> diff --git a/src/gallium/drivers/softpipe/sp_screen.c b/src/gallium/drivers/softpipe/sp_screen.c
> index 909fa1c..7ca259e 100644
> --- a/src/gallium/drivers/softpipe/sp_screen.c
> +++ b/src/gallium/drivers/softpipe/sp_screen.c
> @@ -127,7 +127,7 @@ softpipe_get_param(struct pipe_screen *screen, enum pipe_cap param)
>        return 1;
>     case PIPE_CAP_SEAMLESS_CUBE_MAP:
>     case PIPE_CAP_SEAMLESS_CUBE_MAP_PER_TEXTURE:
> -      return 0;
> +      return 1;
>     case PIPE_CAP_SCALED_RESOLVE:
>        return 0;
>     case PIPE_CAP_MAX_TEXTURE_ARRAY_LAYERS:
> diff --git a/src/gallium/drivers/softpipe/sp_tex_sample.c b/src/gallium/drivers/softpipe/sp_tex_sample.c
> index 7558ef1..9500a03 100644
> --- a/src/gallium/drivers/softpipe/sp_tex_sample.c
> +++ b/src/gallium/drivers/softpipe/sp_tex_sample.c
> @@ -607,6 +607,100 @@ get_texel_2d(const struct sp_sampler_variant *samp,
>     }
>  }
>  
> +static const unsigned face_array[PIPE_TEX_FACE_MAX][4] = {
> +   /* pos X first then neg X is Z different, Y the same */
> +   /* PIPE_TEX_FACE_POS_X,*/
> +   { PIPE_TEX_FACE_POS_Z, PIPE_TEX_FACE_NEG_Z,
> +     PIPE_TEX_FACE_NEG_Y, PIPE_TEX_FACE_POS_Y },
> +   /* PIPE_TEX_FACE_NEG_X */
> +   { PIPE_TEX_FACE_NEG_Z, PIPE_TEX_FACE_POS_Z,
> +     PIPE_TEX_FACE_NEG_Y, PIPE_TEX_FACE_POS_Y },
> +
> +   /* pos Y first then neg Y is X different, X the same */
> +   /* PIPE_TEX_FACE_POS_Y */
> +   { PIPE_TEX_FACE_NEG_X, PIPE_TEX_FACE_POS_X,
> +     PIPE_TEX_FACE_POS_Z, PIPE_TEX_FACE_NEG_Z },
> +
> +   /* PIPE_TEX_FACE_NEG_Y */
> +   { PIPE_TEX_FACE_NEG_X, PIPE_TEX_FACE_POS_X,
> +     PIPE_TEX_FACE_NEG_Z, PIPE_TEX_FACE_POS_Z },
> +
> +   /* pos Z first then neg Y is X different, X the same */
> +   /* PIPE_TEX_FACE_POS_Z */
> +   { PIPE_TEX_FACE_NEG_X, PIPE_TEX_FACE_POS_X,
> +     PIPE_TEX_FACE_NEG_Y, PIPE_TEX_FACE_POS_Y },
> +
> +   /* PIPE_TEX_FACE_NEG_Z */
> +   { PIPE_TEX_FACE_POS_X, PIPE_TEX_FACE_NEG_X,
> +     PIPE_TEX_FACE_NEG_Y, PIPE_TEX_FACE_POS_Y }
> +};
> +
> +static INLINE unsigned
> +get_next_face(unsigned face, int x, int y)
> +{
> +   int idx = 0;
> +
> +   if (x == 0 && y == 0)
> +      return face;
> +   if (x == -1) idx = 0;
> +   else if (x == 1) idx = 1;
> +   else if (y == -1) idx = 2;
> +   else if (y == 1) idx = 3;
> +
> +   return face_array[face][idx];
> +}
> +
> +static INLINE const float *
> +get_texel_cube_seamless(const struct sp_sampler_variant *samp,
> +                        union tex_tile_address addr, int x, int y,
> +                        float *corner)
> +{
> +   const struct pipe_resource *texture = samp->view->texture;
> +   unsigned level = addr.bits.level;
> +   unsigned face = addr.bits.face;
> +   int new_x, new_y;
> +   int max_x, max_y;
> +   int c;
> +
> +   max_x = (int) u_minify(texture->width0, level);
> +   max_y = (int) u_minify(texture->height0, level);
> +   new_x = x;
> +   new_y = y;
> +
> +   /* the corner case */
> +   if ((x < 0 || x >= max_x) &&
> +       (y < 0 || y >= max_y)) {
> +      const float *c1, *c2, *c3;
> +      int fx = x < 0 ? 0 : max_x - 1;
> +      int fy = y < 0 ? 0 : max_y - 1;
> +      c1 = get_texel_2d_no_border( samp, addr, fx, fy);
> +      addr.bits.face = get_next_face(face, (x < 0) ? -1 : 1, 0);
> +      c2 = get_texel_2d_no_border( samp, addr, (x < 0) ? max_x - 1 : 0, fy);
> +      addr.bits.face = get_next_face(face, 0, (y < 0) ? -1 : 1);
> +      c3 = get_texel_2d_no_border( samp, addr, fx, (y < 0) ?  max_y - 1 : 0);
> +      for (c = 0; c < TGSI_QUAD_SIZE; c++)
> +         corner[c] = CLAMP((c1[c] + c2[c] + c3[c]), 0.0F, 1.0F) / 3;
> +
> +      return corner;
I wonder if the recommended corner handling is worth it. As far as I can
tell the spec would allow you to simply drop the special handling and it
would still be conformant (same for dx10). Though since softpipe is more
about correctness rather than performance, I guess this is ok (and the
code would very rarely get hit anyway).

> +   }
> +   /* change the face */
> +   if (x < 0) {
> +      new_x = max_x - 1;
> +      face = get_next_face(face, -1, 0);
> +   } else if (x >= max_x) {
> +      new_x = 0;
> +      face = get_next_face(face, 1, 0);
> +   } else if (y < 0) {
> +      new_y = max_y - 1;
> +      face = get_next_face(face, 0, -1);
> +   } else if (y >= max_y) {
> +      new_y = 0;
> +      face = get_next_face(face, 0, 1);
> +   }
> +
> +   addr.bits.face = face;
> +   return get_texel_2d_no_border( samp, addr, new_x, new_y );
> +}
>  
>  /* Gather a quad of adjacent texels within a tile:
>   */
> @@ -1121,6 +1215,7 @@ img_filter_cube_nearest(struct tgsi_sampler *tgsi_sampler,
>     union tex_tile_address addr;
>     const float *out;
>     int c;
> +   float corner0[TGSI_QUAD_SIZE];
>  
>     width = u_minify(texture->width0, level);
>     height = u_minify(texture->height0, level);
> @@ -1131,10 +1226,23 @@ img_filter_cube_nearest(struct tgsi_sampler *tgsi_sampler,
>     addr.value = 0;
>     addr.bits.level = level;
>  
> -   samp->nearest_texcoord_s(s, width, &x);
> -   samp->nearest_texcoord_t(t, height, &y);
> +   /*
> +    * If NEAREST filtering is done within a miplevel, always apply wrap
> +    * mode CLAMP_TO_EDGE.
> +    */
> +   if (samp->sampler->seamless_cube_map) {
> +      wrap_nearest_clamp_to_edge(s, width, &x);
> +      wrap_nearest_clamp_to_edge(t, height, &y);
> +   } else {
> +      samp->nearest_texcoord_s(s, width, &x);
> +      samp->nearest_texcoord_t(t, height, &y);
> +   }
>  
> -   out = get_texel_2d(samp, face(addr, face_id), x, y);
> +   if (samp->sampler->seamless_cube_map) {
> +      out = get_texel_cube_seamless(samp, face(addr, face_id), x, y, corner0);
> +   } else {
> +      out = get_texel_2d(samp, face(addr, face_id), x, y);
> +   }
I think using texel_cube_seamless here is unnecessary. After you've
forced clamp to edge there's no way you can end up in a different face,
you could directly use get_texel_2d_no_border. But either way it should
be correct.

>     for (c = 0; c < TGSI_QUAD_SIZE; c++)
>        rgba[TGSI_NUM_CHANNELS*c] = out[c];
>  
> @@ -1403,6 +1511,7 @@ img_filter_cube_linear(struct tgsi_sampler *tgsi_sampler,
>     float xw, yw; /* weights */
>     union tex_tile_address addr, addrj;
>     const float *tx0, *tx1, *tx2, *tx3;
> +   float corner0[TGSI_QUAD_SIZE], corner1[TGSI_QUAD_SIZE], corner2[TGSI_QUAD_SIZE], corner3[TGSI_QUAD_SIZE];
>     int c;
>  
>     width = u_minify(texture->width0, level);
> @@ -1414,15 +1523,31 @@ img_filter_cube_linear(struct tgsi_sampler *tgsi_sampler,
>     addr.value = 0;
>     addr.bits.level = level;
>  
> -   samp->linear_texcoord_s(s, width,  &x0, &x1, &xw);
> -   samp->linear_texcoord_t(t, height, &y0, &y1, &yw);
> +   /* For seamless,
> +    * if LINEAR filtering is done within a miplevel, always apply wrap mode
> +    * CLAMP_TO_BORDER.
> +    */
> +   if (samp->sampler->seamless_cube_map) {
> +      wrap_linear_clamp_to_border(s, width, &x0, &x1, &xw);
> +      wrap_linear_clamp_to_border(t, height, &y0, &y1, &yw);
> +   } else {
> +      samp->linear_texcoord_s(s, width,  &x0, &x1, &xw);
> +      samp->linear_texcoord_t(t, height, &y0, &y1, &yw);
> +   }
>  
>     addrj = face(addr, face_id);
> -   tx0 = get_texel_2d(samp, addrj, x0, y0);
> -   tx1 = get_texel_2d(samp, addrj, x1, y0);
> -   tx2 = get_texel_2d(samp, addrj, x0, y1);
> -   tx3 = get_texel_2d(samp, addrj, x1, y1);
>  
> +   if (samp->sampler->seamless_cube_map) {
> +      tx0 = get_texel_cube_seamless(samp, addrj, x0, y0, corner0);
> +      tx1 = get_texel_cube_seamless(samp, addrj, x1, y0, corner1);
> +      tx2 = get_texel_cube_seamless(samp, addrj, x0, y1, corner2);
> +      tx3 = get_texel_cube_seamless(samp, addrj, x1, y1, corner3);
> +   } else {
> +      tx0 = get_texel_2d(samp, addrj, x0, y0);
> +      tx1 = get_texel_2d(samp, addrj, x1, y0);
> +      tx2 = get_texel_2d(samp, addrj, x0, y1);
> +      tx3 = get_texel_2d(samp, addrj, x1, y1);
> +   }
>     /* interpolate R, G, B, A */
>     for (c = 0; c < TGSI_QUAD_SIZE; c++)
>        rgba[TGSI_NUM_CHANNELS*c] = lerp_2d(xw, yw,
> 

This is neat, I shudder thinking about implementing it for llvmpipe...
I didn't check the next face math, but
Reviewed-by: Roland Scheidegger <sroland at vmware.com>


More information about the mesa-dev mailing list