From 0a1479c829ed34a65e60c6619a8164e1b079aaee Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Thu, 14 Feb 2013 01:03:55 +0100 Subject: [PATCH] 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 --- src/mesa/main/texstore.c | 18 +- src/mesa/main/texstore.h | 7 + src/mesa/state_tracker/st_cb_texture.c | 218 ++++++++++++++++++++++++- 3 files changed, 239 insertions(+), 4 deletions(-) diff --git a/src/mesa/main/texstore.c b/src/mesa/main/texstore.c index 9281aa9eafb..77222d6dfb6 100644 --- a/src/mesa/main/texstore.c +++ b/src/mesa/main/texstore.c @@ -3791,8 +3791,11 @@ _mesa_get_texstore_func(gl_format format) } -static GLboolean -_mesa_texstore_memcpy(TEXSTORE_PARAMS) +GLboolean +_mesa_texstore_can_use_memcpy(struct gl_context *ctx, + GLenum baseInternalFormat, gl_format dstFormat, + GLenum srcFormat, GLenum srcType, + const struct gl_pixelstore_attrib *srcPacking) { GLenum dstType; @@ -3834,6 +3837,17 @@ _mesa_texstore_memcpy(TEXSTORE_PARAMS) return GL_FALSE; } + return GL_TRUE; +} + +static GLboolean +_mesa_texstore_memcpy(TEXSTORE_PARAMS) +{ + if (!_mesa_texstore_can_use_memcpy(ctx, baseInternalFormat, dstFormat, + srcFormat, srcType, srcPacking)) { + return GL_FALSE; + } + memcpy_texture(ctx, dims, dstFormat, dstRowStride, dstSlices, diff --git a/src/mesa/main/texstore.h b/src/mesa/main/texstore.h index 7af532d652e..107f2130015 100644 --- a/src/mesa/main/texstore.h +++ b/src/mesa/main/texstore.h @@ -70,6 +70,13 @@ extern GLboolean _mesa_texstore(TEXSTORE_PARAMS); +extern GLboolean +_mesa_texstore_can_use_memcpy(struct gl_context *ctx, + GLenum baseInternalFormat, gl_format dstFormat, + GLenum srcFormat, GLenum srcType, + const struct gl_pixelstore_attrib *srcPacking); + + extern GLubyte * _mesa_make_temp_ubyte_image(struct gl_context *ctx, GLuint dims, GLenum logicalBaseFormat, 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 @@ -585,14 +585,228 @@ 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; -- 2.30.2