Example #1
0
/**
 * Wraps Mesa's implementation to ensure that the base level image is mapped.
 *
 * This relies on internal details of _mesa_generate_mipmap, in particular
 * the fact that the memory for recreated texture images is always freed.
 */
static void radeon_generate_mipmap(GLcontext *ctx, GLenum target,
				   struct gl_texture_object *texObj)
{
	radeonTexObj* t = radeon_tex_obj(texObj);
	GLuint nr_faces = (t->base.Target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
	int i, face;

	radeon_print(RADEON_TEXTURE, RADEON_VERBOSE,
			"%s(%p, tex %p) Target type %s.\n",
			__func__, ctx, texObj,
			_mesa_lookup_enum_by_nr(target));

	_mesa_generate_mipmap(ctx, target, texObj);

	for (face = 0; face < nr_faces; face++) {
		for (i = texObj->BaseLevel + 1; i < texObj->MaxLevel; i++) {
			radeon_texture_image *image;

			image = get_radeon_texture_image(texObj->Image[face][i]);

			if (image == NULL)
				break;

			image->mtlevel = i;
			image->mtface = face;

			radeon_miptree_unreference(&image->mt);
		}
	}
	
}
Example #2
0
void GLAPIENTRY
_mesa_GenerateMipmapEXT(GLenum target)
{
   struct gl_texture_unit *texUnit;
   struct gl_texture_object *texObj;
   GET_CURRENT_CONTEXT(ctx);

   ASSERT_OUTSIDE_BEGIN_END(ctx);
   FLUSH_VERTICES(ctx, _NEW_BUFFERS);

   switch (target) {
   case GL_TEXTURE_1D:
   case GL_TEXTURE_2D:
   case GL_TEXTURE_3D:
   case GL_TEXTURE_CUBE_MAP:
      /* OK, legal value */
      break;
   default:
      _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
      return;
   }

   texUnit = &ctx->Texture.Unit[ctx->Texture.CurrentUnit];
   texObj = _mesa_select_tex_object(ctx, texUnit, target);

   /* XXX this might not handle cube maps correctly */
   _mesa_lock_texture(ctx, texObj);
   _mesa_generate_mipmap(ctx, target, texUnit, texObj);
   _mesa_unlock_texture(ctx, texObj);
}
Example #3
0
/**
 * Generate new mipmap data from BASE+1 to BASE+p (the minimally-sized mipmap
 * level).
 *
 * The texture object's miptree must be mapped.
 *
 * It would be really nice if this was just called by Mesa whenever mipmaps
 * needed to be regenerated, rather than us having to remember to do so in
 * each texture image modification path.
 *
 * This function should also include an accelerated path.
 */
void
intel_generate_mipmap(GLcontext *ctx, GLenum target,
                      struct gl_texture_object *texObj)
{
   struct intel_context *intel = intel_context(ctx);
   struct intel_texture_object *intelObj = intel_texture_object(texObj);
   GLuint nr_faces = (intelObj->base.Target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
   int face, i;

   _mesa_generate_mipmap(ctx, target, texObj);

   /* Update the level information in our private data in the new images, since
    * it didn't get set as part of a normal TexImage path.
    */
   for (face = 0; face < nr_faces; face++) {
      for (i = texObj->BaseLevel + 1; i < texObj->MaxLevel; i++) {
         struct intel_texture_image *intelImage;

	 intelImage = intel_texture_image(texObj->Image[face][i]);
	 if (intelImage == NULL)
	    break;

	 intelImage->level = i;
	 intelImage->face = face;
	 /* Unreference the miptree to signal that the new Data is a bare
	  * pointer from mesa.
	  */
	 intel_miptree_release(intel, &intelImage->mt);
      }
   }
}
Example #4
0
/**
 * Called via ctx->Driver.GenerateMipmap()
 * This is basically a wrapper for _mesa_meta_GenerateMipmap() which checks
 * if we'll be using software mipmap generation.  In that case, we need to
 * map/unmap the base level texture image.
 */
static void
intelGenerateMipmap(struct gl_context *ctx, GLenum target,
                    struct gl_texture_object *texObj)
{
   if (_mesa_meta_check_generate_mipmap_fallback(ctx, target, texObj)) {
      /* sw path: need to map texture images */
      struct intel_context *intel = intel_context(ctx);
      struct intel_texture_object *intelObj = intel_texture_object(texObj);
      struct gl_texture_image *first_image = texObj->Image[0][texObj->BaseLevel];

      fallback_debug("%s - fallback to swrast\n", __FUNCTION__);

      intel_tex_map_level_images(intel, intelObj, texObj->BaseLevel);
      _mesa_generate_mipmap(ctx, target, texObj);
      intel_tex_unmap_level_images(intel, intelObj, texObj->BaseLevel);

      if (!_mesa_is_format_compressed(first_image->TexFormat)) {
         GLuint nr_faces = (texObj->Target == GL_TEXTURE_CUBE_MAP) ? 6 : 1;
         GLuint face, i;
         /* Update the level information in our private data in the new images,
          * since it didn't get set as part of a normal TexImage path.
          */
         for (face = 0; face < nr_faces; face++) {
            for (i = texObj->BaseLevel + 1; i < texObj->MaxLevel; i++) {
               struct intel_texture_image *intelImage =
                  intel_texture_image(texObj->Image[face][i]);
               if (!intelImage)
                  break;
               intelImage->level = i;
               intelImage->face = face;
               /* Unreference the miptree to signal that the new Data is a
                * bare pointer from mesa.
                */
               intel_miptree_release(intel, &intelImage->mt);
            }
         }
      }
   }
   else {
      _mesa_meta_GenerateMipmap(ctx, target, texObj);
   }
}
Example #5
0
/**
 * Called via ctx->Driver.GenerateMipmap().
 */
void
st_generate_mipmap(struct gl_context *ctx, GLenum target,
                   struct gl_texture_object *texObj)
{
   struct st_context *st = st_context(ctx);
   struct st_texture_object *stObj = st_texture_object(texObj);
   struct pipe_resource *pt = st_get_texobj_resource(texObj);
   const uint baseLevel = texObj->BaseLevel;
   uint lastLevel, first_layer, last_layer;
   uint dstLevel;

   if (!pt)
      return;

   /* not sure if this ultimately actually should work,
      but we're not supporting multisampled textures yet. */
   assert(pt->nr_samples < 2);

   /* find expected last mipmap level to generate*/
   lastLevel = compute_num_levels(ctx, texObj, target) - 1;

   if (lastLevel == 0)
      return;

   /* The texture isn't in a "complete" state yet so set the expected
    * lastLevel here, since it won't get done in st_finalize_texture().
    */
   stObj->lastLevel = lastLevel;

   if (pt->last_level < lastLevel) {
      /* The current gallium texture doesn't have space for all the
       * mipmap levels we need to generate.  So allocate a new texture.
       */
      struct pipe_resource *oldTex = stObj->pt;

      /* create new texture with space for more levels */
      stObj->pt = st_texture_create(st,
                                    oldTex->target,
                                    oldTex->format,
                                    lastLevel,
                                    oldTex->width0,
                                    oldTex->height0,
                                    oldTex->depth0,
                                    oldTex->array_size,
                                    0,
                                    oldTex->bind);

      /* This will copy the old texture's base image into the new texture
       * which we just allocated.
       */
      st_finalize_texture(ctx, st->pipe, texObj);

      /* release the old tex (will likely be freed too) */
      pipe_resource_reference(&oldTex, NULL);
      st_texture_release_all_sampler_views(stObj);
   }
   else {
      /* Make sure that the base texture image data is present in the
       * texture buffer.
       */
      st_finalize_texture(ctx, st->pipe, texObj);
   }

   pt = stObj->pt;

   assert(pt->last_level >= lastLevel);

   if (pt->target == PIPE_TEXTURE_CUBE) {
      first_layer = last_layer = _mesa_tex_target_to_face(target);
   }
   else {
      first_layer = 0;
      last_layer = util_max_layer(pt, baseLevel);
   }

   /* Try to generate the mipmap by rendering/texturing.  If that fails,
    * use the software fallback.
    */
   if (!util_gen_mipmap(st->pipe, pt, pt->format, baseLevel, lastLevel,
                        first_layer, last_layer, PIPE_TEX_FILTER_LINEAR)) {
      _mesa_generate_mipmap(ctx, target, texObj);
   }

   /* Fill in the Mesa gl_texture_image fields */
   for (dstLevel = baseLevel + 1; dstLevel <= lastLevel; dstLevel++) {
      const uint srcLevel = dstLevel - 1;
      const struct gl_texture_image *srcImage
         = _mesa_get_tex_image(ctx, texObj, target, srcLevel);
      struct gl_texture_image *dstImage;
      struct st_texture_image *stImage;
      uint border = srcImage->Border;
      uint dstWidth, dstHeight, dstDepth;

      dstWidth = u_minify(pt->width0, dstLevel);
      if (texObj->Target == GL_TEXTURE_1D_ARRAY) {
         dstHeight = pt->array_size;
      }
      else {
         dstHeight = u_minify(pt->height0, dstLevel);
      }
      if (texObj->Target == GL_TEXTURE_2D_ARRAY ||
          texObj->Target == GL_TEXTURE_CUBE_MAP_ARRAY) {
         dstDepth = pt->array_size;
      }
      else {
         dstDepth = u_minify(pt->depth0, dstLevel);
      }

      dstImage = _mesa_get_tex_image(ctx, texObj, target, dstLevel);
      if (!dstImage) {
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps");
         return;
      }

      /* Free old image data */
      ctx->Driver.FreeTextureImageBuffer(ctx, dstImage);

      /* initialize new image */
      _mesa_init_teximage_fields(ctx, dstImage, dstWidth, dstHeight,
                                 dstDepth, border, srcImage->InternalFormat,
                                 srcImage->TexFormat);

      stImage = st_texture_image(dstImage);

      pipe_resource_reference(&stImage->pt, pt);
   }
}
/**
 * The GenerateMipmap() driver hook.
 */
void
brw_generate_mipmap(struct gl_context *ctx, GLenum target,
                    struct gl_texture_object *tex_obj)
{
   struct brw_context *brw = brw_context(ctx);
   struct gen_device_info *devinfo = &brw->screen->devinfo;
   struct intel_texture_object *intel_obj = intel_texture_object(tex_obj);
   const unsigned base_level = tex_obj->BaseLevel;
   unsigned last_level, first_layer, last_layer;

   /* Blorp doesn't handle combined depth/stencil surfaces on Gen4-5 yet. */
   if (devinfo->gen <= 5 &&
       (tex_obj->Image[0][base_level]->_BaseFormat == GL_DEPTH_COMPONENT ||
        tex_obj->Image[0][base_level]->_BaseFormat == GL_DEPTH_STENCIL)) {
      _mesa_meta_GenerateMipmap(ctx, target, tex_obj);
      return;
   }

   /* find expected last mipmap level to generate */
   last_level = _mesa_compute_num_levels(ctx, tex_obj, target) - 1;

   if (last_level == 0)
      return;

   /* The texture isn't in a "complete" state yet so set the expected
    * last_level here; we're not going through normal texture validation.
    */
   intel_obj->_MaxLevel = last_level;

   if (!tex_obj->Immutable) {
      _mesa_prepare_mipmap_levels(ctx, tex_obj, base_level, last_level);

      /* At this point, memory for all the texture levels has been
       * allocated.  However, the base level image may be in one resource
       * while the subsequent/smaller levels may be in another resource.
       * Finalizing the texture will copy the base images from the former
       * resource to the latter.
       *
       * After this, we'll have all mipmap levels in one resource.
       */
      intel_finalize_mipmap_tree(brw, tex_obj);
   }

   struct intel_mipmap_tree *mt = intel_obj->mt;
   if (!mt) {
      _mesa_error(ctx, GL_OUT_OF_MEMORY, "mipmap generation");
      return;
   }

   const mesa_format format = intel_obj->_Format;

   /* Fall back to the CPU for non-renderable cases.
    *
    * TODO: 3D textures require blending data from multiple slices,
    * which means we need custom shaders.  For now, fall back.
    */
   if (!brw->mesa_format_supports_render[format] || target == GL_TEXTURE_3D) {
      _mesa_generate_mipmap(ctx, target, tex_obj);
      return;
   }

   const struct isl_extent4d *base_size = &mt->surf.logical_level0_px;

   if (mt->target == GL_TEXTURE_CUBE_MAP) {
      first_layer = _mesa_tex_target_to_face(target);
      last_layer = first_layer;
   } else {
      first_layer = 0;
      last_layer = base_size->array_len - 1;
   }

   /* The GL_EXT_texture_sRGB_decode extension's issues section says:
    *
    *    "10) How is mipmap generation of sRGB textures affected by the
    *     TEXTURE_SRGB_DECODE_EXT parameter?
    *
    *     RESOLVED:  When the TEXTURE_SRGB_DECODE parameter is DECODE_EXT
    *     for an sRGB texture, mipmap generation should decode sRGB texels
    *     to a linear RGB color space, perform downsampling, then encode
    *     back to an sRGB color space.  (Issue 24 in the EXT_texture_sRGB
    *     specification provides a rationale for why.)  When the parameter
    *     is SKIP_DECODE_EXT instead, mipmap generation skips the encode
    *     and decode steps during mipmap generation.  By skipping the
    *     encode and decode steps, sRGB mipmap generation should match
    *     the mipmap generation for a non-sRGB texture."
    */
   bool do_srgb = tex_obj->Sampler.sRGBDecode == GL_DECODE_EXT;

   for (unsigned dst_level = base_level + 1;
        dst_level <= last_level;
        dst_level++) {

      const unsigned src_level = dst_level - 1;

      for (unsigned layer = first_layer; layer <= last_layer; layer++) {
         brw_blorp_blit_miptrees(brw, mt, src_level, layer, format,
                                 SWIZZLE_XYZW, mt, dst_level, layer, format,
                                 0, 0,
                                 minify(base_size->width, src_level),
                                 minify(base_size->height, src_level),
                                 0, 0,
                                 minify(base_size->width, dst_level),
                                 minify(base_size->height, dst_level),
                                 GL_LINEAR, false, false,
                                 do_srgb, do_srgb);
      }
   }
}
Example #7
0
/**
 * Called via ctx->Driver.GenerateMipmap()
 * Note: We don't yet support 3D textures, 1D/2D array textures or texture
 * borders.
 */
void
_mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
                          struct gl_texture_object *texObj)
{
   struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
   struct vertex verts[4];
   const GLuint baseLevel = texObj->BaseLevel;
   const GLuint maxLevel = texObj->MaxLevel;
   const GLint maxLevelSave = texObj->MaxLevel;
   const GLboolean genMipmapSave = texObj->GenerateMipmap;
   const GLuint currentTexUnitSave = ctx->Texture.CurrentUnit;
   const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
                                      ctx->Extensions.ARB_fragment_shader;
   GLenum faceTarget;
   GLuint dstLevel;
   GLuint samplerSave;
   GLint swizzle[4];
   GLboolean swizzleSaved = GL_FALSE;

   if (fallback_required(ctx, target, texObj)) {
      _mesa_generate_mipmap(ctx, target, texObj);
      return;
   }

   if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
       target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
      faceTarget = target;
      target = GL_TEXTURE_CUBE_MAP;
   } else {
      faceTarget = target;
   }

   _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_DRAW_BUFFERS);

   /* Choose between glsl version and fixed function version of
    * GenerateMipmap function.
    */
   if (use_glsl_version) {
      _mesa_meta_setup_vertex_objects(&mipmap->VAO, &mipmap->VBO, true,
                                      2, 4, 0);
      _mesa_meta_setup_blit_shader(ctx, target, false, &mipmap->shaders);
   } else {
      _mesa_meta_setup_ff_tnl_for_blit(&mipmap->VAO, &mipmap->VBO, 3);
      _mesa_set_enable(ctx, target, GL_TRUE);
   }

   samplerSave = ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
      ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;

   if (currentTexUnitSave != 0)
      glBindTexture(target, texObj->Name);

   if (!mipmap->Sampler) {
      glGenSamplers(1, &mipmap->Sampler);
      glBindSampler(ctx->Texture.CurrentUnit, mipmap->Sampler);

      glSamplerParameteri(mipmap->Sampler,
                              GL_TEXTURE_MIN_FILTER,
                              GL_LINEAR_MIPMAP_LINEAR);
      glSamplerParameteri(mipmap->Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glSamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glSamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glSamplerParameteri(mipmap->Sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

      /* We don't want to encode or decode sRGB values; treat them as linear.
       * This is not technically correct for GLES3 but we don't get any API
       * error at the moment.
       */
      if (ctx->Extensions.EXT_texture_sRGB_decode) {
         glSamplerParameteri(mipmap->Sampler, GL_TEXTURE_SRGB_DECODE_EXT,
               GL_SKIP_DECODE_EXT);
      }
   } else {
      glBindSampler(ctx->Texture.CurrentUnit, mipmap->Sampler);
   }

   assert(mipmap->FBO != 0);
   glBindFramebuffer(GL_FRAMEBUFFER, mipmap->FBO);

   glTexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE);

//   if (texObj->_Swizzle != SWIZZLE_NOOP) {
//      static const GLint swizzleNoop[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
//      memcpy(swizzle, texObj->Swizzle, sizeof(swizzle));
//      swizzleSaved = GL_TRUE;
//      glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzleNoop);
//   }

   /* Silence valgrind warnings about reading uninitialized stack. */
   memset(verts, 0, sizeof(verts));

   /* setup vertex positions */
   verts[0].x = -1.0F;
   verts[0].y = -1.0F;
   verts[1].x =  1.0F;
   verts[1].y = -1.0F;
   verts[2].x =  1.0F;
   verts[2].y =  1.0F;
   verts[3].x = -1.0F;
   verts[3].y =  1.0F;

   /* texture is already locked, unlock now */
   _mesa_unlock_texture(ctx, texObj);

   for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) {
      const struct gl_texture_image *srcImage;
      struct gl_texture_image *dstImage;
      const GLuint srcLevel = dstLevel - 1;
      GLuint layer;
      GLsizei srcWidth, srcHeight, srcDepth;
      GLsizei dstWidth, dstHeight, dstDepth;

      srcImage = _mesa_select_tex_image(texObj, faceTarget, srcLevel);
      assert(srcImage->Border == 0);

      /* src size */
      srcWidth = srcImage->Width;
      if (target == GL_TEXTURE_1D_ARRAY) {
         srcHeight = 1;
         srcDepth = srcImage->Height;
      } else {
         srcHeight = srcImage->Height;
         srcDepth = srcImage->Depth;
      }

      /* new dst size */
      dstWidth = minify(srcWidth, 1);
      dstHeight = minify(srcHeight, 1);
//      dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth;
      dstDepth = srcDepth;

      if (dstWidth == srcWidth &&
          dstHeight == srcHeight &&
          dstDepth == srcDepth) {
         /* all done */
         break;
      }

      /* Allocate storage for the destination mipmap image(s) */

      /* Set MaxLevel large enough to hold the new level when we allocate it */
      glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, dstLevel);

      if (!prepare_mipmap_level(ctx, texObj, dstLevel,
                                dstWidth, dstHeight, dstDepth,
                                srcImage->InternalFormat,
                                srcImage->TexFormat)) {
         /* All done.  We either ran out of memory or we would go beyond the
          * last valid level of an immutable texture if we continued.
          */
         break;
      }
      dstImage = _mesa_select_tex_image(texObj, faceTarget, dstLevel);

      /* limit minification to src level */
      glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, srcLevel);

      /* setup viewport */
      _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight);
      glDrawBuffer(GL_COLOR_ATTACHMENT0);

      for (layer = 0; layer < dstDepth; ++layer) {
         /* Setup texture coordinates */
         _mesa_meta_setup_texture_coords(faceTarget,
                                         layer,
                                         0, 0, 1, /* width, height never used here */
                                         verts[0].tex,
                                         verts[1].tex,
                                         verts[2].tex,
                                         verts[3].tex);

         /* upload vertex data */
         glBufferData(GL_ARRAY_BUFFER, sizeof(verts),
                          verts, GL_DYNAMIC_DRAW);

         _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dstImage, layer);

         /* sanity check */
         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
             GL_FRAMEBUFFER_COMPLETE) {
            _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
                          "_mesa_meta_GenerateMipmap()");
            break;
         }

         assert(dstWidth == ctx->DrawBuffer->Width);
         if (target == GL_TEXTURE_1D_ARRAY) {
            assert(dstHeight == 1);
         } else {
            assert(dstHeight == ctx->DrawBuffer->Height);
         }

         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
      }
   }

   _mesa_lock_texture(ctx, texObj); /* relock */

   glBindSampler(ctx->Texture.CurrentUnit, samplerSave);

   _mesa_meta_end(ctx);

   glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, maxLevelSave);
   if (genMipmapSave)
      glTexParameteri(target, GL_GENERATE_MIPMAP, genMipmapSave);
//   if (swizzleSaved)
//      glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
}
Example #8
0
/**
 * Called via ctx->Driver.GenerateMipmap().
 */
void
st_generate_mipmap(struct gl_context *ctx, GLenum target,
                   struct gl_texture_object *texObj)
{
   struct st_context *st = st_context(ctx);
   struct st_texture_object *stObj = st_texture_object(texObj);
   struct pipe_resource *pt = st_get_texobj_resource(texObj);
   const uint baseLevel = texObj->BaseLevel;
   enum pipe_format format;
   uint lastLevel, first_layer, last_layer;

   if (!pt)
      return;

   /* not sure if this ultimately actually should work,
      but we're not supporting multisampled textures yet. */
   assert(pt->nr_samples < 2);

   /* find expected last mipmap level to generate*/
   lastLevel = compute_num_levels(ctx, texObj, target) - 1;

   if (lastLevel == 0)
      return;

   st_flush_bitmap_cache(st);
   st_invalidate_readpix_cache(st);

   /* The texture isn't in a "complete" state yet so set the expected
    * lastLevel here, since it won't get done in st_finalize_texture().
    */
   stObj->lastLevel = lastLevel;

   if (!texObj->Immutable) {
      const GLboolean genSave = texObj->GenerateMipmap;

      /* Temporarily set GenerateMipmap to true so that allocate_full_mipmap()
       * makes the right decision about full mipmap allocation.
       */
      texObj->GenerateMipmap = GL_TRUE;

      _mesa_prepare_mipmap_levels(ctx, texObj, baseLevel, lastLevel);

      texObj->GenerateMipmap = genSave;

      /* At this point, memory for all the texture levels has been
       * allocated.  However, the base level image may be in one resource
       * while the subsequent/smaller levels may be in another resource.
       * Finalizing the texture will copy the base images from the former
       * resource to the latter.
       *
       * After this, we'll have all mipmap levels in one resource.
       */
      st_finalize_texture(ctx, st->pipe, texObj);
   }

   pt = stObj->pt;
   if (!pt) {
      _mesa_error(ctx, GL_OUT_OF_MEMORY, "mipmap generation");
      return;
   }

   assert(pt->last_level >= lastLevel);

   if (pt->target == PIPE_TEXTURE_CUBE) {
      first_layer = last_layer = _mesa_tex_target_to_face(target);
   }
   else {
      first_layer = 0;
      last_layer = util_max_layer(pt, baseLevel);
   }

   if (stObj->surface_based)
      format = stObj->surface_format;
   else
      format = pt->format;

   /* First see if the driver supports hardware mipmap generation,
    * if not then generate the mipmap by rendering/texturing.
    * If that fails, use the software fallback.
    */
   if (!st->pipe->screen->get_param(st->pipe->screen,
                                    PIPE_CAP_GENERATE_MIPMAP) ||
       !st->pipe->generate_mipmap(st->pipe, pt, format, baseLevel,
                                  lastLevel, first_layer, last_layer)) {

      if (!util_gen_mipmap(st->pipe, pt, format, baseLevel, lastLevel,
                           first_layer, last_layer, PIPE_TEX_FILTER_LINEAR)) {
         _mesa_generate_mipmap(ctx, target, texObj);
      }
   }
}
/**
 * Called via ctx->Driver.GenerateMipmap()
 * Note: We don't yet support 3D textures, or texture borders.
 */
void
_mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
                          struct gl_texture_object *texObj)
{
   struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
   struct vertex verts[4];
   const GLuint baseLevel = texObj->BaseLevel;
   const GLuint maxLevel = texObj->MaxLevel;
   const GLint maxLevelSave = texObj->MaxLevel;
   const GLboolean genMipmapSave = texObj->GenerateMipmap;
   const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
                                      ctx->Extensions.ARB_fragment_shader;
   GLenum faceTarget;
   GLuint dstLevel;
   struct gl_sampler_object *samp_obj_save = NULL;
   GLint swizzle[4];
   GLboolean swizzleSaved = GL_FALSE;

   /* GLint so the compiler won't complain about type signedness mismatch in
    * the calls to _mesa_texture_parameteriv below.
    */
   static const GLint always_false = GL_FALSE;
   static const GLint always_true = GL_TRUE;

   if (fallback_required(ctx, target, texObj)) {
      _mesa_generate_mipmap(ctx, target, texObj);
      return;
   }

   if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
       target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
      faceTarget = target;
      target = GL_TEXTURE_CUBE_MAP;
   } else {
      faceTarget = target;
   }

   _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_DRAW_BUFFERS);
   _mesa_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

   /* Choose between glsl version and fixed function version of
    * GenerateMipmap function.
    */
   if (use_glsl_version) {
      _mesa_meta_setup_vertex_objects(ctx, &mipmap->VAO, &mipmap->buf_obj, true,
                                      2, 4, 0);
      _mesa_meta_setup_blit_shader(ctx, target, false, &mipmap->shaders);
   } else {
      _mesa_meta_setup_ff_tnl_for_blit(ctx, &mipmap->VAO, &mipmap->buf_obj, 3);
      _mesa_set_enable(ctx, target, GL_TRUE);
   }

   _mesa_reference_sampler_object(ctx, &samp_obj_save,
                                  ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler);

   /* We may have been called from glGenerateTextureMipmap with CurrentUnit
    * still set to 0, so we don't know when we can skip binding the texture.
    * Assume that _mesa_BindTexture will be fast if we're rebinding the same
    * texture.
    */
   _mesa_BindTexture(target, texObj->Name);

   if (mipmap->samp_obj == NULL) {
      mipmap->samp_obj =  ctx->Driver.NewSamplerObject(ctx, 0xDEADBEEF);
      if (mipmap->samp_obj == NULL) {
         /* This is a bit lazy.  Flag out of memory, and then don't bother to
          * clean up.  Once out of memory is flagged, the only realistic next
          * move is to destroy the context.  That will trigger all the right
          * clean up.
          */
         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenerateMipmap");
         return;
      }

      _mesa_set_sampler_filters(ctx, mipmap->samp_obj, GL_LINEAR_MIPMAP_LINEAR,
                                GL_LINEAR);
      _mesa_set_sampler_wrap(ctx, mipmap->samp_obj, GL_CLAMP_TO_EDGE,
                             GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);

      /* We don't want to encode or decode sRGB values; treat them as linear. */
      _mesa_set_sampler_srgb_decode(ctx, mipmap->samp_obj, GL_SKIP_DECODE_EXT);
   }

   _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, mipmap->samp_obj);

   assert(mipmap->fb != NULL);
   _mesa_bind_framebuffers(ctx, mipmap->fb, mipmap->fb);

   _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_false, false);

   if (texObj->_Swizzle != SWIZZLE_NOOP) {
      static const GLint swizzleNoop[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
      memcpy(swizzle, texObj->Swizzle, sizeof(swizzle));
      swizzleSaved = GL_TRUE;
      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA,
                                swizzleNoop, false);
   }

   /* Silence valgrind warnings about reading uninitialized stack. */
   memset(verts, 0, sizeof(verts));

   /* setup vertex positions */
   verts[0].x = -1.0F;
   verts[0].y = -1.0F;
   verts[1].x =  1.0F;
   verts[1].y = -1.0F;
   verts[2].x =  1.0F;
   verts[2].y =  1.0F;
   verts[3].x = -1.0F;
   verts[3].y =  1.0F;

   /* texture is already locked, unlock now */
   _mesa_unlock_texture(ctx, texObj);

   _mesa_prepare_mipmap_levels(ctx, texObj, baseLevel, maxLevel);

   for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) {
      const struct gl_texture_image *srcImage;
      struct gl_texture_image *dstImage;
      const GLuint srcLevel = dstLevel - 1;
      GLuint layer;
      GLsizei srcWidth, srcHeight, srcDepth;
      GLsizei dstWidth, dstHeight, dstDepth;

      srcImage = _mesa_select_tex_image(texObj, faceTarget, srcLevel);
      assert(srcImage->Border == 0);

      /* src size */
      srcWidth = srcImage->Width;
      if (target == GL_TEXTURE_1D_ARRAY) {
         srcHeight = 1;
         srcDepth = srcImage->Height;
      } else {
         srcHeight = srcImage->Height;
         srcDepth = srcImage->Depth;
      }

      /* new dst size */
      dstWidth = minify(srcWidth, 1);
      dstHeight = minify(srcHeight, 1);
      dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth;

      if (dstWidth == srcWidth &&
          dstHeight == srcHeight &&
          dstDepth == srcDepth) {
         /* all done */
         break;
      }

      /* Allocate storage for the destination mipmap image(s) */

      /* Set MaxLevel large enough to hold the new level when we allocate it */
      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
                                (GLint *) &dstLevel, false);

      dstImage = _mesa_select_tex_image(texObj, faceTarget, dstLevel);

      /* All done.  We either ran out of memory or we would go beyond the last
       * valid level of an immutable texture if we continued.
       */
      if (dstImage == NULL)
         break;

      /* limit minification to src level */
      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
                                (GLint *) &srcLevel, false);

      /* setup viewport */
      _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight);
      _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);

      for (layer = 0; layer < dstDepth; ++layer) {
         /* Setup texture coordinates */
         _mesa_meta_setup_texture_coords(faceTarget,
                                         layer,
                                         0, 0, /* xoffset, yoffset */
                                         srcWidth, srcHeight, /* img size */
                                         srcWidth, srcHeight, srcDepth,
                                         verts[0].tex,
                                         verts[1].tex,
                                         verts[2].tex,
                                         verts[3].tex);

         /* upload vertex data */
         _mesa_buffer_data(ctx, mipmap->buf_obj, GL_NONE, sizeof(verts), verts,
                           GL_DYNAMIC_DRAW, __func__);

         _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer,
                                              GL_COLOR_ATTACHMENT0, dstImage,
                                              layer);

         /* sanity check */
         if (_mesa_check_framebuffer_status(ctx, ctx->DrawBuffer) !=
             GL_FRAMEBUFFER_COMPLETE) {
            _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
                          "_mesa_meta_GenerateMipmap()");
            break;
         }

         assert(dstWidth == ctx->DrawBuffer->Width);
         if (target == GL_TEXTURE_1D_ARRAY) {
            assert(dstHeight == 1);
         } else {
            assert(dstHeight == ctx->DrawBuffer->Height);
         }

         _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
      }
   }

   _mesa_lock_texture(ctx, texObj); /* relock */

   _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, samp_obj_save);
   _mesa_reference_sampler_object(ctx, &samp_obj_save, NULL);

   _mesa_meta_end(ctx);

   _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, &maxLevelSave,
                             false);
   if (genMipmapSave)
      _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_true,
                                false);
   if (swizzleSaved)
      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA, swizzle,
                                false);
}