st/mesa: ignore gl_texture_object::BaseLevel when allocating gallium textures
authorBrian Paul <brianp@vmware.com>
Thu, 29 Apr 2010 21:32:36 +0000 (15:32 -0600)
committerBrian Paul <brianp@vmware.com>
Fri, 30 Apr 2010 18:33:40 +0000 (12:33 -0600)
Previously, when we created a gallium texture for a corresponding Mesa
texture we'd only allocate space for mipmap levels >= BaseLevel.

This patch undoes that mechanism.  This fixes a render-to-texture bug
when rendering to level 0 when BaseLevel=1.

Also, it makes sense to allocate the whole texture object memory when
BaseLevel > 0 since a common use of GL_TEXTURE_BASE_LEVEL is to
progressively load/render mipmaps.  Eventually, the app almost always
fills in the level=0 mipmap image.

Finally, the texture image code is bit easier to understand now.

src/mesa/state_tracker/st_atom_sampler.c
src/mesa/state_tracker/st_atom_texture.c
src/mesa/state_tracker/st_cb_fbo.c
src/mesa/state_tracker/st_cb_texture.c
src/mesa/state_tracker/st_gen_mipmap.c
src/mesa/state_tracker/st_texture.c
src/mesa/state_tracker/st_texture.h

index a8262a5e1aa3c4639853b056cfc4256fe4d2a592..92fe72d4df60ba947a2694cccf80b90d5c61e43e 100644 (file)
@@ -194,9 +194,12 @@ update_samplers(struct st_context *st)
             sampler->normalized_coords = 1;
 
          sampler->lod_bias = st->ctx->Texture.Unit[su].LodBias;
-         sampler->min_lod = MAX2(0.0f, texobj->MinLod);
-         sampler->max_lod = MIN2(texobj->MaxLevel - texobj->BaseLevel,
-                                 texobj->MaxLod);
+
+         sampler->min_lod = texobj->BaseLevel + texobj->MinLod;
+         if (sampler->min_lod < texobj->BaseLevel)
+            sampler->min_lod = texobj->BaseLevel;
+
+         sampler->max_lod = MIN2((GLfloat) texobj->MaxLevel, texobj->MaxLod);
          if (sampler->max_lod < sampler->min_lod) {
             /* The GL spec doesn't seem to specify what to do in this case.
              * Swap the values.
index f4294ac1e6f0d40705d9a636b62dbf3e71d801e5..2575adda8fc631162b38e368df967d5a409b056e 100644 (file)
@@ -138,7 +138,6 @@ finalize_textures(struct st_context *st)
          const GLuint texUnit = fprog->Base.SamplerUnits[su];
          struct gl_texture_object *texObj
             = st->ctx->Texture.Unit[texUnit]._Current;
-         struct st_texture_object *stObj = st_texture_object(texObj);
 
          if (texObj) {
             GLboolean flush, retval;
@@ -149,8 +148,6 @@ finalize_textures(struct st_context *st)
                st->missing_textures = GL_TRUE;
                continue;
             }
-
-            stObj->teximage_realloc = TRUE;
          }
       }
    }
index e5c956d561d10f5de5203c86a78eef1df149ee3a..c02121fbd1a9a41c860ccff4ef6fce3438aeb285 100644 (file)
@@ -321,15 +321,13 @@ st_render_texture(GLcontext *ctx,
    struct pipe_resource *pt = st_get_texobj_resource(att->Texture);
    struct st_texture_object *stObj;
    const struct gl_texture_image *texImage;
-   GLint pt_level;
 
    /* When would this fail?  Perhaps assert? */
    if (!pt) 
       return;
 
-   /* The first gallium texture level = Mesa BaseLevel */
-   pt_level = MAX2(0, (GLint) att->TextureLevel - att->Texture->BaseLevel);
-   texImage = att->Texture->Image[att->CubeMapFace][pt_level];
+   /* get pointer to texture image we're rendeing to */
+   texImage = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
 
    /* create new renderbuffer which wraps the texture image */
    rb = st_new_renderbuffer(ctx, 0);
@@ -350,7 +348,7 @@ st_render_texture(GLcontext *ctx,
 
    /* point renderbuffer at texobject */
    strb->rtt = stObj;
-   strb->rtt_level = pt_level;
+   strb->rtt_level = att->TextureLevel;
    strb->rtt_face = att->CubeMapFace;
    strb->rtt_slice = att->Zoffset;
 
index 3457214ca4c24e8c384f392459c823d7784d43a7..7e2e533881d35658862cb689759dbc02c012d1bf 100644 (file)
@@ -231,70 +231,86 @@ default_bindings(struct st_context *st, enum pipe_format format)
 }
 
 
+/** Return number of image dimensions (1, 2 or 3) for a texture target. */
+static GLuint
+get_texture_dims(GLenum target)
+{
+   switch (target) {
+   case GL_TEXTURE_1D:
+   case GL_TEXTURE_1D_ARRAY_EXT:
+      return 1;
+   case GL_TEXTURE_2D:
+   case GL_TEXTURE_CUBE_MAP_ARB:
+   case GL_TEXTURE_RECTANGLE_NV:
+   case GL_TEXTURE_2D_ARRAY_EXT:
+      return 2;
+   case GL_TEXTURE_3D:
+      return 3;
+   default:
+      assert(0 && "invalid texture target in get_texture_dims()");
+      return 1;
+   }
+}
+
+
 /**
- * Allocate a pipe_resource object for the given st_texture_object using
- * the given st_texture_image to guess the mipmap size/levels.
+ * Try to allocate a pipe_resource object for the given st_texture_object.
  *
- * [comments...]
- * Otherwise, store it in memory if (Border != 0) or (any dimension ==
- * 1).
- *    
- * Otherwise, if max_level >= level >= min_level, create texture with
- * space for images from min_level down to max_level.
+ * We use the given st_texture_image as a clue to determine the size of the
+ * mipmap image at level=0.
  *
- * Otherwise, create texture with space for images from (level 0)..(1x1).
- * Consider pruning this texture at a validation if the saving is worth it.
  */
 static void
 guess_and_alloc_texture(struct st_context *st,
                        struct st_texture_object *stObj,
                        const struct st_texture_image *stImage)
 {
-   GLuint firstLevel;
-   GLuint lastLevel;
-   GLuint width = stImage->base.Width2;  /* size w/out border */
-   GLuint height = stImage->base.Height2;
-   GLuint depth = stImage->base.Depth2;
-   GLuint i, bindings;
+   const GLuint dims = get_texture_dims(stObj->base.Target);
+   GLuint level, lastLevel, width, height, depth;
+   GLuint bindings;
    enum pipe_format fmt;
 
    DBG("%s\n", __FUNCTION__);
 
    assert(!stObj->pt);
 
-   if (stObj->pt &&
-       (GLint) stImage->level > stObj->base.BaseLevel &&
-       (stImage->base.Width == 1 ||
-        (stObj->base.Target != GL_TEXTURE_1D &&
-         stImage->base.Height == 1) ||
-        (stObj->base.Target == GL_TEXTURE_3D &&
-         stImage->base.Depth == 1)))
-      return;
+   level = stImage->level;
+   width = stImage->base.Width2;  /* size w/out border */
+   height = stImage->base.Height2;
+   depth = stImage->base.Depth2;
 
-   /* If this image disrespects BaseLevel, allocate from level zero.
-    * Usually BaseLevel == 0, so it's unlikely to happen.
-    */
-   if ((GLint) stImage->level < stObj->base.BaseLevel)
-      firstLevel = 0;
-   else
-      firstLevel = stObj->base.BaseLevel;
+   assert(width > 0);
+   assert(height > 0);
+   assert(depth > 0);
 
-
-   /* Figure out image dimensions at start level. 
+   /* Depending on the image's size, we can't always make a guess here.
     */
-   for (i = stImage->level; i > firstLevel; i--) {
+   if (level > 0) {
+      if ( (dims >= 1 && width == 1) ||
+           (dims >= 2 && height == 1) ||
+           (dims >= 3 && depth == 1) ) {
+         /* we can't determine the image size at level=0 */
+         stObj->width0 = stObj->height0 = stObj->depth0 = 0;
+         return;
+      }
+   }
+
+   /* grow the image size until we hit level = 0 */
+   while (level > 0) {
       if (width != 1)
          width <<= 1;
       if (height != 1)
          height <<= 1;
       if (depth != 1)
          depth <<= 1;
-   }
+      level--;
+   }      
 
-   if (width == 0 || height == 0 || depth == 0) {
-      /* no texture needed */
-      return;
-   }
+   assert(level == 0);
+
+   /* At this point, (width x height x depth) is the expected size of
+    * the level=0 mipmap image.
+    */
 
    /* Guess a reasonable value for lastLevel.  This is probably going
     * to be wrong fairly often and might mean that we have to look at
@@ -306,18 +322,23 @@ guess_and_alloc_texture(struct st_context *st,
         stImage->base._BaseFormat == GL_DEPTH_COMPONENT ||
         stImage->base._BaseFormat == GL_DEPTH_STENCIL_EXT) &&
        !stObj->base.GenerateMipmap &&
-       stImage->level == firstLevel) {
+       stImage->level == 0) {
       /* only alloc space for a single mipmap level */
-      lastLevel = firstLevel;
+      lastLevel = 0;
    }
    else {
       /* alloc space for a full mipmap */
       GLuint l2width = util_logbase2(width);
       GLuint l2height = util_logbase2(height);
       GLuint l2depth = util_logbase2(depth);
-      lastLevel = firstLevel + MAX2(MAX2(l2width, l2height), l2depth);
+      lastLevel = MAX2(MAX2(l2width, l2height), l2depth);
    }
 
+   /* Save the level=0 dimensions */
+   stObj->width0 = width;
+   stObj->height0 = height;
+   stObj->depth0 = depth;
+
    fmt = st_mesa_format_to_pipe_format(stImage->base.TexFormat);
 
    bindings = default_bindings(st, fmt);
@@ -581,15 +602,13 @@ st_TexImage(GLcontext * ctx,
     * mipmap.  If so, free the old mipmap.
     */
    if (stObj->pt) {
-      if (stObj->teximage_realloc ||
-          level > (GLint) stObj->pt->last_level ||
+      if (level > (GLint) stObj->pt->last_level ||
           !st_texture_match_image(stObj->pt, &stImage->base,
                                   stImage->face, stImage->level)) {
          DBG("release it\n");
          pipe_resource_reference(&stObj->pt, NULL);
          assert(!stObj->pt);
          pipe_sampler_view_reference(&stObj->sampler_view, NULL);
-         stObj->teximage_realloc = FALSE;
       }
    }
 
@@ -1757,12 +1776,26 @@ st_CopyTexSubImage3D(GLcontext * ctx, GLenum target, GLint level,
 }
 
 
+/**
+ * Copy image data from stImage into the texture object 'stObj' at level
+ * 'dstLevel'.
+ */
 static void
 copy_image_data_to_texture(struct st_context *st,
                           struct st_texture_object *stObj,
                            GLuint dstLevel,
                           struct st_texture_image *stImage)
 {
+   /* debug checks */
+   {
+      const struct gl_texture_image *dstImage =
+         stObj->base.Image[stImage->face][stImage->level];
+      assert(dstImage);
+      assert(dstImage->Width == stImage->base.Width);
+      assert(dstImage->Height == stImage->base.Height);
+      assert(dstImage->Depth == stImage->base.Depth);
+   }
+
    if (stImage->pt) {
       /* Copy potentially with the blitter:
        */
@@ -1811,8 +1844,9 @@ st_finalize_texture(GLcontext *ctx,
    struct st_context *st = st_context(ctx);
    struct st_texture_object *stObj = st_texture_object(tObj);
    const GLuint nr_faces = (stObj->base.Target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
-   GLuint blockSize, face;
+   GLuint face;
    struct st_texture_image *firstImage;
+   enum pipe_format firstImageFormat;
 
    *needFlush = GL_FALSE;
 
@@ -1827,10 +1861,11 @@ st_finalize_texture(GLcontext *ctx,
           stObj->base.MinFilter == GL_NEAREST)
          stObj->lastLevel = stObj->base.BaseLevel;
       else
-         stObj->lastLevel = stObj->base._MaxLevel - stObj->base.BaseLevel;
+         stObj->lastLevel = stObj->base._MaxLevel;
    }
 
    firstImage = st_texture_image(stObj->base.Image[0][stObj->base.BaseLevel]);
+   assert(firstImage);
 
    /* If both firstImage and stObj point to a texture which can contain
     * all active images, favour firstImage.  Note that because of the
@@ -1844,22 +1879,23 @@ st_finalize_texture(GLcontext *ctx,
       pipe_sampler_view_reference(&stObj->sampler_view, NULL);
    }
 
-   /* bytes per pixel block (blocks are usually 1x1) */
-   blockSize = _mesa_get_format_bytes(firstImage->base.TexFormat);
+   /* Find gallium format for the Mesa texture */
+   firstImageFormat = st_mesa_format_to_pipe_format(firstImage->base.TexFormat);
 
    /* If we already have a gallium texture, check that it matches the texture
     * object's format, target, size, num_levels, etc.
     */
    if (stObj->pt) {
-      const enum pipe_format fmt =
-         st_mesa_format_to_pipe_format(firstImage->base.TexFormat);
       if (stObj->pt->target != gl_target_to_pipe(stObj->base.Target) ||
-          stObj->pt->format != fmt ||
-          stObj->pt->last_level < stObj->lastLevel ||
-          stObj->pt->width0 != firstImage->base.Width2 ||
-          stObj->pt->height0 != firstImage->base.Height2 ||
-          stObj->pt->depth0 != firstImage->base.Depth2)
+          stObj->pt->format != firstImageFormat ||
+          stObj->pt->last_level != stObj->lastLevel ||
+          stObj->pt->width0 != stObj->width0 ||
+          stObj->pt->height0 != stObj->height0 ||
+          stObj->pt->depth0 != stObj->depth0)
       {
+         /* The gallium texture does not match the Mesa texture so delete the
+          * gallium texture now.  We'll make a new one below.
+          */
          pipe_resource_reference(&stObj->pt, NULL);
          pipe_sampler_view_reference(&stObj->sampler_view, NULL);
          st->dirty.st |= ST_NEW_FRAMEBUFFER;
@@ -1869,17 +1905,15 @@ st_finalize_texture(GLcontext *ctx,
    /* May need to create a new gallium texture:
     */
    if (!stObj->pt) {
-      const enum pipe_format fmt =
-         st_mesa_format_to_pipe_format(firstImage->base.TexFormat);
-      GLuint bindings = default_bindings(st, fmt);
+      GLuint bindings = default_bindings(st, firstImageFormat);
 
       stObj->pt = st_texture_create(st,
                                     gl_target_to_pipe(stObj->base.Target),
-                                    fmt,
+                                    firstImageFormat,
                                     stObj->lastLevel,
-                                    firstImage->base.Width2,
-                                    firstImage->base.Height2,
-                                    firstImage->base.Depth2,
+                                    stObj->width0,
+                                    stObj->height0,
+                                    stObj->depth0,
                                     bindings);
 
       if (!stObj->pt) {
@@ -1894,7 +1928,7 @@ st_finalize_texture(GLcontext *ctx,
       GLuint level;
       for (level = 0; level <= stObj->lastLevel; level++) {
          struct st_texture_image *stImage =
-            st_texture_image(stObj->base.Image[face][stObj->base.BaseLevel + level]);
+            st_texture_image(stObj->base.Image[face][level]);
 
          /* Need to import images in main memory or held in other textures.
           */
index b8b75c7de629189ae5d35b8e54eaf6ddfb7b5b88..a015c4bb5870c51bbb3366623e49f75e95cccd88 100644 (file)
@@ -342,10 +342,10 @@ st_generate_mipmap(GLcontext *ctx, GLenum target,
 
    assert(lastLevel <= pt->last_level);
 
-   /* Recall that the Mesa BaseLevel image is stored in the gallium
-    * texture's level[0] position.  So pass baseLevel=0 here.
+   /* Try to generate the mipmap by rendering/texturing.  If that fails,
+    * use the software fallback.
     */
-   if (!st_render_mipmap(st, target, stObj, 0, lastLevel)) {
+   if (!st_render_mipmap(st, target, stObj, baseLevel, lastLevel)) {
       fallback_generate_mipmap(ctx, target, texObj);
    }
 
index 1b52e77b7d9931f956dc292cb0e964ca5f6f4c36..722f60e425244a03183c9994068c81b97aa6c23c 100644 (file)
@@ -269,6 +269,9 @@ st_texture_image_copy(struct pipe_context *pipe,
    struct pipe_surface *dst_surface;
    GLuint i;
 
+   assert(src->width0 == dst->width0);
+   assert(src->height0 == dst->height0);
+
    for (i = 0; i < depth; i++) {
       GLuint srcLevel;
 
index d0298817ded5a441c0a786aacf5d7015739334cb..447f091db1fde51a21d229fd0d41892732a91193 100644 (file)
@@ -38,6 +38,9 @@
 struct pipe_resource;
 
 
+/**
+ * Subclass of gl_texure_image.
+ */
 struct st_texture_image
 {
    struct gl_texture_image base;
@@ -57,7 +60,9 @@ struct st_texture_image
 };
 
 
-
+/**
+ * Subclass of gl_texure_object.
+ */
 struct st_texture_object
 {
    struct gl_texture_object base;       /* The "parent" object */
@@ -66,6 +71,9 @@ struct st_texture_object
     */
    GLuint lastLevel;
 
+   /** The size of the level=0 mipmap image */
+   GLuint width0, height0, depth0;
+
    /* On validation any active images held in main memory or in other
     * textures will be copied to this texture and the old storage freed.
     */
@@ -76,8 +84,6 @@ struct st_texture_object
     */
    struct pipe_sampler_view *sampler_view;
 
-   GLboolean teximage_realloc;
-
    /* True if there is/was a surface bound to this texture object.  It helps
     * track whether the texture object is surface based or not.
     */