From c49c373361f65d4554af519fd24d457b4e62ce42 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 13 Dec 2023 19:04:52 +0000 Subject: [PATCH] Spritecache: Move SpriteCache, SpriteDataBuffer to internal header --- src/spritecache.cpp | 181 +-------------------------------- src/spritecache_internal.h | 202 +++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 177 deletions(-) create mode 100644 src/spritecache_internal.h diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 4e91f99867e..e72a0b32d26 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -20,6 +20,8 @@ #include "core/mem_func.hpp" #include "video/video_driver.hpp" #include "scope_info.h" +#include "spritecache.h" +#include "spritecache_internal.h" #include "table/sprites.h" #include "table/strings.h" @@ -32,185 +34,15 @@ #include "safeguards.h" -static const uint RECOLOUR_SPRITE_SIZE = 257; - /* Default of 4MB spritecache */ uint _sprite_cache_size = 4; -static size_t _spritecache_bytes_used = 0; +size_t _spritecache_bytes_used = 0; static uint32 _sprite_lru_counter; static uint32 _spritecache_prune_events = 0; static size_t _spritecache_prune_entries = 0; static size_t _spritecache_prune_total = 0; -struct SpriteCache; - -class SpriteDataBuffer { - friend SpriteCache; - - std::unique_ptr ptr; - uint32 size = 0; - -public: - void *GetPtr() { return this->ptr.get(); } - uint32 GetSize() { return this->size; } - - void Allocate(uint32 size) - { - this->ptr.reset(MallocT(size)); - this->size = size; - } - - void Clear() - { - this->ptr.reset(); - this->size = 0; - } -}; - -struct SpriteCache { - SpriteFile *file; ///< The file the sprite in this entry can be found in. - size_t file_pos; - -private: - std::unique_ptr ptr; - -public: - uint32 id; - uint count; - - SpriteType 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. - uint8 total_missing_zoom_levels = 0; ///< Zoom levels missing entirely - uint16 flags; ///< Control flags, see SpriteCacheCtrlFlags - - void *GetPtr() { return this->ptr.get(); } - const void *GetPtr() const { return this->ptr.get(); } - - SpriteType GetType() const { return this->type; } - void SetType(SpriteType type) { this->type = type; } - bool GetWarned() const { return HasBit(this->flags, SCCF_WARNED); } - void SetWarned(bool warned) { SB(this->flags, SCCF_WARNED, 1, warned ? 1 : 0); } - bool GetHasPalette() const { return GB(this->flags, SCC_PAL_ZOOM_START, 6) != 0; } - bool GetHasNonPalette() const { return GB(this->flags, SCC_32BPP_ZOOM_START, 6) != 0; } - -private: - void Deallocate() - { - if (!this->ptr) return; - - if (this->GetType() == SpriteType::Recolour) { - _spritecache_bytes_used -= RECOLOUR_SPRITE_SIZE; - free(this->ptr.release()); - return; - } - - Sprite *p = (Sprite *)this->ptr.release(); - while (p != nullptr) { - Sprite *next = p->next; - _spritecache_bytes_used -= p->size; - free(p); - p = next; - } - } - - Sprite *GetSpritePtr() { return (Sprite *)this->ptr.get(); } - -public: - void Clear() - { - this->Deallocate(); - this->total_missing_zoom_levels = 0; - } - - void RemoveByMissingZoomLevels(uint8 lvls) - { - Sprite *base = this->GetSpritePtr(); - if (base == nullptr) { - return; - } - if (base->missing_zoom_levels == lvls) { - /* erase top level entry */ - this->ptr.reset(base->next); - _spritecache_bytes_used -= base->size; - free(base); - base = this->GetSpritePtr(); - } - if (base == nullptr) { - this->total_missing_zoom_levels = 0; - return; - } - this->total_missing_zoom_levels = base->missing_zoom_levels; - Sprite *sp = base; - while (sp != nullptr) { - this->total_missing_zoom_levels &= sp->missing_zoom_levels; - - if (sp->next != nullptr && sp->next->missing_zoom_levels == lvls) { - /* found entry to erase */ - _spritecache_bytes_used -= sp->next->size; - Sprite *new_next = sp->next->next; - free(sp->next); - sp->next = new_next; - } - - sp = sp->next; - } - } - - void Assign(SpriteDataBuffer &&other) - { - this->Clear(); - if (!other.ptr) return; - - this->ptr.reset(other.ptr.release()); - if (this->GetType() == SpriteType::Recolour) { - _spritecache_bytes_used += RECOLOUR_SPRITE_SIZE; - } else { - this->GetSpritePtr()->size = other.size; - _spritecache_bytes_used += other.size; - if (this->GetType() == SpriteType::Normal) { - this->total_missing_zoom_levels = this->GetSpritePtr()->missing_zoom_levels; - } - } - other.size = 0; - } - - void Append(SpriteDataBuffer &&other) - { - assert(this->GetType() == SpriteType::Normal); - - if (!this->ptr || this->total_missing_zoom_levels == UINT8_MAX) { - /* Top level has no data or no zoom levels at all, it's safe to replace it because it cannot be cached for a render job */ - this->Assign(std::move(other)); - return; - } - - Sprite *sp = (Sprite *)other.ptr.release(); - if (sp == nullptr) return; - - sp->size = other.size; - - Sprite *p = this->GetSpritePtr(); - while (p->next != nullptr) { - p = p->next; - } - p->next = sp; - this->total_missing_zoom_levels &= sp->missing_zoom_levels; - _spritecache_bytes_used += other.size; - other.size = 0; - } - - ~SpriteCache() - { - this->Clear(); - } - - SpriteCache() = default; - SpriteCache(const SpriteCache &other) = delete; - SpriteCache(SpriteCache &&other) = default; - SpriteCache& operator=(const SpriteCache &other) = delete; - SpriteCache& operator=(SpriteCache &&other) = default; -}; - static std::vector _spritecache; static SpriteDataBuffer _last_sprite_allocation; static std::vector> _sprite_files; @@ -220,12 +52,7 @@ static inline SpriteCache *GetSpriteCache(uint index) return &_spritecache[index]; } -static inline bool IsMapgenSpriteID(SpriteID sprite) -{ - return IsInsideMM(sprite, SPR_MAPGEN_BEGIN, SPR_MAPGEN_END); -} - -static SpriteCache *AllocateSpriteCache(uint index) +SpriteCache *AllocateSpriteCache(uint index) { if (index >= _spritecache.size()) { _spritecache.resize(index + 1); diff --git a/src/spritecache_internal.h b/src/spritecache_internal.h new file mode 100644 index 00000000000..6f17523126b --- /dev/null +++ b/src/spritecache_internal.h @@ -0,0 +1,202 @@ +/* + * This file is part of OpenTTD. + * 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. + * 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. + * 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 . + */ + +/** @file spritecache_internal.h Internal functions to cache sprites in memory. */ + +#ifndef SPRITECACHE_INTERNAL_H +#define SPRITECACHE_INTERNAL_H + +#include "stdafx.h" + +#include "core/math_func.hpp" +#include "gfx_type.h" +#include "spriteloader/spriteloader.hpp" + +#include "table/sprites.h" + +/* These declarations are internal to spritecache but need to be exposed for unit-tests. */ + +extern size_t _spritecache_bytes_used; + +static const uint RECOLOUR_SPRITE_SIZE = 257; + +struct SpriteCache; + +class SpriteDataBuffer { + friend SpriteCache; + + std::unique_ptr ptr; + uint32 size = 0; + +public: + void *GetPtr() { return this->ptr.get(); } + uint32 GetSize() { return this->size; } + + void Allocate(uint32 size) + { + this->ptr.reset(MallocT(size)); + this->size = size; + } + + void Clear() + { + this->ptr.reset(); + this->size = 0; + } +}; + +struct SpriteCache { + SpriteFile *file; ///< The file the sprite in this entry can be found in. + size_t file_pos; + +private: + std::unique_ptr ptr; + +public: + uint32 id; + uint count; + + SpriteType 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. + uint8 total_missing_zoom_levels = 0; ///< Zoom levels missing entirely + uint16 flags; ///< Control flags, see SpriteCacheCtrlFlags + + void *GetPtr() { return this->ptr.get(); } + const void *GetPtr() const { return this->ptr.get(); } + + SpriteType GetType() const { return this->type; } + void SetType(SpriteType type) { this->type = type; } + bool GetWarned() const { return HasBit(this->flags, SCCF_WARNED); } + void SetWarned(bool warned) { SB(this->flags, SCCF_WARNED, 1, warned ? 1 : 0); } + bool GetHasPalette() const { return GB(this->flags, SCC_PAL_ZOOM_START, 6) != 0; } + bool GetHasNonPalette() const { return GB(this->flags, SCC_32BPP_ZOOM_START, 6) != 0; } + +private: + void Deallocate() + { + if (!this->ptr) return; + + if (this->GetType() == SpriteType::Recolour) { + _spritecache_bytes_used -= RECOLOUR_SPRITE_SIZE; + free(this->ptr.release()); + return; + } + + Sprite *p = (Sprite *)this->ptr.release(); + while (p != nullptr) { + Sprite *next = p->next; + _spritecache_bytes_used -= p->size; + free(p); + p = next; + } + } + + Sprite *GetSpritePtr() { return (Sprite *)this->ptr.get(); } + +public: + void Clear() + { + this->Deallocate(); + this->total_missing_zoom_levels = 0; + } + + void RemoveByMissingZoomLevels(uint8 lvls) + { + Sprite *base = this->GetSpritePtr(); + if (base == nullptr) { + return; + } + if (base->missing_zoom_levels == lvls) { + /* erase top level entry */ + this->ptr.reset(base->next); + _spritecache_bytes_used -= base->size; + free(base); + base = this->GetSpritePtr(); + } + if (base == nullptr) { + this->total_missing_zoom_levels = 0; + return; + } + this->total_missing_zoom_levels = base->missing_zoom_levels; + Sprite *sp = base; + while (sp != nullptr) { + this->total_missing_zoom_levels &= sp->missing_zoom_levels; + + if (sp->next != nullptr && sp->next->missing_zoom_levels == lvls) { + /* found entry to erase */ + _spritecache_bytes_used -= sp->next->size; + Sprite *new_next = sp->next->next; + free(sp->next); + sp->next = new_next; + } + + sp = sp->next; + } + } + + void Assign(SpriteDataBuffer &&other) + { + this->Clear(); + if (!other.ptr) return; + + this->ptr.reset(other.ptr.release()); + if (this->GetType() == SpriteType::Recolour) { + _spritecache_bytes_used += RECOLOUR_SPRITE_SIZE; + } else { + this->GetSpritePtr()->size = other.size; + _spritecache_bytes_used += other.size; + if (this->GetType() == SpriteType::Normal) { + this->total_missing_zoom_levels = this->GetSpritePtr()->missing_zoom_levels; + } + } + other.size = 0; + } + + void Append(SpriteDataBuffer &&other) + { + assert(this->GetType() == SpriteType::Normal); + + if (!this->ptr || this->total_missing_zoom_levels == UINT8_MAX) { + /* Top level has no data or no zoom levels at all, it's safe to replace it because it cannot be cached for a render job */ + this->Assign(std::move(other)); + return; + } + + Sprite *sp = (Sprite *)other.ptr.release(); + if (sp == nullptr) return; + + sp->size = other.size; + + Sprite *p = this->GetSpritePtr(); + while (p->next != nullptr) { + p = p->next; + } + p->next = sp; + this->total_missing_zoom_levels &= sp->missing_zoom_levels; + _spritecache_bytes_used += other.size; + other.size = 0; + } + + ~SpriteCache() + { + this->Clear(); + } + + SpriteCache() = default; + SpriteCache(const SpriteCache &other) = delete; + SpriteCache(SpriteCache &&other) = default; + SpriteCache& operator=(const SpriteCache &other) = delete; + SpriteCache& operator=(SpriteCache &&other) = default; +}; + +static inline bool IsMapgenSpriteID(SpriteID sprite) +{ + return IsInsideMM(sprite, SPR_MAPGEN_BEGIN, SPR_MAPGEN_END); +} + +SpriteCache *AllocateSpriteCache(uint index); + +#endif /* SPRITECACHE_INTERNAL_H */