diff options
author | Marek Olšák <[email protected]> | 2013-02-14 01:03:55 +0100 |
---|---|---|
committer | Marek Olšák <[email protected]> | 2013-02-18 17:57:41 +0100 |
commit | 0a1479c829ed34a65e60c6619a8164e1b079aaee (patch) | |
tree | 35ccf42db1ba7490db4442fc3324ccf93faab30e /src/mesa/state_tracker | |
parent | a6e0ac95716ca933558c70e45798ddc67e6ad5ad (diff) |
st/mesa: implement blit-based TexImage and TexSubImage
A temporary texture is created such that it matches the format and type
combination and pixels are copied to it using memcpy. Then the blit is used to
copy the temporary texture to the texture image being modified by TexImage or
TexSubImage. The blit takes care of the format and type conversion and
swizzling. The result is a very fast texture upload involving as little CPU
as possible.
This improves performance in apps which upload textures during rendering.
An example is the Wine OpenGL backend for DirectDraw, which I used to test
the game StarCraft. Profiling had shown that TexSubImage was taking 50% of
CPU time without this patch, which was the main motivation for this work, and
now TexSubImage only takes 14% of CPU time. I had to underclock my CPU to see
any difference in the game and this patch does make the game a lot faster
if the CPU is slow (or using the powersave cpufreq profile).
Reviewed-by: Brian Paul <[email protected]>
Diffstat (limited to 'src/mesa/state_tracker')
-rw-r--r-- | src/mesa/state_tracker/st_cb_texture.c | 218 |
1 files changed, 216 insertions, 2 deletions
diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c index 280b0e82b15..b376f23142a 100644 --- a/src/mesa/state_tracker/st_cb_texture.c +++ b/src/mesa/state_tracker/st_cb_texture.c @@ -586,13 +586,227 @@ get_blit_mask(GLenum srcFormat, GLenum dstFormat) static void +st_TexSubImage(struct gl_context *ctx, GLuint dims, + struct gl_texture_image *texImage, + 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 st_context *st = st_context(ctx); + struct st_texture_image *stImage = st_texture_image(texImage); + struct st_texture_object *stObj = st_texture_object(texImage->TexObject); + struct pipe_context *pipe = st->pipe; + struct pipe_screen *screen = pipe->screen; + struct pipe_resource *dst = stImage->pt; + struct pipe_resource *src = NULL; + struct pipe_resource src_templ; + struct pipe_transfer *transfer; + struct pipe_blit_info blit; + enum pipe_format src_format, dst_format; + gl_format mesa_src_format; + GLenum gl_target = texImage->TexObject->Target; + unsigned bind; + GLubyte *map; + + if (!dst) { + goto fallback; + } + + /* XXX Fallback for depth-stencil formats due to an incomplete stencil + * blit implementation in some drivers. */ + if (format == GL_DEPTH_STENCIL) { + goto fallback; + } + + /* If the base internal format and the texture format don't match, + * we can't use blit-based TexSubImage. */ + if (texImage->_BaseFormat != + _mesa_get_format_base_format(texImage->TexFormat)) { + goto fallback; + } + + /* See if the texture format already matches the format and type, + * in which case the memcpy-based fast path will likely be used and + * we don't have to blit. */ + if (_mesa_format_matches_format_and_type(texImage->TexFormat, format, + type, unpack->SwapBytes)) { + goto fallback; + } + + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL) + bind = PIPE_BIND_DEPTH_STENCIL; + else + bind = PIPE_BIND_RENDER_TARGET; + + /* See if the destination format is supported. + * For luminance and intensity, only the red channel is stored there. */ + dst_format = util_format_linear(dst->format); + dst_format = util_format_luminance_to_red(dst_format); + dst_format = util_format_intensity_to_red(dst_format); + + if (!dst_format || + !screen->is_format_supported(screen, dst_format, dst->target, + dst->nr_samples, bind)) { + goto fallback; + } + + /* Choose the source format. */ + src_format = st_choose_matching_format(screen, PIPE_BIND_SAMPLER_VIEW, + format, type, unpack->SwapBytes); + if (!src_format) { + goto fallback; + } + + mesa_src_format = st_pipe_format_to_mesa_format(src_format); + + /* There is no reason to do this if we cannot use memcpy for the temporary + * source texture at least. This also takes transfer ops into account, + * etc. */ + if (!_mesa_texstore_can_use_memcpy(ctx, + _mesa_get_format_base_format(mesa_src_format), + mesa_src_format, format, type, unpack)) { + goto fallback; + } + + /* TexSubImage only sets a single cubemap face. */ + if (gl_target == GL_TEXTURE_CUBE_MAP) { + gl_target = GL_TEXTURE_2D; + } + + /* Initialize the source texture description. */ + memset(&src_templ, 0, sizeof(src_templ)); + src_templ.target = gl_target_to_pipe(gl_target); + src_templ.format = src_format; + src_templ.bind = PIPE_BIND_SAMPLER_VIEW; + src_templ.usage = PIPE_USAGE_STAGING; + + st_gl_texture_dims_to_pipe_dims(gl_target, width, height, depth, + &src_templ.width0, &src_templ.height0, + &src_templ.depth0, &src_templ.array_size); + + /* Check for NPOT texture support. */ + if (!screen->get_param(screen, PIPE_CAP_NPOT_TEXTURES) && + (!util_is_power_of_two(src_templ.width0) || + !util_is_power_of_two(src_templ.height0) || + !util_is_power_of_two(src_templ.depth0))) { + goto fallback; + } + + /* Create the source texture. */ + src = screen->resource_create(screen, &src_templ); + if (!src) { + goto fallback; + } + + /* Map source pixels. */ + pixels = _mesa_validate_pbo_teximage(ctx, dims, width, height, depth, + format, type, pixels, unpack, + "glTexSubImage"); + if (!pixels) { + /* This is a GL error. */ + pipe_resource_reference(&src, NULL); + return; + } + + /* From now on, we need the gallium representation of dimensions. */ + if (gl_target == GL_TEXTURE_1D_ARRAY) { + depth = height; + height = 1; + } + + map = pipe_transfer_map_3d(pipe, src, 0, PIPE_TRANSFER_WRITE, 0, 0, 0, + width, height, depth, &transfer); + if (!map) { + _mesa_unmap_teximage_pbo(ctx, unpack); + pipe_resource_reference(&src, NULL); + goto fallback; + } + + /* Upload pixels (just memcpy). */ + { + const uint bytesPerRow = width * util_format_get_blocksize(src_format); + GLuint row, slice; + + for (slice = 0; slice < depth; slice++) { + if (gl_target == GL_TEXTURE_1D_ARRAY) { + /* 1D array textures. + * We need to convert gallium coords to GL coords. + */ + GLvoid *src = _mesa_image_address3d(unpack, pixels, + width, depth, format, + type, 0, slice, 0); + memcpy(map, src, bytesPerRow); + } + else { + ubyte *slice_map = map; + + for (row = 0; row < height; row++) { + GLvoid *src = _mesa_image_address3d(unpack, pixels, + width, height, format, + type, slice, row, 0); + memcpy(slice_map, src, bytesPerRow); + slice_map += transfer->stride; + } + } + map += transfer->layer_stride; + } + } + + pipe_transfer_unmap(pipe, transfer); + _mesa_unmap_teximage_pbo(ctx, unpack); + + /* Blit. */ + blit.src.resource = src; + blit.src.level = 0; + blit.src.format = src_format; + blit.dst.resource = dst; + blit.dst.level = stObj->pt != stImage->pt ? 0 : texImage->Level; + blit.dst.format = dst_format; + blit.src.box.x = blit.src.box.y = blit.src.box.z = 0; + blit.dst.box.x = xoffset; + blit.dst.box.y = yoffset; + blit.dst.box.z = zoffset + texImage->Face; + blit.src.box.width = blit.dst.box.width = width; + blit.src.box.height = blit.dst.box.height = height; + blit.src.box.depth = blit.dst.box.depth = depth; + blit.mask = get_blit_mask(format, texImage->_BaseFormat); + blit.filter = PIPE_TEX_FILTER_NEAREST; + blit.scissor_enable = FALSE; + + st->pipe->blit(st->pipe, &blit); + + pipe_resource_reference(&src, NULL); + return; + +fallback: + _mesa_store_texsubimage(ctx, dims, texImage, xoffset, yoffset, zoffset, + width, height, depth, format, type, pixels, + unpack); +} + +static void st_TexImage(struct gl_context * ctx, GLuint dims, struct gl_texture_image *texImage, GLenum format, GLenum type, const void *pixels, const struct gl_pixelstore_attrib *unpack) { + assert(dims == 1 || dims == 2 || dims == 3); + prep_teximage(ctx, texImage, format, type); - _mesa_store_teximage(ctx, dims, texImage, format, type, pixels, unpack); + + if (texImage->Width == 0 || texImage->Height == 0 || texImage->Depth == 0) + return; + + /* allocate storage for texture data */ + if (!ctx->Driver.AllocTextureImageBuffer(ctx, texImage)) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims); + return; + } + + st_TexSubImage(ctx, dims, texImage, 0, 0, 0, + texImage->Width, texImage->Height, texImage->Depth, + format, type, pixels, unpack); } @@ -1692,7 +1906,7 @@ st_init_texture_functions(struct dd_function_table *functions) functions->ChooseTextureFormat = st_ChooseTextureFormat; functions->QuerySamplesForFormat = st_QuerySamplesForFormat; functions->TexImage = st_TexImage; - functions->TexSubImage = _mesa_store_texsubimage; + functions->TexSubImage = st_TexSubImage; functions->CompressedTexSubImage = _mesa_store_compressed_texsubimage; functions->CopyTexSubImage = st_CopyTexSubImage; functions->GenerateMipmap = st_generate_mipmap; |