/src/libvips/libvips/iofuncs/buffer.c
Line | Count | Source |
1 | | /* Manage sets of pixel buffers on an image. |
2 | | * |
3 | | * 30/10/06 |
4 | | * - from window.c |
5 | | * 2/2/07 |
6 | | * - speed up the search, use our own lock (thanks Christian) |
7 | | * 5/2/07 |
8 | | * - split to many buffer lists per image |
9 | | * 11/2/07 |
10 | | * - split to a buffer hash per thread |
11 | | * - reuse buffer mallocs when we can |
12 | | * 20/2/07 |
13 | | * - add VipsBufferCacheList and we can avoid some hash ops on |
14 | | * done/undone |
15 | | * 5/3/10 |
16 | | * - move invalid stuff to region |
17 | | * - move link maintenance to im_demand_hint |
18 | | * 21/9/11 |
19 | | * - switch to vips_tracked_malloc() |
20 | | * 18/12/13 |
21 | | * - keep a few buffers in reserve per image, stops malloc/free |
22 | | * cycling when sharing is repeatedly discovered |
23 | | * 6/6/16 |
24 | | * - free buffers on image close as well as thread exit, so main thread |
25 | | * buffers don't clog up the system |
26 | | * 13/10/16 |
27 | | * - better solution: don't keep a buffercache for non-workers |
28 | | */ |
29 | | |
30 | | /* |
31 | | |
32 | | This file is part of VIPS. |
33 | | |
34 | | VIPS is free software; you can redistribute it and/or modify |
35 | | it under the terms of the GNU Lesser General Public License as published by |
36 | | the Free Software Foundation; either version 2 of the License, or |
37 | | (at your option) any later version. |
38 | | |
39 | | This program is distributed in the hope that it will be useful, |
40 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
41 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
42 | | GNU Lesser General Public License for more details. |
43 | | |
44 | | You should have received a copy of the GNU Lesser General Public License |
45 | | along with this program; if not, write to the Free Software |
46 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
47 | | 02110-1301 USA |
48 | | |
49 | | */ |
50 | | |
51 | | /* |
52 | | |
53 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
54 | | |
55 | | */ |
56 | | |
57 | | /* |
58 | | #define DEBUG_VERBOSE |
59 | | #define DEBUG_CREATE |
60 | | #define DEBUG |
61 | | */ |
62 | | |
63 | | #ifdef HAVE_CONFIG_H |
64 | | #include <config.h> |
65 | | #endif /*HAVE_CONFIG_H*/ |
66 | | #include <glib/gi18n-lib.h> |
67 | | |
68 | | #include <stdio.h> |
69 | | #include <stdlib.h> |
70 | | |
71 | | #include <vips/vips.h> |
72 | | #include <vips/internal.h> |
73 | | |
74 | | #ifdef DEBUG |
75 | | /* Track all buffers here for debugging. |
76 | | */ |
77 | | static GSList *vips__buffer_all = NULL; |
78 | | #endif /*DEBUG*/ |
79 | | |
80 | | #ifdef DEBUG_CREATE |
81 | | static GSList *vips__buffer_cache_all = NULL; |
82 | | #endif /*DEBUG_CREATE*/ |
83 | | |
84 | | /* The maximum numbers of buffers we hold in reserve per image. |
85 | | */ |
86 | | static const int buffer_cache_max_reserve = 2; |
87 | | |
88 | | /* Workers have a BufferThread (and BufferCache) in a GPrivate they have |
89 | | * exclusive access to. |
90 | | */ |
91 | | static void buffer_thread_destroy_notify(gpointer data); |
92 | | static GPrivate buffer_thread_key = |
93 | | G_PRIVATE_INIT(buffer_thread_destroy_notify); |
94 | | |
95 | | void |
96 | | vips_buffer_print(VipsBuffer *buffer) |
97 | 0 | { |
98 | 0 | printf("VipsBuffer: %p ref_count = %d, ", buffer, buffer->ref_count); |
99 | 0 | printf("im = %p, ", buffer->im); |
100 | 0 | printf("area.left = %d, ", buffer->area.left); |
101 | 0 | printf("area.top = %d, ", buffer->area.top); |
102 | 0 | printf("area.width = %d, ", buffer->area.width); |
103 | 0 | printf("area.height = %d, ", buffer->area.height); |
104 | 0 | printf("done = %d, ", buffer->done); |
105 | 0 | printf("cache = %p, ", buffer->cache); |
106 | 0 | printf("buf = %p, ", buffer->buf); |
107 | 0 | printf("bsize = %zd\n", buffer->bsize); |
108 | 0 | } |
109 | | |
110 | | #ifdef DEBUG |
111 | | static void * |
112 | | vips_buffer_dump(VipsBuffer *buffer, size_t *reserve, size_t *alive) |
113 | | { |
114 | | vips_buffer_print(buffer); |
115 | | |
116 | | g_assert(buffer->im); |
117 | | g_assert(buffer->buf); |
118 | | |
119 | | if (!buffer->cache && |
120 | | !buffer->done) { |
121 | | /* Global buffer, not linked to any cache. |
122 | | */ |
123 | | printf("global buffer %p, %.3g MB\n", |
124 | | buffer, buffer->bsize / (1024 * 1024.0)); |
125 | | *alive += buffer->bsize; |
126 | | } |
127 | | |
128 | | else if (buffer->cache && |
129 | | buffer->done && |
130 | | !vips_rect_isempty(&buffer->area) && |
131 | | g_slist_find(buffer->cache->buffers, buffer)) { |
132 | | /* Published on a thread. |
133 | | */ |
134 | | printf("thread buffer %p, %.3g MB\n", |
135 | | buffer, buffer->bsize / (1024 * 1024.0)); |
136 | | *alive += buffer->bsize; |
137 | | } |
138 | | |
139 | | else if (buffer->ref_count == 0 && |
140 | | buffer->cache && |
141 | | !buffer->done && |
142 | | vips_rect_isempty(&buffer->area) && |
143 | | g_slist_find(buffer->cache->reserve, buffer)) |
144 | | /* Held in reserve. |
145 | | */ |
146 | | *reserve += buffer->bsize; |
147 | | |
148 | | else |
149 | | printf("buffer craziness!\n"); |
150 | | |
151 | | return NULL; |
152 | | } |
153 | | #endif /*DEBUG*/ |
154 | | |
155 | | #ifdef DEBUG_CREATE |
156 | | static void * |
157 | | vips_buffer_cache_dump(VipsBufferCache *cache, void *a, void *b) |
158 | | { |
159 | | printf("VipsBufferCache: %p\n", cache); |
160 | | printf("\t%d buffers\n", g_slist_length(cache->buffers)); |
161 | | printf("\tthread %p\n", cache->thread); |
162 | | printf("\timage %p\n", cache->im); |
163 | | printf("\tbuffer_thread %p\n", cache->buffer_thread); |
164 | | printf("\t%d in reserve\n", g_slist_length(cache->reserve)); |
165 | | |
166 | | return NULL; |
167 | | } |
168 | | #endif /*DEBUG_CREATE*/ |
169 | | |
170 | | void |
171 | | vips_buffer_dump_all(void) |
172 | 0 | { |
173 | | #ifdef DEBUG |
174 | | if (vips__buffer_all) { |
175 | | size_t reserve; |
176 | | size_t alive; |
177 | | |
178 | | printf("buffers:\n"); |
179 | | |
180 | | reserve = 0; |
181 | | alive = 0; |
182 | | vips_slist_map2(vips__buffer_all, |
183 | | (VipsSListMap2Fn) vips_buffer_dump, &reserve, &alive); |
184 | | printf("%.3g MB alive\n", alive / (1024 * 1024.0)); |
185 | | printf("%.3g MB in reserve\n", reserve / (1024 * 1024.0)); |
186 | | } |
187 | | |
188 | | #ifdef DEBUG_CREATE |
189 | | if (vips__buffer_cache_all) { |
190 | | printf("buffers: %d buffer cache still alive\n", |
191 | | g_slist_length(vips__buffer_cache_all)); |
192 | | vips_slist_map2(vips__buffer_cache_all, |
193 | | (VipsSListMap2Fn) vips_buffer_cache_dump, NULL, NULL); |
194 | | printf("g_thread_self() == %p\n", g_thread_self()); |
195 | | } |
196 | | #endif /*DEBUG_CREATE*/ |
197 | | #endif /*DEBUG*/ |
198 | 0 | } |
199 | | |
200 | | static void |
201 | | vips_buffer_free(VipsBuffer *buffer) |
202 | 9.75M | { |
203 | 9.75M | VIPS_FREEF(vips_tracked_aligned_free, buffer->buf); |
204 | 9.75M | buffer->bsize = 0; |
205 | 9.75M | g_free(buffer); |
206 | | |
207 | | #ifdef DEBUG |
208 | | g_mutex_lock(&vips__global_lock); |
209 | | |
210 | | g_assert(g_slist_find(vips__buffer_all, buffer)); |
211 | | vips__buffer_all = g_slist_remove(vips__buffer_all, buffer); |
212 | | |
213 | | g_mutex_unlock(&vips__global_lock); |
214 | | #endif /*DEBUG*/ |
215 | | |
216 | | #ifdef DEBUG_VERBOSE |
217 | | printf("vips_buffer_free: freeing buffer %p\n", buffer); |
218 | | #endif /*DEBUG_VERBOSE*/ |
219 | 9.75M | } |
220 | | |
221 | | static void |
222 | | buffer_thread_free(VipsBufferThread *buffer_thread) |
223 | 1.03M | { |
224 | 1.03M | VIPS_FREEF(g_hash_table_destroy, buffer_thread->hash); |
225 | 1.03M | VIPS_FREE(buffer_thread); |
226 | 1.03M | } |
227 | | |
228 | | /* Run for GDestroyNotify on the VipsBufferThread hash. |
229 | | */ |
230 | | static void |
231 | | buffer_cache_free(VipsBufferCache *cache) |
232 | 6.38M | { |
233 | 6.38M | GSList *p; |
234 | | |
235 | | #ifdef DEBUG_CREATE |
236 | | g_mutex_lock(&vips__global_lock); |
237 | | vips__buffer_cache_all = |
238 | | g_slist_remove(vips__buffer_cache_all, cache); |
239 | | g_mutex_unlock(&vips__global_lock); |
240 | | |
241 | | printf("buffer_cache_free: freeing cache %p on thread %p\n", |
242 | | cache, g_thread_self()); |
243 | | printf("\t(%d caches left)\n", |
244 | | g_slist_length(vips__buffer_cache_all)); |
245 | | #endif /*DEBUG_CREATE*/ |
246 | | |
247 | | /* Need to mark undone so we don't try and take them off this cache on |
248 | | * unref. |
249 | | */ |
250 | 6.38M | for (p = cache->buffers; p; p = p->next) { |
251 | 0 | VipsBuffer *buffer = (VipsBuffer *) p->data; |
252 | |
|
253 | 0 | g_assert(buffer->done); |
254 | 0 | g_assert(buffer->cache == cache); |
255 | | |
256 | 0 | buffer->done = FALSE; |
257 | 0 | buffer->cache = NULL; |
258 | 0 | } |
259 | 6.38M | VIPS_FREEF(g_slist_free, cache->buffers); |
260 | | |
261 | 12.6M | for (p = cache->reserve; p; p = p->next) { |
262 | 6.24M | VipsBuffer *buffer = (VipsBuffer *) p->data; |
263 | | |
264 | 6.24M | vips_buffer_free(buffer); |
265 | 6.24M | } |
266 | 6.38M | VIPS_FREEF(g_slist_free, cache->reserve); |
267 | | |
268 | 6.38M | g_free(cache); |
269 | 6.38M | } |
270 | | |
271 | | static VipsBufferCache * |
272 | | buffer_cache_new(VipsBufferThread *buffer_thread, VipsImage *im) |
273 | 6.38M | { |
274 | 6.38M | VipsBufferCache *cache; |
275 | | |
276 | 6.38M | cache = g_new(VipsBufferCache, 1); |
277 | 6.38M | cache->buffers = NULL; |
278 | 6.38M | cache->thread = g_thread_self(); |
279 | 6.38M | cache->im = im; |
280 | 6.38M | cache->buffer_thread = buffer_thread; |
281 | 6.38M | cache->reserve = NULL; |
282 | 6.38M | cache->n_reserve = 0; |
283 | | |
284 | | #ifdef DEBUG_CREATE |
285 | | g_mutex_lock(&vips__global_lock); |
286 | | vips__buffer_cache_all = |
287 | | g_slist_prepend(vips__buffer_cache_all, cache); |
288 | | g_mutex_unlock(&vips__global_lock); |
289 | | |
290 | | printf("buffer_cache_new: new cache %p for thread %p on image %p\n", |
291 | | cache, g_thread_self(), im); |
292 | | printf("\t(%d caches now)\n", |
293 | | g_slist_length(vips__buffer_cache_all)); |
294 | | #endif /*DEBUG_CREATE*/ |
295 | | |
296 | 6.38M | return cache; |
297 | 6.38M | } |
298 | | |
299 | | static VipsBufferThread * |
300 | | buffer_thread_new(void) |
301 | 1.03M | { |
302 | 1.03M | VipsBufferThread *buffer_thread; |
303 | | |
304 | 1.03M | buffer_thread = g_new(VipsBufferThread, 1); |
305 | 1.03M | buffer_thread->hash = g_hash_table_new_full( |
306 | 1.03M | g_direct_hash, g_direct_equal, |
307 | 1.03M | NULL, (GDestroyNotify) buffer_cache_free); |
308 | 1.03M | buffer_thread->thread = g_thread_self(); |
309 | | |
310 | 1.03M | return buffer_thread; |
311 | 1.03M | } |
312 | | |
313 | | /* Get our private VipsBufferThread. NULL for non-worker threads. |
314 | | */ |
315 | | static VipsBufferThread * |
316 | | buffer_thread_get(void) |
317 | 40.4M | { |
318 | 40.4M | VipsBufferThread *buffer_thread; |
319 | | |
320 | 40.4M | if (vips_thread_isvips()) { |
321 | | /* Our threads get a set of private buffers, since we know we |
322 | | * will be calling vips_thread_shutdown() on thread |
323 | | * termination. |
324 | | */ |
325 | 38.5M | if (!(buffer_thread = g_private_get(&buffer_thread_key))) { |
326 | 1.03M | buffer_thread = buffer_thread_new(); |
327 | 1.03M | g_private_set(&buffer_thread_key, buffer_thread); |
328 | 1.03M | } |
329 | | |
330 | 38.5M | g_assert(buffer_thread->thread == g_thread_self()); |
331 | 38.5M | } |
332 | 1.91M | else |
333 | | /* Non-vips threads don't have one. |
334 | | */ |
335 | 1.91M | buffer_thread = NULL; |
336 | | |
337 | 40.4M | return buffer_thread; |
338 | 40.4M | } |
339 | | |
340 | | /* Get the VipsBufferCache for this image, or NULL for a non-worker. |
341 | | */ |
342 | | static VipsBufferCache * |
343 | | buffer_cache_get(VipsImage *im) |
344 | 37.0M | { |
345 | 37.0M | VipsBufferThread *buffer_thread; |
346 | 37.0M | VipsBufferCache *cache; |
347 | | |
348 | 37.0M | if ((buffer_thread = buffer_thread_get())) { |
349 | 35.1M | if (!(cache = (VipsBufferCache *) |
350 | 35.1M | g_hash_table_lookup(buffer_thread->hash, im))) { |
351 | 6.38M | cache = buffer_cache_new(buffer_thread, im); |
352 | 6.38M | g_hash_table_insert(buffer_thread->hash, im, cache); |
353 | 6.38M | } |
354 | | |
355 | 35.1M | g_assert(cache->thread == g_thread_self()); |
356 | 35.1M | } |
357 | 1.91M | else |
358 | 1.91M | cache = NULL; |
359 | | |
360 | 37.0M | return cache; |
361 | 37.0M | } |
362 | | |
363 | | /* Pixels have been calculated: publish for other parts of this thread to see. |
364 | | */ |
365 | | void |
366 | | vips_buffer_done(VipsBuffer *buffer) |
367 | 1.68M | { |
368 | 1.68M | VipsImage *im = buffer->im; |
369 | 1.68M | VipsBufferCache *cache; |
370 | | |
371 | 1.68M | if (!buffer->done && |
372 | 1.68M | (cache = buffer_cache_get(im))) { |
373 | 1.68M | g_assert(!g_slist_find(cache->buffers, buffer)); |
374 | 1.68M | g_assert(!buffer->cache); |
375 | | |
376 | 1.68M | buffer->done = TRUE; |
377 | 1.68M | buffer->cache = cache; |
378 | | |
379 | 1.68M | cache->buffers = g_slist_prepend(cache->buffers, buffer); |
380 | | |
381 | | #ifdef DEBUG_VERBOSE |
382 | | printf("vips_buffer_done: thread %p adding buffer %p to cache %p\n", |
383 | | g_thread_self(), buffer, cache); |
384 | | vips_buffer_print(buffer); |
385 | | #endif /*DEBUG_VERBOSE*/ |
386 | 1.68M | } |
387 | 1.68M | } |
388 | | |
389 | | /* Take off the public 'done' list. Make sure it has no calculated pixels in. |
390 | | */ |
391 | | void |
392 | | vips_buffer_undone(VipsBuffer *buffer) |
393 | 23.8M | { |
394 | 23.8M | if (buffer->done) { |
395 | 1.68M | VipsBufferCache *cache = buffer->cache; |
396 | | |
397 | | #ifdef DEBUG_VERBOSE |
398 | | printf("vips_buffer_undone: thread %p removing " |
399 | | "buffer %p from cache %p\n", |
400 | | g_thread_self(), buffer, cache); |
401 | | #endif /*DEBUG_VERBOSE*/ |
402 | | |
403 | 1.68M | g_assert(cache->thread == g_thread_self()); |
404 | 1.68M | g_assert(cache->buffer_thread->thread == cache->thread); |
405 | 1.68M | g_assert(g_slist_find(cache->buffers, buffer)); |
406 | 1.68M | g_assert(buffer_thread_get()); |
407 | 1.68M | g_assert(cache->buffer_thread == buffer_thread_get()); |
408 | | |
409 | 1.68M | cache->buffers = g_slist_remove(cache->buffers, buffer); |
410 | 1.68M | buffer->done = FALSE; |
411 | | |
412 | | #ifdef DEBUG_VERBOSE |
413 | | printf("vips_buffer_undone: %d buffers left\n", |
414 | | g_slist_length(cache->buffers)); |
415 | | #endif /*DEBUG_VERBOSE*/ |
416 | 1.68M | } |
417 | | |
418 | 23.8M | buffer->cache = NULL; |
419 | 23.8M | buffer->area.width = 0; |
420 | 23.8M | buffer->area.height = 0; |
421 | 23.8M | } |
422 | | |
423 | | void |
424 | | vips_buffer_unref(VipsBuffer *buffer) |
425 | 11.7M | { |
426 | | #ifdef DEBUG_VERBOSE |
427 | | printf("** vips_buffer_unref: left = %d, top = %d, " |
428 | | "width = %d, height = %d (%p)\n", |
429 | | buffer->area.left, buffer->area.top, |
430 | | buffer->area.width, buffer->area.height, |
431 | | buffer); |
432 | | #endif /*DEBUG_VERBOSE*/ |
433 | | |
434 | 11.7M | g_assert(buffer->ref_count > 0); |
435 | | |
436 | 11.7M | buffer->ref_count -= 1; |
437 | | |
438 | 11.7M | if (buffer->ref_count == 0) { |
439 | 11.5M | VipsBufferCache *cache; |
440 | | |
441 | | #ifdef DEBUG_VERBOSE |
442 | | if (!buffer->done) |
443 | | printf("vips_buffer_unref: buffer was not done\n"); |
444 | | #endif /*DEBUG_VERBOSE*/ |
445 | | |
446 | 11.5M | vips_buffer_undone(buffer); |
447 | | |
448 | | /* Place on this thread's reserve list for reuse. |
449 | | */ |
450 | 11.5M | if ((cache = buffer_cache_get(buffer->im)) && |
451 | 10.1M | cache->n_reserve < buffer_cache_max_reserve) { |
452 | 8.04M | g_assert(!buffer->cache); |
453 | | |
454 | 8.04M | cache->reserve = |
455 | 8.04M | g_slist_prepend(cache->reserve, buffer); |
456 | 8.04M | cache->n_reserve += 1; |
457 | | |
458 | 8.04M | buffer->cache = cache; |
459 | 8.04M | buffer->area.width = 0; |
460 | 8.04M | buffer->area.height = 0; |
461 | 8.04M | } |
462 | 3.50M | else |
463 | 3.50M | vips_buffer_free(buffer); |
464 | 11.5M | } |
465 | 11.7M | } |
466 | | |
467 | | static int |
468 | | buffer_move(VipsBuffer *buffer, VipsRect *area) |
469 | 12.0M | { |
470 | 12.0M | VipsImage *im = buffer->im; |
471 | 12.0M | size_t new_bsize; |
472 | 12.0M | size_t align; |
473 | | |
474 | 12.0M | g_assert(buffer->ref_count == 1); |
475 | | |
476 | 12.0M | vips_buffer_undone(buffer); |
477 | 12.0M | g_assert(!buffer->done); |
478 | | |
479 | 12.0M | buffer->area = *area; |
480 | | |
481 | 12.0M | new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL(im) * |
482 | 12.0M | area->width * area->height; |
483 | | |
484 | | /* Need to pad buffer size to be aligned-up to |
485 | | * 64 bytes for the highway paths. |
486 | | */ |
487 | 12.0M | #ifdef HAVE_HWY |
488 | 12.0M | if (im->BandFmt == VIPS_FORMAT_UCHAR) { |
489 | 7.75M | new_bsize += /*HWY_ALIGNMENT*/ 64 - 1; |
490 | 7.75M | align = /*HWY_ALIGNMENT*/ 64; |
491 | 7.75M | } |
492 | 4.31M | else |
493 | 4.31M | #endif /*HAVE_HWY*/ |
494 | 4.31M | align = 16; |
495 | | |
496 | 12.0M | if (buffer->bsize < new_bsize || |
497 | 9.78M | !buffer->buf) { |
498 | 9.78M | buffer->bsize = new_bsize; |
499 | 9.78M | VIPS_FREEF(vips_tracked_aligned_free, buffer->buf); |
500 | 9.78M | if (!(buffer->buf = vips_tracked_aligned_alloc(buffer->bsize, align))) |
501 | 0 | return -1; |
502 | 9.78M | } |
503 | | |
504 | 12.0M | return 0; |
505 | 12.0M | } |
506 | | |
507 | | /* Make a new buffer. |
508 | | */ |
509 | | VipsBuffer * |
510 | | vips_buffer_new(VipsImage *im, VipsRect *area) |
511 | 11.5M | { |
512 | 11.5M | VipsBufferCache *cache; |
513 | 11.5M | VipsBuffer *buffer; |
514 | | |
515 | 11.5M | if ((cache = buffer_cache_get(im)) && |
516 | 11.3M | cache->reserve) { |
517 | 1.79M | buffer = (VipsBuffer *) cache->reserve->data; |
518 | 1.79M | cache->reserve = g_slist_remove(cache->reserve, buffer); |
519 | 1.79M | cache->n_reserve -= 1; |
520 | | |
521 | 1.79M | g_assert(buffer->im == im); |
522 | 1.79M | g_assert(buffer->done == FALSE); |
523 | 1.79M | g_assert(buffer->cache); |
524 | | |
525 | 1.79M | buffer->ref_count = 1; |
526 | 1.79M | buffer->done = FALSE; |
527 | 1.79M | buffer->cache = NULL; |
528 | 1.79M | } |
529 | 9.75M | else { |
530 | 9.75M | buffer = g_new0(VipsBuffer, 1); |
531 | 9.75M | buffer->ref_count = 1; |
532 | 9.75M | buffer->im = im; |
533 | 9.75M | buffer->done = FALSE; |
534 | 9.75M | buffer->cache = NULL; |
535 | 9.75M | buffer->buf = NULL; |
536 | 9.75M | buffer->bsize = 0; |
537 | | |
538 | | #ifdef DEBUG |
539 | | g_mutex_lock(&vips__global_lock); |
540 | | vips__buffer_all = |
541 | | g_slist_prepend(vips__buffer_all, buffer); |
542 | | g_mutex_unlock(&vips__global_lock); |
543 | | #endif /*DEBUG*/ |
544 | 9.75M | } |
545 | | |
546 | 11.5M | if (buffer_move(buffer, area)) { |
547 | 0 | vips_buffer_free(buffer); |
548 | 0 | return NULL; |
549 | 0 | } |
550 | | |
551 | 11.5M | return buffer; |
552 | 11.5M | } |
553 | | |
554 | | /* Find an existing buffer that encloses area and return a ref. Or NULL for no |
555 | | * existing buffer. |
556 | | */ |
557 | | static VipsBuffer * |
558 | | buffer_find(VipsImage *im, VipsRect *r) |
559 | 12.3M | { |
560 | 12.3M | VipsBufferCache *cache; |
561 | 12.3M | VipsBuffer *buffer; |
562 | 12.3M | GSList *p; |
563 | 12.3M | VipsRect *area; |
564 | | |
565 | 12.3M | if (!(cache = buffer_cache_get(im))) |
566 | 238k | return NULL; |
567 | | |
568 | | /* This needs to be quick :-( don't use |
569 | | * vips_slist_map2()/vips_rect_includesrect(), do the search |
570 | | * inline. |
571 | | * |
572 | | * FIXME we return the first enclosing buffer, perhaps we should |
573 | | * search for the largest? |
574 | | */ |
575 | 12.6M | for (p = cache->buffers; p; p = p->next) { |
576 | 797k | buffer = (VipsBuffer *) p->data; |
577 | 797k | area = &buffer->area; |
578 | | |
579 | 797k | if (area->left <= r->left && |
580 | 796k | area->top <= r->top && |
581 | 795k | area->left + area->width >= r->left + r->width && |
582 | 788k | area->top + area->height >= r->top + r->height) { |
583 | 246k | buffer->ref_count += 1; |
584 | | |
585 | | #ifdef DEBUG_VERBOSE |
586 | | printf("buffer_find: left = %d, top = %d, " |
587 | | "width = %d, height = %d, count = %d (%p)\n", |
588 | | buffer->area.left, buffer->area.top, |
589 | | buffer->area.width, buffer->area.height, |
590 | | buffer->ref_count, |
591 | | buffer); |
592 | | #endif /*DEBUG_VERBOSE*/ |
593 | | |
594 | 246k | return buffer; |
595 | 246k | } |
596 | 797k | } |
597 | | |
598 | 11.8M | return NULL; |
599 | 12.0M | } |
600 | | |
601 | | /* Return a ref to a buffer that encloses area. The buffer we return might be |
602 | | * done. |
603 | | */ |
604 | | VipsBuffer * |
605 | | vips_buffer_ref(VipsImage *im, VipsRect *area) |
606 | 0 | { |
607 | 0 | VipsBuffer *buffer; |
608 | |
|
609 | 0 | if ((buffer = buffer_find(im, area))) |
610 | 0 | return buffer; |
611 | 0 | else |
612 | 0 | return vips_buffer_new(im, area); |
613 | 0 | } |
614 | | |
615 | | /* Unref old, ref new, in a single operation. Reuse stuff if we can. The |
616 | | * buffer we return might or might not be done. |
617 | | */ |
618 | | VipsBuffer * |
619 | | vips_buffer_unref_ref(VipsBuffer *old_buffer, VipsImage *im, VipsRect *area) |
620 | 12.3M | { |
621 | 12.3M | VipsBuffer *buffer; |
622 | | |
623 | 12.3M | g_assert(!old_buffer || |
624 | 12.3M | old_buffer->im == im); |
625 | | |
626 | | /* Is the current buffer OK? |
627 | | */ |
628 | 12.3M | if (old_buffer && |
629 | 605k | vips_rect_includesrect(&old_buffer->area, area)) |
630 | 7.99k | return old_buffer; |
631 | | |
632 | | /* Does the new area already have a buffer? |
633 | | */ |
634 | 12.3M | if ((buffer = buffer_find(im, area))) { |
635 | 246k | VIPS_FREEF(vips_buffer_unref, old_buffer); |
636 | 246k | return buffer; |
637 | 246k | } |
638 | | |
639 | | /* Is the current buffer unshared? We can just move it. |
640 | | */ |
641 | 12.0M | if (old_buffer && |
642 | 551k | old_buffer->ref_count == 1) { |
643 | 525k | if (buffer_move(old_buffer, area)) { |
644 | 0 | vips_buffer_unref(old_buffer); |
645 | 0 | return NULL; |
646 | 0 | } |
647 | | |
648 | 525k | return old_buffer; |
649 | 525k | } |
650 | | |
651 | | /* Fallback ... unref the old one, make a new one. |
652 | | */ |
653 | 12.0M | VIPS_FREEF(vips_buffer_unref, old_buffer); |
654 | 11.5M | if (!(buffer = vips_buffer_new(im, area))) |
655 | 0 | return NULL; |
656 | | |
657 | 11.5M | return buffer; |
658 | 11.5M | } |
659 | | |
660 | | static void |
661 | | buffer_thread_destroy_notify(gpointer data) |
662 | 0 | { |
663 | | /* We only come here if vips_thread_shutdown() was not called for this |
664 | | * thread. Do our best to clean up. |
665 | | * |
666 | | * GPrivate has stopped working by this point in destruction, be |
667 | | * careful not to touch that. |
668 | | */ |
669 | 0 | buffer_thread_free(data); |
670 | 0 | } |
671 | | |
672 | | /* Init the buffer cache system. This is called during vips_init. |
673 | | */ |
674 | | void |
675 | | vips__buffer_init(void) |
676 | 39 | { |
677 | 39 | if (buffer_cache_max_reserve < 1) |
678 | 0 | printf("vips__buffer_init: buffer reserve disabled\n"); |
679 | | |
680 | | #ifdef DEBUG |
681 | | printf("vips__buffer_init: DEBUG enabled\n"); |
682 | | #endif /*DEBUG*/ |
683 | | |
684 | | #ifdef DEBUG_CREATE |
685 | | printf("vips__buffer_init: DEBUG_CREATE enabled\n"); |
686 | | #endif /*DEBUG_CREATE*/ |
687 | 39 | } |
688 | | |
689 | | void |
690 | | vips__buffer_shutdown(void) |
691 | 1.52M | { |
692 | 1.52M | VipsBufferThread *buffer_thread; |
693 | | |
694 | 1.52M | if ((buffer_thread = g_private_get(&buffer_thread_key))) { |
695 | 1.03M | buffer_thread_free(buffer_thread); |
696 | | g_private_set(&buffer_thread_key, NULL); |
697 | 1.03M | } |
698 | 1.52M | } |