summaryrefslogtreecommitdiffstats
path: root/src/mesa/state_tracker
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/state_tracker')
-rw-r--r--src/mesa/state_tracker/st_cb_texture.c266
-rw-r--r--src/mesa/state_tracker/st_format.c20
-rw-r--r--src/mesa/state_tracker/st_format.h4
3 files changed, 284 insertions, 6 deletions
diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
index 727432d7ded..98dc6ec74d1 100644
--- a/src/mesa/state_tracker/st_cb_texture.c
+++ b/src/mesa/state_tracker/st_cb_texture.c
@@ -26,6 +26,7 @@
**************************************************************************/
#include "main/mfeatures.h"
+#include "main/bufferobj.h"
#if FEATURE_convolve
#include "main/convolve.h"
#endif
@@ -55,6 +56,7 @@
#include "pipe/p_inlines.h"
#include "util/u_tile.h"
#include "util/u_blit.h"
+#include "util/u_surface.h"
#define DBG if (0) printf
@@ -110,6 +112,25 @@ compressed_num_bytes(GLuint mesaFormat)
}
+static GLboolean
+is_compressed_mesa_format(const struct gl_texture_format *format)
+{
+ switch (format->MesaFormat) {
+ case MESA_FORMAT_RGB_DXT1:
+ case MESA_FORMAT_RGBA_DXT1:
+ case MESA_FORMAT_RGBA_DXT3:
+ case MESA_FORMAT_RGBA_DXT5:
+ case MESA_FORMAT_SRGB_DXT1:
+ case MESA_FORMAT_SRGBA_DXT1:
+ case MESA_FORMAT_SRGBA_DXT3:
+ case MESA_FORMAT_SRGBA_DXT5:
+ return GL_TRUE;
+ default:
+ return GL_FALSE;
+ }
+}
+
+
/** called via ctx->Driver.NewTextureImage() */
static struct gl_texture_image *
st_NewTextureImage(GLcontext * ctx)
@@ -379,6 +400,110 @@ strip_texture_border(GLint border,
/**
+ * Try to do texture compression via rendering. If the Gallium driver
+ * can render into a compressed surface this will allow us to do texture
+ * compression.
+ * \return GL_TRUE for success, GL_FALSE for failure
+ */
+static GLboolean
+compress_with_blit(GLcontext * ctx,
+ GLenum target, GLint level,
+ GLint xoffset, GLint yoffset, GLint zoffset,
+ GLint width, GLint height, GLint depth,
+ GLenum format, GLenum type, const void *pixels,
+ const struct gl_pixelstore_attrib *unpack,
+ struct gl_texture_image *texImage)
+{
+ const GLuint dstImageOffsets[1] = {0};
+ struct st_texture_image *stImage = st_texture_image(texImage);
+ struct pipe_screen *screen = ctx->st->pipe->screen;
+ const GLuint face = _mesa_tex_target_to_face(target);
+ const struct gl_texture_format *mesa_format;
+ struct pipe_texture templ;
+ struct pipe_texture *src_tex;
+ struct pipe_surface *dst_surface;
+ struct pipe_transfer *tex_xfer;
+ void *map;
+
+
+ if (!stImage->pt) {
+ /* XXX: Can this happen? Should we assert? */
+ return GL_FALSE;
+ }
+
+ /* get destination surface (in the compressed texture) */
+ dst_surface = screen->get_tex_surface(screen, stImage->pt,
+ stImage->face, stImage->level, 0,
+ PIPE_BUFFER_USAGE_GPU_WRITE);
+ if (!dst_surface) {
+ /* can't render into this format (or other problem) */
+ return GL_FALSE;
+ }
+
+ /* Choose format for the temporary RGBA texture image.
+ */
+ mesa_format = st_ChooseTextureFormat(ctx, GL_RGBA, format, type);
+ assert(mesa_format);
+ if (!mesa_format)
+ return GL_FALSE;
+
+ /* Create the temporary source texture
+ */
+ memset(&templ, 0, sizeof(templ));
+ templ.target = PIPE_TEXTURE_2D;
+ templ.format = st_mesa_format_to_pipe_format(mesa_format->MesaFormat);
+ pf_get_block(templ.format, &templ.block);
+ templ.width[0] = width;
+ templ.height[0] = height;
+ templ.depth[0] = 1;
+ templ.last_level = 0;
+ templ.tex_usage = PIPE_TEXTURE_USAGE_SAMPLER;
+ src_tex = screen->texture_create(screen, &templ);
+
+ if (!src_tex)
+ return GL_FALSE;
+
+ /* Put user's tex data into the temporary texture
+ */
+ tex_xfer = screen->get_tex_transfer(screen, src_tex,
+ face, level, 0,
+ PIPE_TRANSFER_WRITE,
+ 0, 0, width, height); /* x, y, w, h */
+ map = screen->transfer_map(screen, tex_xfer);
+
+ mesa_format->StoreImage(ctx, 2, GL_RGBA, mesa_format,
+ map, /* dest ptr */
+ 0, 0, 0, /* dest x/y/z offset */
+ tex_xfer->stride, /* dest row stride (bytes) */
+ dstImageOffsets, /* image offsets (for 3D only) */
+ width, height, 1, /* size */
+ format, type, /* source format/type */
+ pixels, /* source data */
+ unpack); /* source data packing */
+
+ screen->transfer_unmap(screen, tex_xfer);
+ screen->tex_transfer_destroy(tex_xfer);
+
+ /* copy / compress image */
+ util_blit_pixels_tex(ctx->st->blit,
+ src_tex, /* pipe_texture (src) */
+ 0, 0, /* src x0, y0 */
+ width, height, /* src x1, y1 */
+ dst_surface, /* pipe_surface (dst) */
+ xoffset, yoffset, /* dst x0, y0 */
+ xoffset + width, /* dst x1 */
+ yoffset + height, /* dst y1 */
+ 0.0, /* z */
+ PIPE_TEX_MIPFILTER_NEAREST);
+
+ pipe_surface_reference(&dst_surface, NULL);
+ pipe_texture_reference(&src_tex, NULL);
+
+ return GL_TRUE;
+}
+
+
+/**
* Do glTexImage1/2/3D().
*/
static void
@@ -392,8 +517,9 @@ st_TexImage(GLcontext * ctx,
const struct gl_pixelstore_attrib *unpack,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage,
- GLsizei imageSize, GLboolean compressed)
+ GLsizei imageSize, GLboolean compressed_src)
{
+ struct pipe_screen *screen = ctx->st->pipe->screen;
struct st_texture_object *stObj = st_texture_object(texObj);
struct st_texture_image *stImage = st_texture_image(texImage);
GLint postConvWidth, postConvHeight;
@@ -522,7 +648,7 @@ st_TexImage(GLcontext * ctx,
* the expectation that the texture will be set up but nothing
* more will be done. This is where those calls return:
*/
- if (compressed) {
+ if (compressed_src) {
pixels = _mesa_validate_pbo_compressed_teximage(ctx, imageSize, pixels,
unpack,
"glCompressedTexImage");
@@ -535,6 +661,21 @@ st_TexImage(GLcontext * ctx,
if (!pixels)
return;
+ /* See if we can do texture compression with a blit/render.
+ */
+ if (!compressed_src &&
+ !ctx->Mesa_DXTn &&
+ is_compressed_mesa_format(texImage->TexFormat) &&
+ screen->is_format_supported(screen,
+ stImage->pt->format,
+ stImage->pt->target,
+ PIPE_TEXTURE_USAGE_RENDER_TARGET, 0)) {
+ if (compress_with_blit(ctx, target, level, 0, 0, 0, width, height, depth,
+ format, type, pixels, unpack, texImage)) {
+ return;
+ }
+ }
+
if (stImage->pt) {
texImage->Data = st_texture_image_map(ctx->st, stImage, 0,
PIPE_TRANSFER_WRITE, 0, 0,
@@ -570,7 +711,7 @@ st_TexImage(GLcontext * ctx,
* the blitter to copy. Or, use the hardware to do the format
* conversion and copy:
*/
- if (compressed) {
+ if (compressed_src) {
memcpy(texImage->Data, pixels, imageSize);
}
else {
@@ -607,7 +748,7 @@ st_TexImage(GLcontext * ctx,
_mesa_unmap_teximage_pbo(ctx, unpack);
- if (stImage->pt) {
+ if (stImage->pt && texImage->Data) {
st_texture_image_unmap(ctx->st, stImage);
texImage->Data = NULL;
}
@@ -678,6 +819,90 @@ st_CompressedTexImage2D(GLcontext *ctx, GLenum target, GLint level,
}
+
+/**
+ * glGetTexImage() helper: decompress a compressed texture by rendering
+ * a textured quad. Store the results in the user's buffer.
+ */
+static void
+decompress_with_blit(GLcontext * ctx, GLenum target, GLint level,
+ GLenum format, GLenum type, GLvoid *pixels,
+ struct gl_texture_object *texObj,
+ struct gl_texture_image *texImage)
+{
+ struct pipe_screen *screen = ctx->st->pipe->screen;
+ struct st_texture_image *stImage = st_texture_image(texImage);
+ const GLuint width = texImage->Width;
+ const GLuint height = texImage->Height;
+ struct pipe_surface *dst_surface;
+ struct pipe_texture *dst_texture;
+ struct pipe_transfer *tex_xfer;
+
+ /* create temp / dest surface */
+ if (!util_create_rgba_surface(screen, width, height,
+ &dst_texture, &dst_surface)) {
+ _mesa_problem(ctx, "util_create_rgba_surface() failed "
+ "in decompress_with_blit()");
+ return;
+ }
+
+ /* blit/render/decompress */
+ util_blit_pixels_tex(ctx->st->blit,
+ stImage->pt, /* pipe_texture (src) */
+ 0, 0, /* src x0, y0 */
+ width, height, /* src x1, y1 */
+ dst_surface, /* pipe_surface (dst) */
+ 0, 0, /* dst x0, y0 */
+ width, height, /* dst x1, y1 */
+ 0.0, /* z */
+ PIPE_TEX_MIPFILTER_NEAREST);
+
+ /* map the dst_surface so we can read from it */
+ tex_xfer = screen->get_tex_transfer(screen, dst_texture, 0, 0, 0,
+ PIPE_TRANSFER_READ,
+ 0, 0, width, height);
+
+ pixels = _mesa_map_readpix_pbo(ctx, &ctx->Pack, pixels);
+
+ /* copy/pack data into user buffer */
+ if (st_equal_formats(stImage->pt->format, format, type)) {
+ /* memcpy */
+ const uint bytesPerRow = width * pf_get_size(stImage->pt->format);
+ ubyte *map = screen->transfer_map(screen, tex_xfer);
+ GLuint row;
+ for (row = 0; row < height; row++) {
+ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width,
+ height, format, type, row, 0);
+ memcpy(dest, map, bytesPerRow);
+ map += tex_xfer->stride;
+ }
+ screen->transfer_unmap(screen, tex_xfer);
+ }
+ else {
+ /* format translation via floats */
+ GLuint row;
+ for (row = 0; row < height; row++) {
+ const GLbitfield transferOps = 0x0; /* bypassed for glGetTexImage() */
+ GLfloat rgba[4 * MAX_WIDTH];
+ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width,
+ height, format, type, row, 0);
+
+ /* get float[4] rgba row from surface */
+ pipe_get_tile_rgba(tex_xfer, 0, row, width, 1, rgba);
+
+ _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format,
+ type, dest, &ctx->Pack, transferOps);
+ }
+ }
+
+ _mesa_unmap_readpix_pbo(ctx, &ctx->Pack);
+
+ /* destroy the temp / dest surface */
+ util_destroy_rgba_surface(dst_texture, dst_surface);
+}
+
+
+
/**
* Need to map texture image into memory before copying image data,
* then unmap it.
@@ -686,7 +911,7 @@ static void
st_get_tex_image(GLcontext * ctx, GLenum target, GLint level,
GLenum format, GLenum type, GLvoid * pixels,
struct gl_texture_object *texObj,
- struct gl_texture_image *texImage, GLboolean compressed)
+ struct gl_texture_image *texImage, GLboolean compressed_dst)
{
struct st_texture_image *stImage = st_texture_image(texImage);
const GLuint dstImageStride =
@@ -695,6 +920,18 @@ st_get_tex_image(GLcontext * ctx, GLenum target, GLint level,
GLuint depth, i;
GLubyte *dest;
+ if (stImage->pt &&
+ pf_is_compressed(stImage->pt->format) &&
+ !compressed_dst) {
+ /* Need to decompress the texture.
+ * We'll do this by rendering a textured quad.
+ * Note that we only expect RGBA formats (no Z/depth formats).
+ */
+ decompress_with_blit(ctx, target, level, format, type, pixels,
+ texObj, texImage);
+ return;
+ }
+
/* Map */
if (stImage->pt) {
/* Image is stored in hardware format in a buffer managed by the
@@ -728,7 +965,7 @@ st_get_tex_image(GLcontext * ctx, GLenum target, GLint level,
dest = (GLubyte *) pixels;
for (i = 0; i < depth; i++) {
- if (compressed) {
+ if (compressed_dst) {
_mesa_get_compressed_teximage(ctx, target, level, dest,
texObj, texImage);
}
@@ -791,6 +1028,7 @@ st_TexSubimage(GLcontext *ctx, GLint dims, GLenum target, GLint level,
struct gl_texture_object *texObj,
struct gl_texture_image *texImage)
{
+ struct pipe_screen *screen = ctx->st->pipe->screen;
struct st_texture_image *stImage = st_texture_image(texImage);
GLuint dstRowStride;
const GLuint srcImageStride =
@@ -808,6 +1046,22 @@ st_TexSubimage(GLcontext *ctx, GLint dims, GLenum target, GLint level,
if (!pixels)
return;
+ /* See if we can do texture compression with a blit/render.
+ */
+ if (!ctx->Mesa_DXTn &&
+ is_compressed_mesa_format(texImage->TexFormat) &&
+ screen->is_format_supported(screen,
+ stImage->pt->format,
+ stImage->pt->target,
+ PIPE_TEXTURE_USAGE_RENDER_TARGET, 0)) {
+ if (compress_with_blit(ctx, target, level,
+ xoffset, yoffset, zoffset,
+ width, height, depth,
+ format, type, pixels, packing, texImage)) {
+ return;
+ }
+ }
+
/* Map buffer if necessary. Need to lock to prevent other contexts
* from uploading the buffer under us.
*/
diff --git a/src/mesa/state_tracker/st_format.c b/src/mesa/state_tracker/st_format.c
index 9e2d60c9269..d507e3e58dd 100644
--- a/src/mesa/state_tracker/st_format.c
+++ b/src/mesa/state_tracker/st_format.c
@@ -716,3 +716,23 @@ st_ChooseTextureFormat(GLcontext *ctx, GLint internalFormat,
return translate_gallium_format_to_mesa_format(pFormat);
}
+
+
+/**
+ * Test if a gallium format is equivalent to a GL format/type.
+ */
+GLboolean
+st_equal_formats(enum pipe_format pFormat, GLenum format, GLenum type)
+{
+ switch (pFormat) {
+ case PIPE_FORMAT_R8G8B8A8_UNORM:
+ return format == GL_RGBA && type == GL_UNSIGNED_BYTE;
+ case PIPE_FORMAT_B8G8R8A8_UNORM:
+ return format == GL_BGRA && type == GL_UNSIGNED_BYTE;
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ return format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5;
+ /* XXX more combos... */
+ default:
+ return GL_FALSE;
+ }
+}
diff --git a/src/mesa/state_tracker/st_format.h b/src/mesa/state_tracker/st_format.h
index 3f5ac3201b1..7bbbe2d5704 100644
--- a/src/mesa/state_tracker/st_format.h
+++ b/src/mesa/state_tracker/st_format.h
@@ -76,4 +76,8 @@ st_ChooseTextureFormat(GLcontext * ctx, GLint internalFormat,
GLenum format, GLenum type);
+extern GLboolean
+st_equal_formats(enum pipe_format pFormat, GLenum format, GLenum type);
+
+
#endif /* ST_CB_TEXIMAGE_H */