Merge branch 'mesa_7_6_branch'
[mesa.git] / src / mesa / drivers / common / meta.c
index 8d60f6963431ceec8843b713cf958da15c60ac69..d1c2232e26a8aa1c890e54631c08b0233c45ab5b 100644 (file)
 #include "main/arrayobj.h"
 #include "main/blend.h"
 #include "main/bufferobj.h"
+#include "main/buffers.h"
 #include "main/depth.h"
 #include "main/enable.h"
+#include "main/fbobject.h"
 #include "main/image.h"
 #include "main/macros.h"
 #include "main/matrix.h"
+#include "main/mipmap.h"
 #include "main/polygon.h"
 #include "main/readpix.h"
 #include "main/scissor.h"
@@ -137,6 +140,24 @@ struct save_state
 };
 
 
+/**
+ * Temporary texture used for glBlitFramebuffer, glDrawPixels, etc.
+ * This is currently shared by all the meta ops.  But we could create a
+ * separate one for each of glDrawPixel, glBlitFramebuffer, glCopyPixels, etc.
+ */
+struct temp_texture
+{
+   GLuint TexObj;
+   GLenum Target;         /**< GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE */
+   GLsizei MinSize;       /**< Min texture size to allocate */
+   GLsizei MaxSize;       /**< Max possible texture size */
+   GLboolean NPOT;        /**< Non-power of two size OK? */
+   GLsizei Width, Height; /**< Current texture size */
+   GLenum IntFormat;
+   GLfloat Sright, Ttop;  /**< right, top texcoords */
+};
+
+
 /**
  * State for glBlitFramebufer()
  */
@@ -188,24 +209,18 @@ struct bitmap_state
 {
    GLuint ArrayObj;
    GLuint VBO;
+   struct temp_texture Tex;  /**< separate texture from other meta ops */
 };
 
 
 /**
- * Temporary texture used for glBlitFramebuffer, glDrawPixels, etc.
- * This is currently shared by all the meta ops.  But we could create a
- * separate one for each of glDrawPixel, glBlitFramebuffer, glCopyPixels, etc.
+ * State for _mesa_meta_generate_mipmap()
  */
-struct temp_texture
+struct gen_mipmap_state
 {
-   GLuint TexObj;
-   GLenum Target;         /**< GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE */
-   GLsizei MinSize;       /**< Min texture size to allocate */
-   GLsizei MaxSize;       /**< Max possible texture size */
-   GLboolean NPOT;        /**< Non-power of two size OK? */
-   GLsizei Width, Height; /**< Current texture size */
-   GLenum IntFormat;
-   GLfloat Sright, Ttop;  /**< right, top texcoords */
+   GLuint ArrayObj;
+   GLuint VBO;
+   GLuint FBO;
 };
 
 
@@ -223,10 +238,7 @@ struct gl_meta_state
    struct copypix_state CopyPix;  /**< For _mesa_meta_copy_pixels() */
    struct drawpix_state DrawPix;  /**< For _mesa_meta_draw_pixels() */
    struct bitmap_state Bitmap;    /**< For _mesa_meta_bitmap() */
-
-   /* other possible meta-ops:
-    * glGenerateMipmap()
-    */
+   struct gen_mipmap_state Mipmap;    /**< For _mesa_meta_generate_mipmap() */
 };
 
 
@@ -257,6 +269,7 @@ _mesa_meta_free(GLcontext *ctx)
        * still get freed by _mesa_free_context_data().
        */
 
+      /* the temporary texture */
       _mesa_DeleteTextures(1, &meta->TempTex.TexObj);
 
       /* glBlitFramebuffer */
@@ -281,6 +294,7 @@ _mesa_meta_free(GLcontext *ctx)
       /* glBitmap */
       _mesa_DeleteBuffersARB(1, & meta->Bitmap.VBO);
       _mesa_DeleteVertexArraysAPPLE(1, &meta->Bitmap.ArrayObj);
+      _mesa_DeleteTextures(1, &meta->Bitmap.Tex.TexObj);
    }
 
    _mesa_free(ctx->Meta);
@@ -747,8 +761,8 @@ init_temp_texture(GLcontext *ctx, struct temp_texture *tex)
 
 
 /**
- * Return pointer to temp_texture info.  This does some one-time init
- * if needed.
+ * Return pointer to temp_texture info for non-bitmap ops.
+ * This does some one-time init if needed.
  */
 static struct temp_texture *
 get_temp_texture(GLcontext *ctx)
@@ -763,6 +777,24 @@ get_temp_texture(GLcontext *ctx)
 }
 
 
+/**
+ * Return pointer to temp_texture info for _mesa_meta_bitmap().
+ * We use a separate texture for bitmaps to reduce texture
+ * allocation/deallocation.
+ */
+static struct temp_texture *
+get_bitmap_temp_texture(GLcontext *ctx)
+{
+   struct temp_texture *tex = &ctx->Meta->Bitmap.Tex;
+
+   if (!tex->TexObj) {
+      init_temp_texture(ctx, tex);
+   }
+
+   return tex;
+}
+
+
 /**
  * Compute the width/height of texture needed to draw an image of the
  * given size.  Return a flag indicating whether the current texture
@@ -1480,7 +1512,10 @@ _mesa_meta_draw_pixels(GLcontext *ctx,
 
    if (_mesa_is_color_format(format)) {
       /* use more compact format when possible */
-      if (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA)
+      /* XXX disable special case for GL_LUMINANCE for now to work around
+       * apparent i965 driver bug (see bug #23670).
+       */
+      if (/*format == GL_LUMINANCE ||*/ format == GL_LUMINANCE_ALPHA)
          texIntFormat = format;
       else
          texIntFormat = GL_RGBA;
@@ -1686,7 +1721,7 @@ _mesa_meta_bitmap(GLcontext *ctx,
                   const GLubyte *bitmap1)
 {
    struct bitmap_state *bitmap = &ctx->Meta->Bitmap;
-   struct temp_texture *tex = get_temp_texture(ctx);
+   struct temp_texture *tex = get_bitmap_temp_texture(ctx);
    const GLenum texIntFormat = GL_ALPHA;
    const struct gl_pixelstore_attrib unpackSave = *unpack;
    GLfloat verts[4][9]; /* four verts of X,Y,Z,S,T,R,G,B,A */
@@ -1819,3 +1854,167 @@ _mesa_meta_bitmap(GLcontext *ctx,
 
    _mesa_meta_end(ctx);
 }
+
+
+void
+_mesa_meta_generate_mipmap(GLcontext *ctx, GLenum target,
+                           struct gl_texture_object *texObj)
+{
+   struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
+   struct { GLfloat x, y, s, t, r; } verts[4];
+   const GLuint baseLevel = texObj->BaseLevel;
+   const GLuint maxLevel = texObj->MaxLevel;
+   const GLenum minFilterSave = texObj->MinFilter;
+   const GLenum magFilterSave = texObj->MagFilter;
+   const GLuint fboSave = ctx->DrawBuffer->Name;
+   GLenum faceTarget;
+   GLuint level;
+   GLuint border = 0;
+
+   /* check for fallbacks */
+   if (!ctx->Extensions.EXT_framebuffer_object) {
+      _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, META_ALL);
+
+   if (mipmap->ArrayObj == 0) {
+      /* one-time setup */
+
+      /* create vertex array object */
+      _mesa_GenVertexArraysAPPLE(1, &mipmap->ArrayObj);
+      _mesa_BindVertexArrayAPPLE(mipmap->ArrayObj);
+
+      /* create vertex array buffer */
+      _mesa_GenBuffersARB(1, &mipmap->VBO);
+      _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, mipmap->VBO);
+      _mesa_BufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(verts),
+                          NULL, GL_DYNAMIC_DRAW_ARB);
+
+      /* setup vertex arrays */
+      _mesa_VertexPointer(2, GL_FLOAT, sizeof(verts[0]),
+                          (void *) (0 * sizeof(GLfloat)));
+      _mesa_TexCoordPointer(3, GL_FLOAT, sizeof(verts[0]),
+                            (void *) (2 * sizeof(GLfloat)));
+
+      _mesa_EnableClientState(GL_VERTEX_ARRAY);
+      _mesa_EnableClientState(GL_TEXTURE_COORD_ARRAY);
+   }
+   else {
+      _mesa_BindVertexArray(mipmap->ArrayObj);
+      _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, mipmap->VBO);
+   }
+
+   if (!mipmap->FBO) {
+      /* Bind the new renderbuffer to the color attachment point. */
+      _mesa_GenFramebuffersEXT(1, &mipmap->FBO);
+   }
+
+   _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mipmap->FBO);
+
+   _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+   _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+   _mesa_set_enable(ctx, target, GL_TRUE);
+
+   /* setup texcoords once (XXX what about border?) */
+   switch (faceTarget) {
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+      break;
+   case GL_TEXTURE_2D:
+      verts[0].s = 0.0F;
+      verts[0].t = 0.0F;
+      verts[0].r = 0.0F;
+      verts[1].s = 1.0F;
+      verts[1].t = 0.0F;
+      verts[2].r = 0.0F;
+      verts[3].s = 1.0F;
+      verts[3].t = 1.0F;
+      verts[3].r = 0.0F;
+      verts[4].s = 0.0F;
+      verts[4].t = 1.0F;
+      verts[4].r = 0.0F;
+      break;
+   }
+
+
+   for (level = baseLevel + 1; level <= maxLevel; level++) {
+      const struct gl_texture_image *srcImage;
+      const GLuint srcLevel = level - 1;
+      GLsizei srcWidth, srcHeight;
+      GLsizei newWidth, newHeight;
+      GLenum status;
+
+      srcImage = _mesa_select_tex_image(ctx, texObj, target, srcLevel);
+      assert(srcImage->Border == 0); /* XXX we can fix this */
+
+      srcWidth = srcImage->Width - 2 * border;
+      srcHeight = srcImage->Height - 2 * border;
+
+      newWidth = MAX2(1, srcWidth / 2) + 2 * border;
+      newHeight = MAX2(1, srcHeight / 2) + 2 * border;
+
+      if (newWidth == srcImage->Width && newHeight == srcImage->Height) {
+        break;
+      }
+
+      /* Create empty image */
+      _mesa_TexImage2D(GL_TEXTURE_2D, level, srcImage->InternalFormat,
+                      newWidth, newHeight, border,
+                      GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+      /* vertex positions */
+      {
+         verts[0].x = 0.0F;
+         verts[0].y = 0.0F;
+         verts[1].x = (GLfloat) newWidth;
+         verts[1].y = 0.0F;
+         verts[2].x = (GLfloat) newWidth;
+         verts[2].y = (GLfloat) newHeight;
+         verts[3].x = 0.0F;
+         verts[3].y = (GLfloat) newHeight;
+
+         /* upload new vertex data */
+         _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
+      }
+
+      /* limit sampling to src level */
+      _mesa_TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, srcLevel);
+      _mesa_TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, srcLevel);
+
+      /* Set to draw into the current level */
+      _mesa_FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+                                    GL_COLOR_ATTACHMENT0_EXT,
+                                    target,
+                                    texObj->Name,
+                                    level);
+
+      /* Choose to render to the color attachment. */
+      _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+
+      status = _mesa_CheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+      if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+         abort();
+         break;
+      }
+
+      _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
+   }
+
+   _mesa_meta_end(ctx);
+
+   _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilterSave);
+   _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilterSave);
+
+   /* restore (XXX add to meta_begin/end()? */
+   _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboSave);
+}