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.
This commit is contained in:
Brian Paul
2018-01-12 09:32:41 -07:00
parent 5092610f29
commit c860171c63
3 changed files with 153 additions and 68 deletions

View File

@@ -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. * Make texture containing an image for glDrawPixels image.
* If 'pixels' is NULL, leave the texture image data undefined. * If 'pixels' is NULL, leave the texture image data undefined.
@@ -392,44 +516,11 @@ make_texture(struct st_context *st,
GLenum baseInternalFormat; GLenum baseInternalFormat;
#if USE_DRAWPIXELS_CACHE #if USE_DRAWPIXELS_CACHE
const GLint bpp = _mesa_bytes_per_pixel(format, type); pt = search_drawpixels_cache(st, width, height, format, type,
unpack, pixels);
/* Check if the glDrawPixels() parameters and state matches the cache */ if (pt) {
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; 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;
}
pipe_resource_reference(&st->drawpix_cache.texture, NULL);
#endif #endif
/* Choose a pixel format for the temp texture which will hold the /* 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); _mesa_unmap_pbo_source(ctx, unpack);
#if USE_DRAWPIXELS_CACHE #if USE_DRAWPIXELS_CACHE
/* Save the glDrawPixels parameter and image in the cache */ cache_drawpixels_image(st, width, height, format, type, unpack, pixels, pt);
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);
}
}
#endif #endif
return pt; 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]); cso_delete_vertex_shader(st->cso_context, st->drawpix.vert_shaders[0]);
if (st->drawpix.vert_shaders[1]) if (st->drawpix.vert_shaders[1])
cso_delete_vertex_shader(st->cso_context, 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);
}
} }

View File

@@ -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 */ /* free glReadPixels cache data */
st_invalidate_readpix_cache(st); st_invalidate_readpix_cache(st);

View File

@@ -86,6 +86,20 @@ struct st_bound_handles
uint64_t *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
{ {
struct st_context_iface iface; struct st_context_iface iface;
@@ -208,12 +222,10 @@ struct st_context
void *vert_shaders[2]; /**< ureg shaders */ void *vert_shaders[2]; /**< ureg shaders */
} drawpix; } drawpix;
/** Cache of glDrawPixels images */
struct { struct {
GLsizei width, height; struct drawpix_cache_entry entries[NUM_DRAWPIX_CACHE_ENTRIES];
GLenum format, type; unsigned age;
const void *user_pointer; /**< Last user 'pixels' pointer */
void *image; /**< Copy of the glDrawPixels image data */
struct pipe_resource *texture;
} drawpix_cache; } drawpix_cache;
/** for glReadPixels */ /** for glReadPixels */