aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/mesa/drivers/common/meta.c
diff options
context:
space:
mode:
Diffstat (limited to 'mesalib/src/mesa/drivers/common/meta.c')
-rw-r--r--mesalib/src/mesa/drivers/common/meta.c492
1 files changed, 400 insertions, 92 deletions
diff --git a/mesalib/src/mesa/drivers/common/meta.c b/mesalib/src/mesa/drivers/common/meta.c
index ad04d369f..2ebcd35bd 100644
--- a/mesalib/src/mesa/drivers/common/meta.c
+++ b/mesalib/src/mesa/drivers/common/meta.c
@@ -59,6 +59,7 @@
#include "main/stencil.h"
#include "main/texobj.h"
#include "main/texenv.h"
+#include "main/texgetimage.h"
#include "main/teximage.h"
#include "main/texparam.h"
#include "main/texstate.h"
@@ -259,6 +260,18 @@ struct gen_mipmap_state
GLuint FBO;
};
+
+/**
+ * State for texture decompression
+ */
+struct decompress_state
+{
+ GLuint ArrayObj;
+ GLuint VBO, FBO, RBO;
+ GLint Width, Height;
+};
+
+
#define MAX_META_OPS_DEPTH 2
/**
* All per-context meta state.
@@ -278,6 +291,7 @@ struct gl_meta_state
struct drawpix_state DrawPix; /**< For _mesa_meta_DrawPixels() */
struct bitmap_state Bitmap; /**< For _mesa_meta_Bitmap() */
struct gen_mipmap_state Mipmap; /**< For _mesa_meta_GenerateMipmap() */
+ struct decompress_state Decompress; /**< For texture decompression */
};
@@ -2477,6 +2491,160 @@ _mesa_meta_check_generate_mipmap_fallback(struct gl_context *ctx, GLenum target,
/**
+ * Compute the texture coordinates for the four vertices of a quad for
+ * drawing a 2D texture image or slice of a cube/3D texture.
+ * \param faceTarget GL_TEXTURE_1D/2D/3D or cube face name
+ * \param slice slice of a 1D/2D array texture or 3D texture
+ * \param width width of the texture image
+ * \param height height of the texture image
+ * \param coords0/1/2/3 returns the computed texcoords
+ */
+static void
+setup_texture_coords(GLenum faceTarget,
+ GLint slice,
+ GLint width,
+ GLint height,
+ GLfloat coords0[3],
+ GLfloat coords1[3],
+ GLfloat coords2[3],
+ GLfloat coords3[3])
+{
+ static const GLfloat st[4][2] = {
+ {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}
+ };
+ GLuint i;
+ GLfloat r;
+
+ switch (faceTarget) {
+ case GL_TEXTURE_1D:
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_3D:
+ case GL_TEXTURE_2D_ARRAY:
+ if (faceTarget == GL_TEXTURE_3D)
+ r = 1.0F / slice;
+ else if (faceTarget == GL_TEXTURE_2D_ARRAY)
+ r = slice;
+ else
+ r = 0.0F;
+ coords0[0] = 0.0F; /* s */
+ coords0[1] = 0.0F; /* t */
+ coords0[2] = r; /* r */
+ coords1[0] = 1.0F;
+ coords1[1] = 0.0F;
+ coords1[2] = r;
+ coords2[0] = 1.0F;
+ coords2[1] = 1.0F;
+ coords2[2] = r;
+ coords3[0] = 0.0F;
+ coords3[1] = 1.0F;
+ coords3[2] = r;
+ break;
+ case GL_TEXTURE_RECTANGLE_ARB:
+ coords0[0] = 0.0F; /* s */
+ coords0[1] = 0.0F; /* t */
+ coords0[2] = 0.0F; /* r */
+ coords1[0] = width;
+ coords1[1] = 0.0F;
+ coords1[2] = 0.0F;
+ coords2[0] = width;
+ coords2[1] = height;
+ coords2[2] = 0.0F;
+ coords3[0] = 0.0F;
+ coords3[1] = height;
+ coords3[2] = 0.0F;
+ break;
+ case GL_TEXTURE_1D_ARRAY:
+ coords0[0] = 0.0F; /* s */
+ coords0[1] = slice; /* t */
+ coords0[2] = 0.0F; /* r */
+ coords1[0] = 1.0f;
+ coords1[1] = slice;
+ coords1[2] = 0.0F;
+ coords2[0] = 1.0F;
+ coords2[1] = slice;
+ coords2[2] = 0.0F;
+ coords3[0] = 0.0F;
+ coords3[1] = slice;
+ coords3[2] = 0.0F;
+ break;
+
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ /* loop over quad verts */
+ for (i = 0; i < 4; i++) {
+ /* Compute sc = +/-scale and tc = +/-scale.
+ * Not +/-1 to avoid cube face selection ambiguity near the edges,
+ * though that can still sometimes happen with this scale factor...
+ */
+ const GLfloat scale = 0.9999f;
+ const GLfloat sc = (2.0f * st[i][0] - 1.0f) * scale;
+ const GLfloat tc = (2.0f * st[i][1] - 1.0f) * scale;
+ GLfloat *coord;
+
+ switch (i) {
+ case 0:
+ coord = coords0;
+ break;
+ case 1:
+ coord = coords1;
+ break;
+ case 2:
+ coord = coords2;
+ break;
+ case 3:
+ coord = coords3;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (faceTarget) {
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ coord[0] = 1.0f;
+ coord[1] = -tc;
+ coord[2] = -sc;
+ break;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ coord[0] = -1.0f;
+ coord[1] = -tc;
+ coord[2] = sc;
+ break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ coord[0] = sc;
+ coord[1] = 1.0f;
+ coord[2] = tc;
+ break;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ coord[0] = sc;
+ coord[1] = -1.0f;
+ coord[2] = -tc;
+ break;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ coord[0] = sc;
+ coord[1] = -tc;
+ coord[2] = 1.0f;
+ break;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ coord[0] = -sc;
+ coord[1] = -tc;
+ coord[2] = -1.0f;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ default:
+ assert(0 && "unexpected target in meta setup_texture_coords()");
+ }
+}
+
+
+/**
* Called via ctx->Driver.GenerateMipmap()
* Note: We don't yet support 3D textures, 1D/2D array textures or texture
* borders.
@@ -2487,7 +2655,7 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
{
struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
struct vertex {
- GLfloat x, y, s, t, r;
+ GLfloat x, y, tex[3];
};
struct vertex verts[4];
const GLuint baseLevel = texObj->BaseLevel;
@@ -2503,7 +2671,8 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
const GLuint original_active_unit = ctx->Texture.CurrentUnit;
GLenum faceTarget;
GLuint dstLevel;
- GLuint border = 0;
+ const GLuint border = 0;
+ const GLint slice = 0;
if (_mesa_meta_check_generate_mipmap_fallback(ctx, target, texObj)) {
_mesa_generate_mipmap(ctx, target, texObj);
@@ -2539,7 +2708,7 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
/* setup vertex arrays */
_mesa_VertexPointer(2, GL_FLOAT, sizeof(struct vertex), OFFSET(x));
- _mesa_TexCoordPointer(3, GL_FLOAT, sizeof(struct vertex), OFFSET(s));
+ _mesa_TexCoordPointer(3, GL_FLOAT, sizeof(struct vertex), OFFSET(tex));
_mesa_EnableClientState(GL_VERTEX_ARRAY);
_mesa_EnableClientState(GL_TEXTURE_COORD_ARRAY);
}
@@ -2562,98 +2731,27 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
_mesa_set_enable(ctx, target, GL_TRUE);
- /* setup texcoords once (XXX what about border?) */
- switch (faceTarget) {
- case GL_TEXTURE_1D:
- 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[1].r = 0.0F;
- verts[2].s = 1.0F;
- verts[2].t = 1.0F;
- verts[2].r = 0.0F;
- verts[3].s = 0.0F;
- verts[3].t = 1.0F;
- verts[3].r = 0.0F;
- break;
- case GL_TEXTURE_3D:
- abort();
- break;
- default:
- /* cube face */
- {
- static const GLfloat st[4][2] = {
- {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}
- };
- GLuint i;
-
- /* loop over quad verts */
- for (i = 0; i < 4; i++) {
- /* Compute sc = +/-scale and tc = +/-scale.
- * Not +/-1 to avoid cube face selection ambiguity near the edges,
- * though that can still sometimes happen with this scale factor...
- */
- const GLfloat scale = 0.9999f;
- const GLfloat sc = (2.0f * st[i][0] - 1.0f) * scale;
- const GLfloat tc = (2.0f * st[i][1] - 1.0f) * scale;
-
- switch (faceTarget) {
- case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- verts[i].s = 1.0f;
- verts[i].t = -tc;
- verts[i].r = -sc;
- break;
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- verts[i].s = -1.0f;
- verts[i].t = -tc;
- verts[i].r = sc;
- break;
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- verts[i].s = sc;
- verts[i].t = 1.0f;
- verts[i].r = tc;
- break;
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- verts[i].s = sc;
- verts[i].t = -1.0f;
- verts[i].r = -tc;
- break;
- case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- verts[i].s = sc;
- verts[i].t = -tc;
- verts[i].r = 1.0f;
- break;
- case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- verts[i].s = -sc;
- verts[i].t = -tc;
- verts[i].r = -1.0f;
- break;
- default:
- assert(0);
- }
- }
- }
- }
-
- _mesa_set_enable(ctx, target, GL_TRUE);
+ /* setup texcoords (XXX what about border?) */
+ setup_texture_coords(faceTarget,
+ 0.0, 0.0, /* width, height never used here */
+ slice,
+ verts[0].tex,
+ verts[1].tex,
+ verts[2].tex,
+ verts[3].tex);
/* setup vertex positions */
- {
- verts[0].x = 0.0F;
- verts[0].y = 0.0F;
- verts[1].x = 1.0F;
- verts[1].y = 0.0F;
- verts[2].x = 1.0F;
- verts[2].y = 1.0F;
- verts[3].x = 0.0F;
- verts[3].y = 1.0F;
+ verts[0].x = 0.0F;
+ verts[0].y = 0.0F;
+ verts[1].x = 1.0F;
+ verts[1].y = 0.0F;
+ verts[2].x = 1.0F;
+ verts[2].y = 1.0F;
+ verts[3].x = 0.0F;
+ verts[3].y = 1.0F;
- /* upload new vertex data */
- _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
- }
+ /* upload new vertex data */
+ _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
/* setup projection matrix */
_mesa_MatrixMode(GL_PROJECTION);
@@ -2939,3 +3037,213 @@ _mesa_meta_CopyTexSubImage3D(struct gl_context *ctx, GLenum target, GLint level,
copy_tex_sub_image(ctx, 3, target, level, xoffset, yoffset, zoffset,
x, y, width, height);
}
+
+
+/**
+ * Decompress a texture image by drawing a quad with the compressed
+ * texture and reading the pixels out of the color buffer.
+ * \param slice which slice of a 3D texture or layer of a 1D/2D texture
+ * \param destFormat format, ala glReadPixels
+ * \param destType type, ala glReadPixels
+ * \param dest destination buffer
+ * \param destRowLength dest image rowLength (ala GL_PACK_ROW_LENGTH)
+ */
+static void
+decompress_texture_image(struct gl_context *ctx,
+ struct gl_texture_image *texImage,
+ GLuint slice,
+ GLenum destFormat, GLenum destType,
+ GLvoid *dest, GLint destRowLength)
+{
+ struct decompress_state *decompress = &ctx->Meta->Decompress;
+ struct gl_texture_object *texObj = texImage->TexObject;
+ const GLint width = texImage->Width;
+ const GLint height = texImage->Height;
+ const GLenum target = texObj->Target;
+ GLenum faceTarget;
+ struct vertex {
+ GLfloat x, y, tex[3];
+ };
+ struct vertex verts[4];
+ GLuint fboDrawSave, fboReadSave;
+
+ if (slice > 0) {
+ assert(target == GL_TEXTURE_3D ||
+ target == GL_TEXTURE_2D_ARRAY);
+ }
+
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + texImage->Face;
+ }
+ else {
+ faceTarget = target;
+ }
+
+ /* save fbo bindings (not saved by _mesa_meta_begin()) */
+ fboDrawSave = ctx->DrawBuffer->Name;
+ fboReadSave = ctx->ReadBuffer->Name;
+
+ _mesa_meta_begin(ctx, MESA_META_ALL);
+
+ /* Create/bind FBO/renderbuffer */
+ if (decompress->FBO == 0) {
+ _mesa_GenFramebuffersEXT(1, &decompress->FBO);
+ _mesa_GenRenderbuffersEXT(1, &decompress->RBO);
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, decompress->FBO);
+ _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, decompress->RBO);
+ _mesa_FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT,
+ decompress->RBO);
+ }
+ else {
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, decompress->FBO);
+ }
+
+ /* alloc dest surface */
+ if (width != decompress->Width || height != decompress->Height) {
+ _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA,
+ width, height);
+ decompress->Width = width;
+ decompress->Height = height;
+ }
+
+ /* setup VBO data */
+ if (decompress->ArrayObj == 0) {
+ /* create vertex array object */
+ _mesa_GenVertexArrays(1, &decompress->ArrayObj);
+ _mesa_BindVertexArray(decompress->ArrayObj);
+
+ /* create vertex array buffer */
+ _mesa_GenBuffersARB(1, &decompress->VBO);
+ _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, decompress->VBO);
+ _mesa_BufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(verts),
+ NULL, GL_DYNAMIC_DRAW_ARB);
+
+ /* setup vertex arrays */
+ _mesa_VertexPointer(2, GL_FLOAT, sizeof(struct vertex), OFFSET(x));
+ _mesa_TexCoordPointer(3, GL_FLOAT, sizeof(struct vertex), OFFSET(tex));
+ _mesa_EnableClientState(GL_VERTEX_ARRAY);
+ _mesa_EnableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+ else {
+ _mesa_BindVertexArray(decompress->ArrayObj);
+ _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, decompress->VBO);
+ }
+
+ setup_texture_coords(faceTarget, slice, width, height,
+ verts[0].tex,
+ verts[1].tex,
+ verts[2].tex,
+ verts[3].tex);
+
+ /* setup vertex positions */
+ verts[0].x = 0.0F;
+ verts[0].y = 0.0F;
+ verts[1].x = width;
+ verts[1].y = 0.0F;
+ verts[2].x = width;
+ verts[2].y = height;
+ verts[3].x = 0.0F;
+ verts[3].y = height;
+
+ /* upload new vertex data */
+ _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
+
+ /* setup texture state */
+ _mesa_BindTexture(target, texObj->Name);
+ _mesa_Enable(target);
+
+ {
+ /* save texture object state */
+ const GLenum minFilterSave = texObj->Sampler.MinFilter;
+ const GLenum magFilterSave = texObj->Sampler.MagFilter;
+ const GLint baseLevelSave = texObj->BaseLevel;
+ const GLint maxLevelSave = texObj->MaxLevel;
+ const GLenum wrapSSave = texObj->Sampler.WrapS;
+ const GLenum wrapTSave = texObj->Sampler.WrapT;
+ const GLenum srgbSave = texObj->Sampler.sRGBDecode;
+
+ /* restrict sampling to the texture level of interest */
+ _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, texImage->Level);
+ _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, texImage->Level);
+ /* nearest filtering */
+ _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ /* No sRGB decode or encode.*/
+ if (ctx->Extensions.EXT_texture_sRGB_decode) {
+ _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT,
+ GL_SKIP_DECODE_EXT);
+ }
+ if (ctx->Extensions.EXT_framebuffer_sRGB) {
+ _mesa_Disable(GL_FRAMEBUFFER_SRGB_EXT);
+ }
+
+ /* render quad w/ texture into renderbuffer */
+ _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ /* Restore texture object state, the texture binding will
+ * be restored by _mesa_meta_end().
+ */
+ _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilterSave);
+ _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilterSave);
+ if (target != GL_TEXTURE_RECTANGLE_ARB) {
+ _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, baseLevelSave);
+ _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, maxLevelSave);
+ }
+ _mesa_TexParameteri(target, GL_TEXTURE_WRAP_S, wrapSSave);
+ _mesa_TexParameteri(target, GL_TEXTURE_WRAP_T, wrapTSave);
+ if (ctx->Extensions.EXT_texture_sRGB_decode) {
+ _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT, srgbSave);
+ }
+ }
+
+ /* read pixels from renderbuffer */
+ ctx->Pack.RowLength = destRowLength;
+ _mesa_ReadPixels(0, 0, width, height, destFormat, destType, dest);
+
+ _mesa_meta_end(ctx);
+
+ /* restore fbo bindings */
+ if (fboDrawSave == fboReadSave) {
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboDrawSave);
+ }
+ else {
+ _mesa_BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboDrawSave);
+ _mesa_BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fboReadSave);
+ }
+}
+
+
+/**
+ * This is just a wrapper around _mesa_get_tex_image() and
+ * decompress_texture_image(). Meta functions should not be directly called
+ * from core Mesa.
+ */
+void
+_mesa_meta_GetTexImage(struct gl_context *ctx, GLenum target, GLint level,
+ GLenum format, GLenum type, GLvoid *pixels,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+ /* We can only use the decompress-with-blit method here if the texels are
+ * unsigned, normalized values. We could handle signed and unnormalized
+ * with floating point renderbuffers...
+ */
+ if (_mesa_is_format_compressed(texImage->TexFormat) &&
+ _mesa_get_format_datatype(texImage->TexFormat)
+ == GL_UNSIGNED_NORMALIZED) {
+ const GLuint slice = 0; /* only 2D compressed textures for now */
+ /* Need to unlock the texture here to prevent deadlock... */
+ _mesa_unlock_texture(ctx, texObj);
+ decompress_texture_image(ctx, texImage, slice, format, type, pixels,
+ ctx->Pack.RowLength);
+ /* ... and relock it */
+ _mesa_lock_texture(ctx, texObj);
+ }
+ else {
+ _mesa_get_teximage(ctx, target, level, format, type, pixels,
+ texObj, texImage);
+ }
+}