summaryrefslogtreecommitdiffstats
path: root/src/mesa/state_tracker
diff options
context:
space:
mode:
authorMarek Olšák <[email protected]>2013-02-14 01:03:55 +0100
committerMarek Olšák <[email protected]>2013-02-18 17:57:41 +0100
commit0a1479c829ed34a65e60c6619a8164e1b079aaee (patch)
tree35ccf42db1ba7490db4442fc3324ccf93faab30e /src/mesa/state_tracker
parenta6e0ac95716ca933558c70e45798ddc67e6ad5ad (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.c218
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;