Mesa (master): r300g: add support for 3D NPOT textures without mipmapping

Marek Olšák mareko at kemper.freedesktop.org
Tue Sep 28 03:35:19 UTC 2010


Module: Mesa
Branch: master
Commit: 13359e6a4b732335cdd8da48276960d0b176ffe3
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=13359e6a4b732335cdd8da48276960d0b176ffe3

Author: Marek Olšák <maraeo at gmail.com>
Date:   Sun Aug 29 03:48:42 2010 +0200

r300g: add support for 3D NPOT textures without mipmapping

The driver actually creates a 3D texture aligned to POT and does all
the magic with texture coordinates in the fragment shader. It first
emulates REPEAT and MIRRORED wrap modes in the fragment shader to get
the coordinates into the range [0, 1]. (already done for 2D NPOT)
Then it scales them to get the coordinates of the NPOT subtexture.

NPOT textures are now less of a lie and we can at least display
something meaningful even for the 3D ones.

Supported wrap modes:
- REPEAT
- MIRRORED_REPEAT
- CLAMP_TO_EDGE (NEAREST filtering only)
- MIRROR_CLAMP_TO_EDGE (NEAREST filtering only)
- The behavior of other CLAMP modes is undefined on borders, but they usually
  give results very close to CLAMP_TO_EDGE with mirroring working perfectly.

This fixes:
- piglit/fbo-3d
- piglit/tex3d-npot

---

 src/gallium/drivers/r300/r300_context.h            |    7 ++++
 src/gallium/drivers/r300/r300_emit.c               |   16 +++++++--
 src/gallium/drivers/r300/r300_fs.c                 |    3 ++
 src/gallium/drivers/r300/r300_texture.c            |   15 +++-----
 src/gallium/drivers/r300/r300_texture_desc.c       |   35 +++++++++++++-------
 src/mesa/drivers/dri/r300/compiler/radeon_code.h   |    9 ++++-
 .../drivers/dri/r300/compiler/radeon_program_tex.c |   32 +++++++++++++++++-
 7 files changed, 90 insertions(+), 27 deletions(-)

diff --git a/src/gallium/drivers/r300/r300_context.h b/src/gallium/drivers/r300/r300_context.h
index 1927370..b59bc00 100644
--- a/src/gallium/drivers/r300/r300_context.h
+++ b/src/gallium/drivers/r300/r300_context.h
@@ -336,6 +336,13 @@ struct r300_texture_desc {
     /* Parent class. */
     struct u_resource b;
 
+    /* Width, height, and depth.
+     * Most of the time, these are equal to pipe_texture::width0, height0,
+     * and depth0. However, NPOT 3D textures must have dimensions aligned
+     * to POT, and this is the only case when these variables differ from
+     * pipe_texture. */
+    unsigned width0, height0, depth0;
+
     /* Buffer tiling.
      * Macrotiling is specified per-level because small mipmaps cannot
      * be macrotiled. */
diff --git a/src/gallium/drivers/r300/r300_emit.c b/src/gallium/drivers/r300/r300_emit.c
index db783ff..3a1085d 100644
--- a/src/gallium/drivers/r300/r300_emit.c
+++ b/src/gallium/drivers/r300/r300_emit.c
@@ -89,7 +89,7 @@ static const float * get_rc_constant_state(
 {
     struct r300_textures_state* texstate = r300->textures_state.state;
     static float vec[4] = { 0.0, 0.0, 0.0, 1.0 };
-    struct pipe_resource *tex;
+    struct r300_texture *tex;
 
     assert(constant->Type == RC_CONSTANT_STATE);
 
@@ -97,9 +97,17 @@ static const float * get_rc_constant_state(
         /* Factor for converting rectangle coords to
          * normalized coords. Should only show up on non-r500. */
         case RC_STATE_R300_TEXRECT_FACTOR:
-            tex = texstate->sampler_views[constant->u.State[1]]->base.texture;
-            vec[0] = 1.0 / tex->width0;
-            vec[1] = 1.0 / tex->height0;
+            tex = r300_texture(texstate->sampler_views[constant->u.State[1]]->base.texture);
+            vec[0] = 1.0 / tex->desc.width0;
+            vec[1] = 1.0 / tex->desc.height0;
+            break;
+
+        case RC_STATE_R300_TEXSCALE_FACTOR:
+            tex = r300_texture(texstate->sampler_views[constant->u.State[1]]->base.texture);
+            /* Add a small number to the texture size to work around rounding errors in hw. */
+            vec[0] = tex->desc.b.b.width0  / (tex->desc.width0  + 0.001f);
+            vec[1] = tex->desc.b.b.height0 / (tex->desc.height0 + 0.001f);
+            vec[2] = tex->desc.b.b.depth0  / (tex->desc.depth0  + 0.001f);
             break;
 
         case RC_STATE_R300_VIEWPORT_SCALE:
diff --git a/src/gallium/drivers/r300/r300_fs.c b/src/gallium/drivers/r300/r300_fs.c
index 3f89987..d9d4a93 100644
--- a/src/gallium/drivers/r300/r300_fs.c
+++ b/src/gallium/drivers/r300/r300_fs.c
@@ -200,6 +200,9 @@ static void get_external_state(
             default:
                 state->unit[i].wrap_mode = RC_WRAP_NONE;
             }
+
+            if (t->desc.b.b.target == PIPE_TEXTURE_3D)
+                state->unit[i].clamp_and_scale_before_fetch = TRUE;
         }
     }
 }
diff --git a/src/gallium/drivers/r300/r300_texture.c b/src/gallium/drivers/r300/r300_texture.c
index 34105aa..a7911c6 100644
--- a/src/gallium/drivers/r300/r300_texture.c
+++ b/src/gallium/drivers/r300/r300_texture.c
@@ -556,18 +556,15 @@ void r300_texture_setup_format_state(struct r300_screen *screen,
     out->tile_config = 0;
 
     /* Set sampler state. */
-    out->format0 = R300_TX_WIDTH((u_minify(pt->width0, level) - 1) & 0x7ff) |
-                   R300_TX_HEIGHT((u_minify(pt->height0, level) - 1) & 0x7ff);
+    out->format0 =
+        R300_TX_WIDTH((u_minify(desc->width0, level) - 1) & 0x7ff) |
+        R300_TX_HEIGHT((u_minify(desc->height0, level) - 1) & 0x7ff) |
+        R300_TX_DEPTH(util_logbase2(u_minify(desc->depth0, level)) & 0xf);
 
     if (desc->uses_stride_addressing) {
         /* rectangles love this */
         out->format0 |= R300_TX_PITCH_EN;
         out->format2 = (desc->stride_in_pixels[level] - 1) & 0x1fff;
-    } else {
-        /* Power of two textures (3D, mipmaps, and no pitch),
-         * also NPOT textures with a width being POT. */
-        out->format0 |=
-            R300_TX_DEPTH(util_logbase2(u_minify(pt->depth0, level)) & 0xf);
     }
 
     if (pt->target == PIPE_TEXTURE_CUBE) {
@@ -580,10 +577,10 @@ void r300_texture_setup_format_state(struct r300_screen *screen,
     /* large textures on r500 */
     if (is_r500)
     {
-        if (pt->width0 > 2048) {
+        if (desc->width0 > 2048) {
             out->format2 |= R500_TXWIDTH_BIT11;
         }
-        if (pt->height0 > 2048) {
+        if (desc->height0 > 2048) {
             out->format2 |= R500_TXHEIGHT_BIT11;
         }
     }
diff --git a/src/gallium/drivers/r300/r300_texture_desc.c b/src/gallium/drivers/r300/r300_texture_desc.c
index 6a1030f..a49029e 100644
--- a/src/gallium/drivers/r300/r300_texture_desc.c
+++ b/src/gallium/drivers/r300/r300_texture_desc.c
@@ -91,9 +91,9 @@ static boolean r300_texture_macro_switch(struct r300_texture_desc *desc,
     tile = r300_get_pixel_alignment(desc->b.b.format, desc->b.b.nr_samples,
                                     desc->microtile, R300_BUFFER_TILED, dim);
     if (dim == DIM_WIDTH) {
-        texdim = u_minify(desc->b.b.width0, level);
+        texdim = u_minify(desc->width0, level);
     } else {
-        texdim = u_minify(desc->b.b.height0, level);
+        texdim = u_minify(desc->height0, level);
     }
 
     /* See TX_FILTER1_n.MACRO_SWITCH. */
@@ -124,7 +124,7 @@ static unsigned r300_texture_get_stride(struct r300_screen *screen,
         return 0;
     }
 
-    width = u_minify(desc->b.b.width0, level);
+    width = u_minify(desc->width0, level);
 
     if (util_format_is_plain(desc->b.b.format)) {
         tile_width = r300_get_pixel_alignment(desc->b.b.format,
@@ -172,7 +172,7 @@ static unsigned r300_texture_get_nblocksy(struct r300_texture_desc *desc,
 {
     unsigned height, tile_height;
 
-    height = u_minify(desc->b.b.height0, level);
+    height = u_minify(desc->height0, level);
 
     if (util_format_is_plain(desc->b.b.format)) {
         tile_height = r300_get_pixel_alignment(desc->b.b.format,
@@ -237,7 +237,7 @@ static void r300_texture_3d_fix_mipmapping(struct r300_screen *screen,
                     r300_texture_get_nblocksy(desc, i, FALSE);
         }
 
-        size *= desc->b.b.depth0;
+        size *= desc->depth0;
         desc->size_in_bytes = size;
     }
 }
@@ -292,7 +292,7 @@ static void r300_setup_miptree(struct r300_screen *screen,
         if (base->target == PIPE_TEXTURE_CUBE)
             size = layer_size * 6;
         else
-            size = layer_size * u_minify(base->depth0, i);
+            size = layer_size * u_minify(desc->depth0, i);
 
         desc->offset_in_bytes[i] = desc->size_in_bytes;
         desc->size_in_bytes = desc->offset_in_bytes[i] + size;
@@ -303,8 +303,8 @@ static void r300_setup_miptree(struct r300_screen *screen,
 
         SCREEN_DBG(screen, DBG_TEXALLOC, "r300: Texture miptree: Level %d "
                 "(%dx%dx%d px, pitch %d bytes) %d bytes total, macrotiled %s\n",
-                i, u_minify(base->width0, i), u_minify(base->height0, i),
-                u_minify(base->depth0, i), stride, desc->size_in_bytes,
+                i, u_minify(desc->width0, i), u_minify(desc->height0, i),
+                u_minify(desc->depth0, i), stride, desc->size_in_bytes,
                 desc->macrotile[i] ? "TRUE" : "FALSE");
     }
 }
@@ -313,14 +313,14 @@ static void r300_setup_flags(struct r300_texture_desc *desc)
 {
     desc->uses_stride_addressing =
         !util_is_power_of_two(desc->b.b.width0) ||
-        !util_is_power_of_two(desc->b.b.height0) ||
         (desc->stride_in_bytes_override &&
          stride_to_width(desc->b.b.format,
                          desc->stride_in_bytes_override) != desc->b.b.width0);
 
     desc->is_npot =
         desc->uses_stride_addressing ||
-        !util_is_power_of_two(desc->b.b.height0);
+        !util_is_power_of_two(desc->b.b.height0) ||
+        !util_is_power_of_two(desc->b.b.depth0);
 }
 
 static void r300_setup_cbzb_flags(struct r300_screen *rscreen,
@@ -416,9 +416,21 @@ boolean r300_texture_desc_init(struct r300_screen *rscreen,
 {
     desc->b.b = *base;
     desc->b.b.screen = &rscreen->screen;
-
     desc->stride_in_bytes_override = stride_in_bytes_override;
+    desc->width0 = base->width0;
+    desc->height0 = base->height0;
+    desc->depth0 = base->depth0;
+
+    r300_setup_flags(desc);
+
+    /* Align a 3D NPOT texture to POT. */
+    if (base->target == PIPE_TEXTURE_3D && desc->is_npot) {
+        desc->width0 = util_next_power_of_two(desc->width0);
+        desc->height0 = util_next_power_of_two(desc->height0);
+        desc->depth0 = util_next_power_of_two(desc->depth0);
+    }
 
+    /* Setup tiling. */
     if (microtile == R300_BUFFER_SELECT_LAYOUT ||
         macrotile == R300_BUFFER_SELECT_LAYOUT) {
         r300_setup_tiling(rscreen, desc);
@@ -428,7 +440,6 @@ boolean r300_texture_desc_init(struct r300_screen *rscreen,
         assert(desc->b.b.last_level == 0);
     }
 
-    r300_setup_flags(desc);
     r300_setup_cbzb_flags(rscreen, desc);
 
     /* Setup the miptree description. */
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_code.h b/src/mesa/drivers/dri/r300/compiler/radeon_code.h
index 53cc1bd..2dd9c5e 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_code.h
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_code.h
@@ -60,6 +60,7 @@ enum {
 
 	RC_STATE_R300_WINDOW_DIMENSION,
 	RC_STATE_R300_TEXRECT_FACTOR,
+	RC_STATE_R300_TEXSCALE_FACTOR,
 	RC_STATE_R300_VIEWPORT_SCALE,
 	RC_STATE_R300_VIEWPORT_OFFSET
 };
@@ -158,7 +159,13 @@ struct r300_fragment_program_external_state {
 		 * If this field is \ref RC_WRAP_NONE (aka 0), no wrapping maths
 		 * will be performed on the coordinates.
 		 */
-		unsigned wrap_mode : 2;
+		unsigned wrap_mode : 3;
+
+		/**
+		 * The coords are scaled after applying the wrap mode emulation
+		 * and right before texture fetch. The scaling factor is given by
+		 * RC_STATE_R300_TEXSCALE_FACTOR. */
+		unsigned clamp_and_scale_before_fetch : 1;
 	} unit[16];
 };
 
diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_program_tex.c b/src/mesa/drivers/dri/r300/compiler/radeon_program_tex.c
index de988b7..530afa5 100644
--- a/src/mesa/drivers/dri/r300/compiler/radeon_program_tex.c
+++ b/src/mesa/drivers/dri/r300/compiler/radeon_program_tex.c
@@ -252,7 +252,8 @@ int radeonTransformTEX(
 
 	/* Divide by W if needed. */
 	if (inst->U.I.Opcode == RC_OPCODE_TXP &&
-	    (wrapmode == RC_WRAP_REPEAT || wrapmode == RC_WRAP_MIRRORED_REPEAT)) {
+	    (wrapmode == RC_WRAP_REPEAT || wrapmode == RC_WRAP_MIRRORED_REPEAT ||
+	     compiler->state.unit[inst->U.I.TexSrcUnit].clamp_and_scale_before_fetch)) {
 		projective_divide(compiler, inst);
 	}
 
@@ -388,6 +389,35 @@ int radeonTransformTEX(
 		inst->U.I.SrcReg[0].Index = temp;
 	}
 
+	if (inst->U.I.Opcode != RC_OPCODE_KIL &&
+	    compiler->state.unit[inst->U.I.TexSrcUnit].clamp_and_scale_before_fetch) {
+		struct rc_instruction *inst_mov;
+		unsigned temp = rc_find_free_temporary(c);
+
+		/* Saturate XYZ. */
+		inst_mov = rc_insert_new_instruction(c, inst->Prev);
+		inst_mov->U.I.Opcode = RC_OPCODE_MOV;
+		inst_mov->U.I.SaturateMode = RC_SATURATE_ZERO_ONE;
+		inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
+		inst_mov->U.I.DstReg.Index = temp;
+		inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZ;
+		inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
+
+		/* Copy W. */
+		inst_mov = rc_insert_new_instruction(c, inst->Prev);
+		inst_mov->U.I.Opcode = RC_OPCODE_MOV;
+		inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
+		inst_mov->U.I.DstReg.Index = temp;
+		inst_mov->U.I.DstReg.WriteMask = RC_MASK_W;
+		inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
+
+		reset_srcreg(&inst->U.I.SrcReg[0]);
+		inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
+		inst->U.I.SrcReg[0].Index = temp;
+
+		scale_texcoords(compiler, inst, RC_STATE_R300_TEXSCALE_FACTOR);
+	}
+
 	/* Cannot write texture to output registers (all chips) or with masks (non-r500) */
 	if (inst->U.I.Opcode != RC_OPCODE_KIL &&
 		(inst->U.I.DstReg.File != RC_FILE_TEMPORARY ||




More information about the mesa-commit mailing list