Remove unused string code SCC_STRING_ID
[openttd/fttd.git] / src / spritecache.cpp
blob49f2d7ff4b7893acc7693339439ef1f3c1856821
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file spritecache.cpp Caching of sprites. */
12 #include "stdafx.h"
13 #include "fileio_func.h"
14 #include "spriteloader/grf.hpp"
15 #include "gfx_func.h"
16 #include "error.h"
17 #include "zoom_func.h"
18 #include "settings_type.h"
19 #include "blitter/factory.hpp"
20 #include "core/math_func.hpp"
21 #include "core/mem_func.hpp"
23 #include "table/sprites.h"
24 #include "table/strings.h"
25 #include "table/palette_convert.h"
27 /* Default of 4MB spritecache */
28 uint _sprite_cache_size = 4;
30 typedef SimpleTinyEnumT<SpriteType, byte> SpriteTypeByte;
32 struct SpriteCache {
33 void *ptr;
34 size_t file_pos;
35 uint32 id;
36 uint16 file_slot;
37 int16 lru;
38 SpriteTypeByte type; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
39 bool warned; ///< True iff the user has been warned about incorrect use of this sprite
40 byte container_ver; ///< Container version of the GRF the sprite is from.
44 static uint _spritecache_items = 0;
45 static SpriteCache *_spritecache = NULL;
48 static inline SpriteCache *GetSpriteCache(uint index)
50 return &_spritecache[index];
53 static inline bool IsMapgenSpriteID(SpriteID sprite)
55 return IsInsideMM(sprite, 4845, 4882);
58 static SpriteCache *AllocateSpriteCache(uint index)
60 if (index >= _spritecache_items) {
61 /* Add another 1024 items to the 'pool' */
62 uint items = Align(index + 1, 1024);
64 DEBUG(sprite, 4, "Increasing sprite cache to %u items (" PRINTF_SIZE " bytes)", items, items * sizeof(*_spritecache));
66 _spritecache = ReallocT(_spritecache, items);
68 /* Reset the new items and update the count */
69 memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
70 _spritecache_items = items;
73 return GetSpriteCache(index);
77 struct MemBlock {
78 size_t size;
79 byte data[];
82 static uint _sprite_lru_counter;
83 static MemBlock *_spritecache_ptr;
84 static uint _allocated_sprite_cache_size = 0;
85 static int _compact_cache_counter;
87 static void CompactSpriteCache();
88 static void *AllocSprite(size_t mem_req);
90 /**
91 * Skip the given amount of sprite graphics data.
92 * @param type the type of sprite (compressed etc)
93 * @param num the amount of sprites to skip
94 * @return true if the data could be correctly skipped.
96 bool SkipSpriteData(byte type, uint16 num)
98 if (type & 2) {
99 FioSkipBytes(num);
100 } else {
101 while (num > 0) {
102 int8 i = FioReadByte();
103 if (i >= 0) {
104 int size = (i == 0) ? 0x80 : i;
105 if (size > num) return false;
106 num -= size;
107 FioSkipBytes(size);
108 } else {
109 i = -(i >> 3);
110 num -= i;
111 FioReadByte();
115 return true;
118 /* Check if the given Sprite ID exists */
119 bool SpriteExists(SpriteID id)
121 if (id >= _spritecache_items) return false;
123 /* Special case for Sprite ID zero -- its position is also 0... */
124 if (id == 0) return true;
125 return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0);
129 * Get the sprite type of a given sprite.
130 * @param sprite The sprite to look at.
131 * @return the type of sprite.
133 SpriteType GetSpriteType(SpriteID sprite)
135 if (!SpriteExists(sprite)) return ST_INVALID;
136 return GetSpriteCache(sprite)->type;
140 * Get the (FIOS) file slot of a given sprite.
141 * @param sprite The sprite to look at.
142 * @return the FIOS file slot
144 uint GetOriginFileSlot(SpriteID sprite)
146 if (!SpriteExists(sprite)) return 0;
147 return GetSpriteCache(sprite)->file_slot;
151 * Get a reasonable (upper bound) estimate of the maximum
152 * SpriteID used in OpenTTD; there will be no sprites with
153 * a higher SpriteID, although there might be up to roughly
154 * a thousand unused SpriteIDs below this number.
155 * @note It's actually the number of spritecache items.
156 * @return maximum SpriteID
158 uint GetMaxSpriteID()
160 return _spritecache_items;
163 static bool ResizeSpriteIn(SpriteLoader::Sprite *sprite, ZoomLevel src, ZoomLevel tgt)
165 uint8 scaled_1 = ScaleByZoom(1, (ZoomLevel)(src - tgt));
167 /* Check for possible memory overflow. */
168 if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX) return false;
170 sprite[tgt].width = sprite[src].width * scaled_1;
171 sprite[tgt].height = sprite[src].height * scaled_1;
172 sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
173 sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
175 sprite[tgt].AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
177 SpriteLoader::CommonPixel *dst = sprite[tgt].data;
178 for (int y = 0; y < sprite[tgt].height; y++) {
179 const SpriteLoader::CommonPixel *src_ln = &sprite[src].data[y / scaled_1 * sprite[src].width];
180 for (int x = 0; x < sprite[tgt].width; x++) {
181 *dst = src_ln[x / scaled_1];
182 dst++;
186 return true;
189 static void ResizeSpriteOut(SpriteLoader::Sprite *sprite, ZoomLevel zoom)
191 /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
192 sprite[zoom].width = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom);
193 sprite[zoom].height = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom);
194 sprite[zoom].x_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom);
195 sprite[zoom].y_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom);
197 sprite[zoom].AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
199 SpriteLoader::CommonPixel *dst = sprite[zoom].data;
200 const SpriteLoader::CommonPixel *src = sprite[zoom - 1].data;
201 const SpriteLoader::CommonPixel *src_end = src + sprite[zoom - 1].height * sprite[zoom - 1].width;
203 for (uint y = 0; y < sprite[zoom].height; y++) {
204 const SpriteLoader::CommonPixel *src_ln = src + sprite[zoom - 1].width;
205 assert(src_ln <= src_end);
206 for (uint x = 0; x < sprite[zoom].width; x++) {
207 assert(src < src_ln);
208 if (src + 1 != src_ln && (src + 1)->a != 0) {
209 *dst = *(src + 1);
210 } else {
211 *dst = *src;
213 dst++;
214 src += 2;
216 src = src_ln + sprite[zoom - 1].width;
220 static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom)
222 uint width = sprite->width + pad_left + pad_right;
223 uint height = sprite->height + pad_top + pad_bottom;
225 if (width > UINT16_MAX || height > UINT16_MAX) return false;
227 /* Copy source data and reallocate sprite memory. */
228 SpriteLoader::CommonPixel *src_data = MallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
229 MemCpyT(src_data, sprite->data, sprite->width * sprite->height);
230 sprite->AllocateData(zoom, width * height);
232 /* Copy with padding to destination. */
233 SpriteLoader::CommonPixel *src = src_data;
234 SpriteLoader::CommonPixel *data = sprite->data;
235 for (uint y = 0; y < height; y++) {
236 if (y < pad_top || pad_bottom + y >= height) {
237 /* Top/bottom padding. */
238 MemSetT(data, 0, width);
239 data += width;
240 } else {
241 if (pad_left > 0) {
242 /* Pad left. */
243 MemSetT(data, 0, pad_left);
244 data += pad_left;
247 /* Copy pixels. */
248 MemCpyT(data, src, sprite->width);
249 src += sprite->width;
250 data += sprite->width;
252 if (pad_right > 0) {
253 /* Pad right. */
254 MemSetT(data, 0, pad_right);
255 data += pad_right;
259 free(src_data);
261 /* Update sprite size. */
262 sprite->width = width;
263 sprite->height = height;
264 sprite->x_offs -= pad_left;
265 sprite->y_offs -= pad_top;
267 return true;
270 static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail)
272 /* Get minimum top left corner coordinates. */
273 int min_xoffs = INT32_MAX;
274 int min_yoffs = INT32_MAX;
275 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
276 if (HasBit(sprite_avail, zoom)) {
277 min_xoffs = min(min_xoffs, ScaleByZoom(sprite[zoom].x_offs, zoom));
278 min_yoffs = min(min_yoffs, ScaleByZoom(sprite[zoom].y_offs, zoom));
282 /* Get maximum dimensions taking necessary padding at the top left into account. */
283 int max_width = INT32_MIN;
284 int max_height = INT32_MIN;
285 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
286 if (HasBit(sprite_avail, zoom)) {
287 max_width = max(max_width, ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom), zoom));
288 max_height = max(max_height, ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom), zoom));
292 /* Pad sprites where needed. */
293 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
294 if (HasBit(sprite_avail, zoom)) {
295 /* Scaling the sprite dimensions in the blitter is done with rounding up,
296 * so a negative padding here is not an error. */
297 int pad_left = max(0, sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom));
298 int pad_top = max(0, sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom));
299 int pad_right = max(0, UnScaleByZoom(max_width, zoom) - sprite[zoom].width - pad_left);
300 int pad_bottom = max(0, UnScaleByZoom(max_height, zoom) - sprite[zoom].height - pad_top);
302 if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
303 if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom)) return false;
308 return true;
311 static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
313 /* Create a fully zoomed image if it does not exist */
314 ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
315 if (first_avail != ZOOM_LVL_NORMAL) {
316 if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_NORMAL)) return false;
317 SetBit(sprite_avail, ZOOM_LVL_NORMAL);
320 /* Pad sprites to make sizes match. */
321 if (!PadSprites(sprite, sprite_avail)) return false;
323 /* Create other missing zoom levels */
324 for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
325 if (HasBit(sprite_avail, zoom)) {
326 /* Check that size and offsets match the fully zoomed image. */
327 assert(sprite[zoom].width == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom));
328 assert(sprite[zoom].height == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom));
329 assert(sprite[zoom].x_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom));
330 assert(sprite[zoom].y_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom));
333 /* Zoom level is not available, or unusable, so create it */
334 if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
337 return true;
341 * Load a recolour sprite into memory.
342 * @param file_slot GRF we're reading from.
343 * @param num Size of the sprite in the GRF.
344 * @return Sprite data.
346 static void *ReadRecolourSprite(uint16 file_slot, uint num)
348 /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
349 * number of recolour sprites that are 17 bytes that only exist in DOS
350 * GRFs which are the same as 257 byte recolour sprites, but with the last
351 * 240 bytes zeroed. */
352 static const uint RECOLOUR_SPRITE_SIZE = 257;
353 byte *dest = (byte *)AllocSprite(max(RECOLOUR_SPRITE_SIZE, num));
355 if (_palette_remap_grf[file_slot]) {
356 byte *dest_tmp = AllocaM(byte, max(RECOLOUR_SPRITE_SIZE, num));
358 /* Only a few recolour sprites are less than 257 bytes */
359 if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
360 FioReadBlock(dest_tmp, num);
362 /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
363 for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
364 dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]];
366 } else {
367 FioReadBlock(dest, num);
370 return dest;
374 * Read a sprite from disk.
375 * @param sc Location of sprite.
376 * @param id Sprite number.
377 * @param sprite_type Type of sprite.
378 * @param allocator Allocator function to use.
379 * @return Read sprite data.
381 static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, AllocatorProc *allocator)
383 uint8 file_slot = sc->file_slot;
384 size_t file_pos = sc->file_pos;
386 assert(sprite_type != ST_RECOLOUR);
387 assert(IsMapgenSpriteID(id) == (sprite_type == ST_MAPGEN));
388 assert(sc->type == sprite_type);
390 DEBUG(sprite, 9, "Load sprite %d", id);
392 SpriteLoader::Sprite sprite[ZOOM_LVL_COUNT];
393 uint8 sprite_avail = 0;
394 sprite[ZOOM_LVL_NORMAL].type = sprite_type;
396 SpriteLoaderGrf sprite_loader(sc->container_ver);
397 if (sprite_type != ST_MAPGEN && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) {
398 /* Try for 32bpp sprites first. */
399 sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true);
401 if (sprite_avail == 0) {
402 sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false);
405 if (sprite_avail == 0) {
406 if (sprite_type == ST_MAPGEN) return NULL;
407 if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
408 return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
411 if (sprite_type == ST_MAPGEN) {
412 /* Ugly hack to work around the problem that the old landscape
413 * generator assumes that those sprites are stored uncompressed in
414 * the memory, and they are only read directly by the code, never
415 * send to the blitter. So do not send it to the blitter (which will
416 * result in a data array in the format the blitter likes most), but
417 * extract the data directly and store that as sprite.
418 * Ugly: yes. Other solution: no. Blame the original author or
419 * something ;) The image should really have been a data-stream
420 * (so type = 0xFF basically). */
421 uint num = sprite[ZOOM_LVL_NORMAL].width * sprite[ZOOM_LVL_NORMAL].height;
423 Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
424 s->width = sprite[ZOOM_LVL_NORMAL].width;
425 s->height = sprite[ZOOM_LVL_NORMAL].height;
426 s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
427 s->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
429 SpriteLoader::CommonPixel *src = sprite[ZOOM_LVL_NORMAL].data;
430 byte *dest = s->data;
431 while (num-- > 0) {
432 *dest++ = src->m;
433 src++;
436 return s;
439 if (sprite_type == ST_NORMAL) {
440 if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
441 if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
442 return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
445 return BlitterFactory::GetCurrentBlitter()->Encode(sprite, allocator);
449 /** Map from sprite numbers to position in the GRF file. */
450 static std::map<uint32, size_t> _grf_sprite_offsets;
453 * Get the file offset for a specific sprite in the sprite section of a GRF.
454 * @param id ID of the sprite to look up.
455 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
457 size_t GetGRFSpriteOffset(uint32 id)
459 return _grf_sprite_offsets.find(id) != _grf_sprite_offsets.end() ? _grf_sprite_offsets[id] : SIZE_MAX;
463 * Parse the sprite section of GRFs.
464 * @param container_version Container version of the GRF we're currently processing.
466 void ReadGRFSpriteOffsets(byte container_version)
468 _grf_sprite_offsets.clear();
470 if (container_version >= 2) {
471 /* Seek to sprite section of the GRF. */
472 size_t data_offset = FioReadDword();
473 size_t old_pos = FioGetPos();
474 FioSeekTo(data_offset, SEEK_CUR);
476 /* Loop over all sprite section entries and store the file
477 * offset for each newly encountered ID. */
478 uint32 id, prev_id = 0;
479 while ((id = FioReadDword()) != 0) {
480 if (id != prev_id) _grf_sprite_offsets[id] = FioGetPos() - 4;
481 prev_id = id;
482 FioSkipBytes(FioReadDword());
485 /* Continue processing the data section. */
486 FioSeekTo(old_pos, SEEK_SET);
492 * Load a real or recolour sprite.
493 * @param load_index Global sprite index.
494 * @param file_slot GRF to load from.
495 * @param file_sprite_id Sprite number in the GRF.
496 * @param container_version Container version of the GRF.
497 * @return True if a valid sprite was loaded, false on any error.
499 bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
501 size_t file_pos = FioGetPos();
503 /* Read sprite header. */
504 uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord();
505 if (num == 0) return false;
506 byte grf_type = FioReadByte();
508 SpriteType type;
509 void *data = NULL;
510 if (grf_type == 0xFF) {
511 /* Some NewGRF files have "empty" pseudo-sprites which are 1
512 * byte long. Catch these so the sprites won't be displayed. */
513 if (num == 1) {
514 FioReadByte();
515 return false;
517 type = ST_RECOLOUR;
518 data = ReadRecolourSprite(file_slot, num);
519 } else if (container_version >= 2 && grf_type == 0xFD) {
520 if (num != 4) {
521 /* Invalid sprite section include, ignore. */
522 FioSkipBytes(num);
523 return false;
525 /* It is not an error if no sprite with the provided ID is found in the sprite section. */
526 file_pos = GetGRFSpriteOffset(FioReadDword());
527 type = ST_NORMAL;
528 } else {
529 FioSkipBytes(7);
530 type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
531 /* Inline sprites are not supported for container version >= 2. */
532 if (container_version >= 2) return false;
535 if (type == ST_INVALID) return false;
537 if (load_index >= MAX_SPRITES) {
538 usererror("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
541 bool is_mapgen = IsMapgenSpriteID(load_index);
543 if (is_mapgen) {
544 if (type != ST_NORMAL) usererror("Uhm, would you be so kind not to load a NewGRF that changes the type of the map generator sprites?");
545 type = ST_MAPGEN;
548 SpriteCache *sc = AllocateSpriteCache(load_index);
549 sc->file_slot = file_slot;
550 sc->file_pos = file_pos;
551 sc->ptr = data;
552 sc->lru = 0;
553 sc->id = file_sprite_id;
554 sc->type = type;
555 sc->warned = false;
556 sc->container_ver = container_version;
558 return true;
562 void DupSprite(SpriteID old_spr, SpriteID new_spr)
564 SpriteCache *scnew = AllocateSpriteCache(new_spr); // may reallocate: so put it first
565 SpriteCache *scold = GetSpriteCache(old_spr);
567 scnew->file_slot = scold->file_slot;
568 scnew->file_pos = scold->file_pos;
569 scnew->ptr = NULL;
570 scnew->id = scold->id;
571 scnew->type = scold->type;
572 scnew->warned = false;
573 scnew->container_ver = scold->container_ver;
577 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
578 * If they are non-zero, the block is free.
579 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
580 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
582 static const size_t S_FREE_MASK = sizeof(size_t) - 1;
584 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
585 assert_compile(sizeof(MemBlock) == sizeof(size_t));
586 /* make sure it's a power of two */
587 assert_compile((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
589 static inline MemBlock *NextBlock(MemBlock *block)
591 return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
594 static size_t GetSpriteCacheUsage()
596 size_t tot_size = 0;
597 MemBlock *s;
599 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
600 if (!(s->size & S_FREE_MASK)) tot_size += s->size;
603 return tot_size;
607 void IncreaseSpriteLRU()
609 /* Increase all LRU values */
610 if (_sprite_lru_counter > 16384) {
611 SpriteID i;
613 DEBUG(sprite, 3, "Fixing lru %u, inuse=" PRINTF_SIZE, _sprite_lru_counter, GetSpriteCacheUsage());
615 for (i = 0; i != _spritecache_items; i++) {
616 SpriteCache *sc = GetSpriteCache(i);
617 if (sc->ptr != NULL) {
618 if (sc->lru >= 0) {
619 sc->lru = -1;
620 } else if (sc->lru != -32768) {
621 sc->lru--;
625 _sprite_lru_counter = 0;
628 /* Compact sprite cache every now and then. */
629 if (++_compact_cache_counter >= 740) {
630 CompactSpriteCache();
631 _compact_cache_counter = 0;
636 * Called when holes in the sprite cache should be removed.
637 * That is accomplished by moving the cached data.
639 static void CompactSpriteCache()
641 MemBlock *s;
643 DEBUG(sprite, 3, "Compacting sprite cache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
645 for (s = _spritecache_ptr; s->size != 0;) {
646 if (s->size & S_FREE_MASK) {
647 MemBlock *next = NextBlock(s);
648 MemBlock temp;
649 SpriteID i;
651 /* Since free blocks are automatically coalesced, this should hold true. */
652 assert(!(next->size & S_FREE_MASK));
654 /* If the next block is the sentinel block, we can safely return */
655 if (next->size == 0) break;
657 /* Locate the sprite belonging to the next pointer. */
658 for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
659 assert(i != _spritecache_items);
662 GetSpriteCache(i)->ptr = s->data; // Adjust sprite array entry
663 /* Swap this and the next block */
664 temp = *s;
665 memmove(s, next, next->size);
666 s = NextBlock(s);
667 *s = temp;
669 /* Coalesce free blocks */
670 while (NextBlock(s)->size & S_FREE_MASK) {
671 s->size += NextBlock(s)->size & ~S_FREE_MASK;
673 } else {
674 s = NextBlock(s);
680 * Delete a single entry from the sprite cache.
681 * @param item Entry to delete.
683 static void DeleteEntryFromSpriteCache(uint item)
685 /* Mark the block as free (the block must be in use) */
686 MemBlock *s = (MemBlock*)GetSpriteCache(item)->ptr - 1;
687 assert(!(s->size & S_FREE_MASK));
688 s->size |= S_FREE_MASK;
689 GetSpriteCache(item)->ptr = NULL;
691 /* And coalesce adjacent free blocks */
692 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
693 if (s->size & S_FREE_MASK) {
694 while (NextBlock(s)->size & S_FREE_MASK) {
695 s->size += NextBlock(s)->size & ~S_FREE_MASK;
701 static void DeleteEntryFromSpriteCache()
703 uint best = UINT_MAX;
704 int cur_lru;
706 DEBUG(sprite, 3, "DeleteEntryFromSpriteCache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
708 cur_lru = 0xffff;
709 for (SpriteID i = 0; i != _spritecache_items; i++) {
710 SpriteCache *sc = GetSpriteCache(i);
711 if (sc->type != ST_RECOLOUR && sc->ptr != NULL && sc->lru < cur_lru) {
712 cur_lru = sc->lru;
713 best = i;
717 /* Display an error message and die, in case we found no sprite at all.
718 * This shouldn't really happen, unless all sprites are locked. */
719 if (best == UINT_MAX) error("Out of sprite memory");
721 DeleteEntryFromSpriteCache(best);
724 static void *AllocSprite(size_t mem_req)
726 mem_req += sizeof(MemBlock);
728 /* Align this to correct boundary. This also makes sure at least one
729 * bit is not used, so we can use it for other things. */
730 mem_req = Align(mem_req, S_FREE_MASK + 1);
732 for (;;) {
733 MemBlock *s;
735 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
736 if (s->size & S_FREE_MASK) {
737 size_t cur_size = s->size & ~S_FREE_MASK;
739 /* Is the block exactly the size we need or
740 * big enough for an additional free block? */
741 if (cur_size == mem_req ||
742 cur_size >= mem_req + sizeof(MemBlock)) {
743 /* Set size and in use */
744 s->size = mem_req;
746 /* Do we need to inject a free block too? */
747 if (cur_size != mem_req) {
748 NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
751 return s->data;
756 /* Reached sentinel, but no block found yet. Delete some old entry. */
757 DeleteEntryFromSpriteCache();
762 * Handles the case when a sprite of different type is requested than is present in the SpriteCache.
763 * For ST_FONT sprites, it is normal. In other cases, default sprite is loaded instead.
764 * @param sprite ID of loaded sprite
765 * @param requested requested sprite type
766 * @param sc the currently known sprite cache for the requested sprite
767 * @return fallback sprite
768 * @note this function will do usererror() in the case the fallback sprite isn't available
770 static void *HandleInvalidSpriteRequest(SpriteID sprite, SpriteType requested, SpriteCache *sc, AllocatorProc *allocator)
772 static const char * const sprite_types[] = {
773 "normal", // ST_NORMAL
774 "map generator", // ST_MAPGEN
775 "character", // ST_FONT
776 "recolour", // ST_RECOLOUR
779 SpriteType available = sc->type;
780 if (requested == ST_FONT && available == ST_NORMAL) {
781 if (sc->ptr == NULL) sc->type = ST_FONT;
782 return GetRawSprite(sprite, sc->type, allocator);
785 byte warning_level = sc->warned ? 6 : 0;
786 sc->warned = true;
787 DEBUG(sprite, warning_level, "Tried to load %s sprite #%d as a %s sprite. Probable cause: NewGRF interference", sprite_types[available], sprite, sprite_types[requested]);
789 switch (requested) {
790 case ST_NORMAL:
791 if (sprite == SPR_IMG_QUERY) usererror("Uhm, would you be so kind not to load a NewGRF that makes the 'query' sprite a non-normal sprite?");
792 /* FALL THROUGH */
793 case ST_FONT:
794 return GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
795 case ST_RECOLOUR:
796 if (sprite == PALETTE_TO_DARK_BLUE) usererror("Uhm, would you be so kind not to load a NewGRF that makes the 'PALETTE_TO_DARK_BLUE' sprite a non-remap sprite?");
797 return GetRawSprite(PALETTE_TO_DARK_BLUE, ST_RECOLOUR, allocator);
798 case ST_MAPGEN:
799 /* this shouldn't happen, overriding of ST_MAPGEN sprites is checked in LoadNextSprite()
800 * (the only case the check fails is when these sprites weren't even loaded...) */
801 default:
802 NOT_REACHED();
807 * Reads a sprite (from disk or sprite cache).
808 * If the sprite is not available or of wrong type, a fallback sprite is returned.
809 * @param sprite Sprite to read.
810 * @param type Expected sprite type.
811 * @param allocator Allocator function to use. Set to NULL to use the usual sprite cache.
812 * @return Sprite raw data
814 void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator)
816 assert(type != ST_MAPGEN || IsMapgenSpriteID(sprite));
817 assert(type < ST_INVALID);
819 if (!SpriteExists(sprite)) {
820 DEBUG(sprite, 1, "Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", sprite);
822 /* SPR_IMG_QUERY is a BIG FAT RED ? */
823 sprite = SPR_IMG_QUERY;
826 SpriteCache *sc = GetSpriteCache(sprite);
828 if (sc->type != type) return HandleInvalidSpriteRequest(sprite, type, sc, allocator);
830 if (allocator == NULL) {
831 /* Load sprite into/from spritecache */
833 /* Update LRU */
834 sc->lru = ++_sprite_lru_counter;
836 /* Load the sprite, if it is not loaded, yet */
837 if (sc->ptr == NULL) sc->ptr = ReadSprite(sc, sprite, type, AllocSprite);
839 return sc->ptr;
840 } else {
841 /* Do not use the spritecache, but a different allocator. */
842 return ReadSprite(sc, sprite, type, allocator);
847 static void GfxInitSpriteCache()
849 /* initialize sprite cache heap */
850 int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
851 uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
853 /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
854 static uint last_alloc_attempt = 0;
856 if (_spritecache_ptr == NULL || (_allocated_sprite_cache_size != target_size && target_size != last_alloc_attempt)) {
857 delete[] reinterpret_cast<byte *>(_spritecache_ptr);
859 last_alloc_attempt = target_size;
860 _allocated_sprite_cache_size = target_size;
862 do {
863 try {
864 /* Try to allocate 50% more to make sure we do not allocate almost all available. */
865 _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size + _allocated_sprite_cache_size / 2]);
866 } catch (std::bad_alloc &) {
867 _spritecache_ptr = NULL;
870 if (_spritecache_ptr != NULL) {
871 /* Allocation succeeded, but we wanted less. */
872 delete[] reinterpret_cast<byte *>(_spritecache_ptr);
873 _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size]);
874 } else if (_allocated_sprite_cache_size < 2 * 1024 * 1024) {
875 usererror("Cannot allocate spritecache");
876 } else {
877 /* Try again to allocate half. */
878 _allocated_sprite_cache_size >>= 1;
880 } while (_spritecache_ptr == NULL);
882 if (_allocated_sprite_cache_size != target_size) {
883 DEBUG(misc, 0, "Not enough memory to allocate %d MiB of spritecache. Spritecache was reduced to %d MiB.", target_size / 1024 / 1024, _allocated_sprite_cache_size / 1024 / 1024);
885 ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG);
886 msg.SetDParam(0, target_size);
887 msg.SetDParam(1, _allocated_sprite_cache_size);
888 ScheduleErrorMessage(msg);
892 /* A big free block */
893 _spritecache_ptr->size = (_allocated_sprite_cache_size - sizeof(MemBlock)) | S_FREE_MASK;
894 /* Sentinel block (identified by size == 0) */
895 NextBlock(_spritecache_ptr)->size = 0;
898 void GfxInitSpriteMem()
900 GfxInitSpriteCache();
902 /* Reset the spritecache 'pool' */
903 free(_spritecache);
904 _spritecache_items = 0;
905 _spritecache = NULL;
907 _compact_cache_counter = 0;
911 * Remove all encoded sprites from the sprite cache without
912 * discarding sprite location information.
914 void GfxClearSpriteCache()
916 /* Clear sprite ptr for all cached items */
917 for (uint i = 0; i != _spritecache_items; i++) {
918 SpriteCache *sc = GetSpriteCache(i);
919 if (sc->type != ST_RECOLOUR && sc->ptr != NULL) DeleteEntryFromSpriteCache(i);
923 /* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer[ZOOM_LVL_COUNT];