#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"
/**
+ * 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()
*/
struct blit_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.
+ * State for glBitmap()
*/
-struct temp_texture
+struct bitmap_state
{
- GLuint TexObj;
- GLenum Target; /**< GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE */
- 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;
+ struct temp_texture Tex; /**< separate texture from other meta ops */
+};
+
+
+/**
+ * State for _mesa_meta_generate_mipmap()
+ */
+struct gen_mipmap_state
+{
+ GLuint ArrayObj;
+ GLuint VBO;
+ GLuint FBO;
};
struct clear_state Clear; /**< For _mesa_meta_clear() */
struct copypix_state CopyPix; /**< For _mesa_meta_copy_pixels() */
struct drawpix_state DrawPix; /**< For _mesa_meta_draw_pixels() */
-
- /* other possible meta-ops:
- * glBitmap()
- * glGenerateMipmap()
- */
+ struct bitmap_state Bitmap; /**< For _mesa_meta_bitmap() */
+ struct gen_mipmap_state Mipmap; /**< For _mesa_meta_generate_mipmap() */
};
* still get freed by _mesa_free_context_data().
*/
+ /* the temporary texture */
_mesa_DeleteTextures(1, &meta->TempTex.TexObj);
/* glBlitFramebuffer */
_mesa_DeleteVertexArraysAPPLE(1, &meta->DrawPix.ArrayObj);
_mesa_DeletePrograms(1, &meta->DrawPix.DepthFP);
_mesa_DeletePrograms(1, &meta->DrawPix.StencilFP);
+
+ /* glBitmap */
+ _mesa_DeleteBuffersARB(1, & meta->Bitmap.VBO);
+ _mesa_DeleteVertexArraysAPPLE(1, &meta->Bitmap.ArrayObj);
+ _mesa_DeleteTextures(1, &meta->Bitmap.Tex.TexObj);
}
_mesa_free(ctx->Meta);
if (state & META_ALPHA_TEST) {
save->AlphaEnabled = ctx->Color.AlphaEnabled;
if (ctx->Color.AlphaEnabled)
- _mesa_Disable(GL_ALPHA_TEST);
+ _mesa_set_enable(ctx, GL_ALPHA_TEST, GL_FALSE);
}
if (state & META_BLEND) {
save->BlendEnabled = ctx->Color.BlendEnabled;
if (ctx->Color.BlendEnabled)
- _mesa_Disable(GL_BLEND);
+ _mesa_set_enable(ctx, GL_BLEND, GL_FALSE);
save->ColorLogicOpEnabled = ctx->Color.ColorLogicOpEnabled;
if (ctx->Color.ColorLogicOpEnabled)
- _mesa_Disable(GL_COLOR_LOGIC_OP);
+ _mesa_set_enable(ctx, GL_COLOR_LOGIC_OP, GL_FALSE);
}
if (state & META_COLOR_MASK) {
if (state & META_DEPTH_TEST) {
save->Depth = ctx->Depth; /* struct copy */
if (ctx->Depth.Test)
- _mesa_Disable(GL_DEPTH_TEST);
+ _mesa_set_enable(ctx, GL_DEPTH_TEST, GL_FALSE);
}
if (state & META_FOG) {
if (state & META_STENCIL_TEST) {
save->Stencil = ctx->Stencil; /* struct copy */
if (ctx->Stencil.Enabled)
- _mesa_Disable(GL_STENCIL_TEST);
+ _mesa_set_enable(ctx, GL_STENCIL_TEST, GL_FALSE);
/* NOTE: other stencil state not reset */
}
save->ClientActiveUnit = ctx->Array.ActiveTexture;
save->EnvMode = ctx->Texture.Unit[0].EnvMode;
+ if (ctx->Texture._EnabledUnits |
+ ctx->Texture._EnabledCoordUnits |
+ ctx->Texture._TexGenEnabled |
+ ctx->Texture._TexMatEnabled) {
+
/* Disable all texture units */
for (u = 0; u < ctx->Const.MaxTextureUnits; u++) {
save->TexEnabled[u] = ctx->Texture.Unit[u].Enabled;
_mesa_set_enable(ctx, GL_TEXTURE_GEN_Q, GL_FALSE);
}
}
+ }
/* save current texture objects for unit[0] only */
for (tgt = 0; tgt < NUM_TEXTURE_TARGETS; tgt++) {
/**
- * Return pointer to temp_texture info. This does some one-time init
- * if needed.
+ * One-time init for a temp_texture object.
+ * Choose tex target, compute max tex size, etc.
+ */
+static void
+init_temp_texture(GLcontext *ctx, struct temp_texture *tex)
+{
+ /* prefer texture rectangle */
+ if (ctx->Extensions.NV_texture_rectangle) {
+ tex->Target = GL_TEXTURE_RECTANGLE;
+ tex->MaxSize = ctx->Const.MaxTextureRectSize;
+ tex->NPOT = GL_TRUE;
+ }
+ else {
+ /* use 2D texture, NPOT if possible */
+ tex->Target = GL_TEXTURE_2D;
+ tex->MaxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
+ tex->NPOT = ctx->Extensions.ARB_texture_non_power_of_two;
+ }
+ tex->MinSize = 16; /* 16 x 16 at least */
+ assert(tex->MaxSize > 0);
+
+ _mesa_GenTextures(1, &tex->TexObj);
+ _mesa_BindTexture(tex->Target, tex->TexObj);
+}
+
+
+/**
+ * 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)
struct temp_texture *tex = &ctx->Meta->TempTex;
if (!tex->TexObj) {
- /* do one-time init */
+ init_temp_texture(ctx, tex);
+ }
- /* prefer texture rectangle */
- if (ctx->Extensions.NV_texture_rectangle) {
- tex->Target = GL_TEXTURE_RECTANGLE;
- tex->MaxSize = ctx->Const.MaxTextureRectSize;
- tex->NPOT = GL_TRUE;
- }
- else {
- /* use 2D texture, NPOT if possible */
- tex->Target = GL_TEXTURE_2D;
- tex->MaxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
- tex->NPOT = ctx->Extensions.ARB_texture_non_power_of_two;
- }
- assert(tex->MaxSize > 0);
+ return tex;
+}
- _mesa_GenTextures(1, &tex->TexObj);
- _mesa_BindTexture(tex->Target, tex->TexObj);
+
+/**
+ * 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;
{
GLboolean newTex = GL_FALSE;
+ ASSERT(width <= tex->MaxSize);
+ ASSERT(height <= tex->MaxSize);
+
if (width > tex->Width ||
height > tex->Height ||
intFormat != tex->IntFormat) {
if (tex->NPOT) {
/* use non-power of two size */
- tex->Width = width;
- tex->Height = height;
+ tex->Width = MAX2(tex->MinSize, width);
+ tex->Height = MAX2(tex->MinSize, height);
}
else {
/* find power of two size */
GLsizei w, h;
- w = h = 16;
+ w = h = tex->MinSize;
while (w < width)
w *= 2;
while (h < height)
_mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
}
- _mesa_Enable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_TRUE);
if (mask & GL_COLOR_BUFFER_BIT) {
setup_copypix_texture(tex, newTex, srcX, srcY, srcW, srcH,
/* XXX can't easily do stencil */
}
- _mesa_Disable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_FALSE);
_mesa_meta_end(ctx);
setup_copypix_texture(tex, newTex, srcX, srcY, width, height,
GL_RGBA, GL_NEAREST);
- _mesa_Enable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_TRUE);
/* draw textured quad */
_mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
- _mesa_Disable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_FALSE);
_mesa_meta_end(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;
/* set given unpack params */
ctx->Unpack = *unpack;
- _mesa_Enable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_TRUE);
if (_mesa_is_stencil_format(format)) {
/* Drawing stencil */
_mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
- _mesa_Disable(tex->Target);
+ _mesa_set_enable(ctx, tex->Target, GL_FALSE);
/* restore unpack params */
ctx->Unpack = unpackSave;
_mesa_meta_end(ctx);
}
+
+
+/**
+ * Do glBitmap with a alpha texture quad. Use the alpha test to
+ * cull the 'off' bits. If alpha test is already enabled, fall back
+ * to swrast (should be a rare case).
+ * A bitmap cache as in the gallium/mesa state tracker would
+ * improve performance a lot.
+ */
+void
+_mesa_meta_bitmap(GLcontext *ctx,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ const struct gl_pixelstore_attrib *unpack,
+ const GLubyte *bitmap1)
+{
+ struct bitmap_state *bitmap = &ctx->Meta->Bitmap;
+ 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 */
+ GLboolean newTex;
+ GLubyte *bitmap8;
+
+ /*
+ * Check if swrast fallback is needed.
+ */
+ if (ctx->_ImageTransferState ||
+ ctx->Color.AlphaEnabled ||
+ ctx->Fog.Enabled ||
+ ctx->Texture._EnabledUnits ||
+ width > tex->MaxSize ||
+ height > tex->MaxSize) {
+ _swrast_Bitmap(ctx, x, y, width, height, unpack, bitmap1);
+ return;
+ }
+
+ /* Most GL state applies to glBitmap (like blending, stencil, etc),
+ * but a there's a few things we need to override:
+ */
+ _mesa_meta_begin(ctx, (META_ALPHA_TEST |
+ META_PIXEL_STORE |
+ META_RASTERIZATION |
+ META_SHADER |
+ META_TEXTURE |
+ META_TRANSFORM |
+ META_VERTEX |
+ META_VIEWPORT));
+
+ if (bitmap->ArrayObj == 0) {
+ /* one-time setup */
+
+ /* create vertex array object */
+ _mesa_GenVertexArraysAPPLE(1, &bitmap->ArrayObj);
+ _mesa_BindVertexArrayAPPLE(bitmap->ArrayObj);
+
+ /* create vertex array buffer */
+ _mesa_GenBuffersARB(1, &bitmap->VBO);
+ _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, bitmap->VBO);
+ _mesa_BufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(verts),
+ NULL, GL_DYNAMIC_DRAW_ARB);
+
+ /* setup vertex arrays */
+ _mesa_VertexPointer(3, GL_FLOAT, sizeof(verts[0]),
+ (void *) (0 * sizeof(GLfloat)));
+ _mesa_TexCoordPointer(2, GL_FLOAT, sizeof(verts[0]),
+ (void *) (3 * sizeof(GLfloat)));
+ _mesa_ColorPointer(4, GL_FLOAT, sizeof(verts[0]),
+ (void *) (5 * sizeof(GLfloat)));
+
+ _mesa_EnableClientState(GL_VERTEX_ARRAY);
+ _mesa_EnableClientState(GL_TEXTURE_COORD_ARRAY);
+ _mesa_EnableClientState(GL_COLOR_ARRAY);
+ }
+ else {
+ _mesa_BindVertexArray(bitmap->ArrayObj);
+ _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, bitmap->VBO);
+ }
+
+ newTex = alloc_texture(tex, width, height, texIntFormat);
+
+ /* vertex positions, texcoords, colors (after texture allocation!) */
+ {
+ const GLfloat x0 = (GLfloat) x;
+ const GLfloat y0 = (GLfloat) y;
+ const GLfloat x1 = (GLfloat) (x + width);
+ const GLfloat y1 = (GLfloat) (y + height);
+ const GLfloat z = ctx->Current.RasterPos[2];
+ GLuint i;
+
+ verts[0][0] = x0;
+ verts[0][1] = y0;
+ verts[0][2] = z;
+ verts[0][3] = 0.0F;
+ verts[0][4] = 0.0F;
+ verts[1][0] = x1;
+ verts[1][1] = y0;
+ verts[1][2] = z;
+ verts[1][3] = tex->Sright;
+ verts[1][4] = 0.0F;
+ verts[2][0] = x1;
+ verts[2][1] = y1;
+ verts[2][2] = z;
+ verts[2][3] = tex->Sright;
+ verts[2][4] = tex->Ttop;
+ verts[3][0] = x0;
+ verts[3][1] = y1;
+ verts[3][2] = z;
+ verts[3][3] = 0.0F;
+ verts[3][4] = tex->Ttop;
+
+ for (i = 0; i < 4; i++) {
+ verts[i][5] = ctx->Current.RasterColor[0];
+ verts[i][6] = ctx->Current.RasterColor[1];
+ verts[i][7] = ctx->Current.RasterColor[2];
+ verts[i][8] = ctx->Current.RasterColor[3];
+ }
+
+ /* upload new vertex data */
+ _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
+ }
+
+ bitmap1 = _mesa_map_pbo_source(ctx, &unpackSave, bitmap1);
+ if (!bitmap1)
+ return;
+
+ bitmap8 = (GLubyte *) _mesa_calloc(width * height);
+ if (bitmap8) {
+ _mesa_expand_bitmap(width, height, &unpackSave, bitmap1,
+ bitmap8, width, 0xff);
+
+ _mesa_set_enable(ctx, tex->Target, GL_TRUE);
+
+ _mesa_set_enable(ctx, GL_ALPHA_TEST, GL_TRUE);
+ _mesa_AlphaFunc(GL_GREATER, 0.0);
+
+ setup_drawpix_texture(tex, newTex, texIntFormat, width, height,
+ GL_ALPHA, GL_UNSIGNED_BYTE, bitmap8);
+
+ _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ _mesa_set_enable(ctx, tex->Target, GL_FALSE);
+
+ _mesa_free(bitmap8);
+ }
+
+ _mesa_unmap_pbo_source(ctx, &unpackSave);
+
+ _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);
+}