diff options
Diffstat (limited to 'src/mesa/state_tracker')
-rw-r--r-- | src/mesa/state_tracker/st_cb_texture.c | 266 | ||||
-rw-r--r-- | src/mesa/state_tracker/st_format.c | 20 | ||||
-rw-r--r-- | src/mesa/state_tracker/st_format.h | 4 |
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 */ |