+/**
+ * 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 ||
+ 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);
+ }
+ }
+}
+
+