From c860171c632a34f9c2a8ab1c06dc5a93325e84c9 Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Fri, 12 Jan 2018 09:32:41 -0700 Subject: [PATCH] st/mesa: expand glDrawPixels cache to handle multiple images The newest version of WSI Fusion makes several glDrawPixels calls per frame. By caching more than one image, we get better performance when panning/zooming the map. v2: move pixel unpack param checking out of cache search loop, per Roland v3: also move unpack->BufferObj check out of loop, per Roland. --- src/mesa/state_tracker/st_cb_drawpixels.c | 195 +++++++++++++++------- src/mesa/state_tracker/st_context.c | 4 - src/mesa/state_tracker/st_context.h | 22 ++- 3 files changed, 153 insertions(+), 68 deletions(-) diff --git a/src/mesa/state_tracker/st_cb_drawpixels.c b/src/mesa/state_tracker/st_cb_drawpixels.c index 1d88976ed98..ff3eb9b6143 100644 --- a/src/mesa/state_tracker/st_cb_drawpixels.c +++ b/src/mesa/state_tracker/st_cb_drawpixels.c @@ -374,6 +374,130 @@ alloc_texture(struct st_context *st, GLsizei width, GLsizei height, } +/** + * Search the cache for an image which matches the given parameters. + * \return pipe_resource pointer if found, NULL if not found. + */ +static struct pipe_resource * +search_drawpixels_cache(struct st_context *st, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const struct gl_pixelstore_attrib *unpack, + const void *pixels) +{ + struct pipe_resource *pt = NULL; + const GLint bpp = _mesa_bytes_per_pixel(format, type); + unsigned i; + + if ((unpack->RowLength != 0 && unpack->RowLength != width) || + unpack->SkipPixels != 0 || + unpack->SkipRows != 0 || + unpack->SwapBytes || + _mesa_is_bufferobj(unpack->BufferObj)) { + /* we don't allow non-default pixel unpacking values */ + return NULL; + } + + /* Search cache entries for a match */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + + if (width == entry->width && + height == entry->height && + format == entry->format && + type == entry->type && + pixels == entry->user_pointer && + entry->image) { + assert(entry->texture); + + /* check if the pixel data is the same */ + if (memcmp(pixels, entry->image, width * height * bpp) == 0) { + /* Success - found a cache match */ + pipe_resource_reference(&pt, entry->texture); + /* refcount of returned texture should be at least two here. One + * reference for the cache to hold on to, one for the caller (which + * it will release), and possibly more held by the driver. + */ + assert(pt->reference.count >= 2); + + /* update the age of this entry */ + entry->age = ++st->drawpix_cache.age; + + return pt; + } + } + } + + /* no cache match found */ + return NULL; +} + + +/** + * Find the oldest entry in the glDrawPixels cache. We'll replace this + * one when we need to store a new image. + */ +static struct drawpix_cache_entry * +find_oldest_drawpixels_cache_entry(struct st_context *st) +{ + unsigned oldest_age = ~0u, oldest_index = ~0u; + unsigned i; + + /* Find entry with oldest (lowest) age */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + const struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + if (entry->age < oldest_age) { + oldest_age = entry->age; + oldest_index = i; + } + } + + assert(oldest_index != ~0u); + + return &st->drawpix_cache.entries[oldest_index]; +} + + +/** + * Try to save the given glDrawPixels image in the cache. + */ +static void +cache_drawpixels_image(struct st_context *st, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const struct gl_pixelstore_attrib *unpack, + const void *pixels, + struct pipe_resource *pt) +{ + if ((unpack->RowLength == 0 || unpack->RowLength == width) && + unpack->SkipPixels == 0 && + unpack->SkipRows == 0) { + const GLint bpp = _mesa_bytes_per_pixel(format, type); + struct drawpix_cache_entry *entry = + find_oldest_drawpixels_cache_entry(st); + assert(entry); + entry->width = width; + entry->height = height; + entry->format = format; + entry->type = type; + entry->user_pointer = pixels; + free(entry->image); + entry->image = malloc(width * height * bpp); + if (entry->image) { + memcpy(entry->image, pixels, width * height * bpp); + pipe_resource_reference(&entry->texture, pt); + entry->age = ++st->drawpix_cache.age; + } + else { + /* out of memory, free/disable cached texture */ + entry->width = 0; + entry->height = 0; + pipe_resource_reference(&entry->texture, NULL); + } + } +} + + /** * Make texture containing an image for glDrawPixels image. * If 'pixels' is NULL, leave the texture image data undefined. @@ -392,44 +516,11 @@ make_texture(struct st_context *st, GLenum baseInternalFormat; #if USE_DRAWPIXELS_CACHE - const GLint bpp = _mesa_bytes_per_pixel(format, type); - - /* Check if the glDrawPixels() parameters and state matches the cache */ - if (width == st->drawpix_cache.width && - height == st->drawpix_cache.height && - format == st->drawpix_cache.format && - type == st->drawpix_cache.type && - pixels == st->drawpix_cache.user_pointer && - !_mesa_is_bufferobj(unpack->BufferObj) && - (unpack->RowLength == 0 || unpack->RowLength == width) && - unpack->SkipPixels == 0 && - unpack->SkipRows == 0 && - unpack->SwapBytes == GL_FALSE && - st->drawpix_cache.image) { - assert(st->drawpix_cache.texture); - - /* check if the pixel data is the same */ - if (memcmp(pixels, st->drawpix_cache.image, width * height * bpp) == 0) { - /* OK, re-use the cached texture */ - pipe_resource_reference(&pt, st->drawpix_cache.texture); - /* refcount of returned texture should be at least two here. One - * reference for the cache to hold on to, one for the caller (which - * it will release), and possibly more held by the driver. - */ - assert(pt->reference.count >= 2); - return pt; - } - } - - /* discard the cached image and texture (if there is one) */ - st->drawpix_cache.width = 0; - st->drawpix_cache.height = 0; - st->drawpix_cache.user_pointer = NULL; - if (st->drawpix_cache.image) { - free(st->drawpix_cache.image); - st->drawpix_cache.image = NULL; + pt = search_drawpixels_cache(st, width, height, format, type, + unpack, pixels); + if (pt) { + return pt; } - pipe_resource_reference(&st->drawpix_cache.texture, NULL); #endif /* Choose a pixel format for the temp texture which will hold the @@ -522,28 +613,7 @@ make_texture(struct st_context *st, _mesa_unmap_pbo_source(ctx, unpack); #if USE_DRAWPIXELS_CACHE - /* Save the glDrawPixels parameter and image in the cache */ - if ((unpack->RowLength == 0 || unpack->RowLength == width) && - unpack->SkipPixels == 0 && - unpack->SkipRows == 0) { - st->drawpix_cache.width = width; - st->drawpix_cache.height = height; - st->drawpix_cache.format = format; - st->drawpix_cache.type = type; - st->drawpix_cache.user_pointer = pixels; - assert(!st->drawpix_cache.image); - st->drawpix_cache.image = malloc(width * height * bpp); - if (st->drawpix_cache.image) { - memcpy(st->drawpix_cache.image, pixels, width * height * bpp); - pipe_resource_reference(&st->drawpix_cache.texture, pt); - } - else { - /* out of memory, free/disable cached texture */ - st->drawpix_cache.width = 0; - st->drawpix_cache.height = 0; - pipe_resource_reference(&st->drawpix_cache.texture, NULL); - } - } + cache_drawpixels_image(st, width, height, format, type, unpack, pixels, pt); #endif return pt; @@ -1658,4 +1728,11 @@ st_destroy_drawpix(struct st_context *st) cso_delete_vertex_shader(st->cso_context, st->drawpix.vert_shaders[0]); if (st->drawpix.vert_shaders[1]) cso_delete_vertex_shader(st->cso_context, st->drawpix.vert_shaders[1]); + + /* Free cache data */ + for (i = 0; i < ARRAY_SIZE(st->drawpix_cache.entries); i++) { + struct drawpix_cache_entry *entry = &st->drawpix_cache.entries[i]; + free(entry->image); + pipe_resource_reference(&entry->texture, NULL); + } } diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c index 3ba48479266..eb6c4588197 100644 --- a/src/mesa/state_tracker/st_context.c +++ b/src/mesa/state_tracker/st_context.c @@ -273,10 +273,6 @@ st_destroy_context_priv(struct st_context *st, bool destroy_pipe) } } - /* free glDrawPixels cache data */ - free(st->drawpix_cache.image); - pipe_resource_reference(&st->drawpix_cache.texture, NULL); - /* free glReadPixels cache data */ st_invalidate_readpix_cache(st); diff --git a/src/mesa/state_tracker/st_context.h b/src/mesa/state_tracker/st_context.h index 0258bed36b1..ae2bdf596ac 100644 --- a/src/mesa/state_tracker/st_context.h +++ b/src/mesa/state_tracker/st_context.h @@ -86,6 +86,20 @@ struct st_bound_handles uint64_t *handles; }; + +#define NUM_DRAWPIX_CACHE_ENTRIES 4 + +struct drawpix_cache_entry +{ + GLsizei width, height; + GLenum format, type; + const void *user_pointer; /**< Last user 'pixels' pointer */ + void *image; /**< Copy of the glDrawPixels image data */ + struct pipe_resource *texture; + unsigned age; +}; + + struct st_context { struct st_context_iface iface; @@ -208,12 +222,10 @@ struct st_context void *vert_shaders[2]; /**< ureg shaders */ } drawpix; + /** Cache of glDrawPixels images */ struct { - GLsizei width, height; - GLenum format, type; - const void *user_pointer; /**< Last user 'pixels' pointer */ - void *image; /**< Copy of the glDrawPixels image data */ - struct pipe_resource *texture; + struct drawpix_cache_entry entries[NUM_DRAWPIX_CACHE_ENTRIES]; + unsigned age; } drawpix_cache; /** for glReadPixels */ -- 2.30.2