1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2009-2015 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19 #include "image-cache.h"
20 #include "red-parse-qxl.h"
21 #include "display-channel.h"
22
image_cache_find(ImageCache * cache,uint64_t id)23 static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
24 {
25 ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
26
27 while (item) {
28 if (item->id == id) {
29 return item;
30 }
31 item = item->next;
32 }
33 return nullptr;
34 }
35
image_cache_hit(ImageCache * cache,uint64_t id)36 static bool image_cache_hit(ImageCache *cache, uint64_t id)
37 {
38 ImageCacheItem *item;
39 if (!(item = image_cache_find(cache, id))) {
40 return FALSE;
41 }
42 #ifdef IMAGE_CACHE_AGE
43 item->age = cache->age;
44 #endif
45 ring_remove(&item->lru_link);
46 ring_add(&cache->lru, &item->lru_link);
47 return TRUE;
48 }
49
image_cache_remove(ImageCache * cache,ImageCacheItem * item)50 static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
51 {
52 ImageCacheItem **now;
53
54 now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
55 for (;;) {
56 spice_assert(*now);
57 if (*now == item) {
58 *now = item->next;
59 break;
60 }
61 now = &(*now)->next;
62 }
63 ring_remove(&item->lru_link);
64 pixman_image_unref(item->image);
65 g_free(item);
66 #ifndef IMAGE_CACHE_AGE
67 cache->num_items--;
68 #endif
69 }
70
71 #define IMAGE_CACHE_MAX_ITEMS 2
72
image_cache_put(SpiceImageCache * spice_cache,uint64_t id,pixman_image_t * image)73 static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
74 {
75 ImageCache *cache = SPICE_UPCAST(ImageCache, spice_cache);
76 ImageCacheItem *item;
77
78 #ifndef IMAGE_CACHE_AGE
79 if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
80 SPICE_VERIFY(SPICE_OFFSETOF(ImageCacheItem, lru_link) == 0);
81 ImageCacheItem *tail =
82 SPICE_CONTAINEROF(ring_get_tail(&cache->lru), ImageCacheItem, lru_link);
83 spice_assert(tail);
84 image_cache_remove(cache, tail);
85 }
86 #endif
87
88 item = g_new(ImageCacheItem, 1);
89 item->id = id;
90 #ifdef IMAGE_CACHE_AGE
91 item->age = cache->age;
92 #else
93 cache->num_items++;
94 #endif
95 item->image = pixman_image_ref(image);
96 ring_item_init(&item->lru_link);
97
98 item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
99 cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
100
101 ring_add(&cache->lru, &item->lru_link);
102 }
103
image_cache_get(SpiceImageCache * spice_cache,uint64_t id)104 static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
105 {
106 ImageCache *cache = SPICE_UPCAST(ImageCache, spice_cache);
107
108 ImageCacheItem *item = image_cache_find(cache, id);
109 if (!item) {
110 spice_error("not found");
111 }
112 return pixman_image_ref(item->image);
113 }
114
image_cache_init(ImageCache * cache)115 void image_cache_init(ImageCache *cache)
116 {
117 static const SpiceImageCacheOps image_cache_ops = {
118 image_cache_put,
119 image_cache_get,
120 };
121
122 cache->base.ops = &image_cache_ops;
123 memset(cache->hash_table, 0, sizeof(cache->hash_table));
124 ring_init(&cache->lru);
125 #ifdef IMAGE_CACHE_AGE
126 cache->age = 0;
127 #else
128 cache->num_items = 0;
129 #endif
130 }
131
image_cache_reset(ImageCache * cache)132 void image_cache_reset(ImageCache *cache)
133 {
134 ImageCacheItem *item;
135
136 SPICE_VERIFY(SPICE_OFFSETOF(ImageCacheItem, lru_link) == 0);
137 while ((item = SPICE_CONTAINEROF(ring_get_head(&cache->lru), ImageCacheItem, lru_link))) {
138 image_cache_remove(cache, item);
139 }
140 #ifdef IMAGE_CACHE_AGE
141 cache->age = 0;
142 #endif
143 }
144
145 #define IMAGE_CACHE_DEPTH 4
146
image_cache_aging(ImageCache * cache)147 void image_cache_aging(ImageCache *cache)
148 {
149 SPICE_VERIFY(SPICE_OFFSETOF(ImageCacheItem, lru_link) == 0);
150 #ifdef IMAGE_CACHE_AGE
151 ImageCacheItem *item;
152
153 cache->age++;
154 while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
155 cache->age - item->age > IMAGE_CACHE_DEPTH) {
156 image_cache_remove(cache, item);
157 }
158 #endif
159 }
160
image_cache_localize(ImageCache * cache,SpiceImage ** image_ptr,SpiceImage * image_store,Drawable * drawable)161 void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
162 SpiceImage *image_store, Drawable *drawable)
163 {
164 SpiceImage *image = *image_ptr;
165
166 if (image == nullptr) {
167 spice_assert(drawable != nullptr);
168 spice_assert(drawable->red_drawable->self_bitmap_image != nullptr);
169 *image_ptr = drawable->red_drawable->self_bitmap_image;
170 return;
171 }
172
173 if (image_cache_hit(cache, image->descriptor.id)) {
174 image_store->descriptor = image->descriptor;
175 image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
176 image_store->descriptor.flags = 0;
177 *image_ptr = image_store;
178 return;
179 }
180
181 switch (image->descriptor.type) {
182 case SPICE_IMAGE_TYPE_QUIC: {
183 image_store->descriptor = image->descriptor;
184 image_store->u.quic = image->u.quic;
185 *image_ptr = image_store;
186 #ifdef IMAGE_CACHE_AGE
187 image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
188 #else
189 if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
190 image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
191 }
192 #endif
193 break;
194 }
195 case SPICE_IMAGE_TYPE_BITMAP:
196 case SPICE_IMAGE_TYPE_SURFACE:
197 /* nothing */
198 break;
199 default:
200 spice_error("invalid image type");
201 }
202 }
203
image_cache_localize_brush(ImageCache * cache,SpiceBrush * brush,SpiceImage * image_store)204 void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
205 {
206 if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
207 image_cache_localize(cache, &brush->u.pattern.pat, image_store, nullptr);
208 }
209 }
210
image_cache_localize_mask(ImageCache * cache,SpiceQMask * mask,SpiceImage * image_store)211 void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
212 {
213 if (mask->bitmap) {
214 image_cache_localize(cache, &mask->bitmap, image_store, nullptr);
215 }
216 }
217