diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index bb701684db..436f5e287c 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -634,7 +634,7 @@ void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 t right += _clipLeft; bottom += _clipTop; - auto texture = _textureCache->GetOrLoadImageTexture(image); + const auto texture = _textureCache->GetOrLoadImageTexture(image); int paletteCount; ivec3 palettes{}; @@ -673,10 +673,10 @@ void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 t DrawRectCommand& command = _commandBuffers.transparent.allocate(); command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom }; - command.texColourAtlas = texture->index; - command.texColourBounds = texture->normalizedBounds; - command.texMaskAtlas = texture->index; - command.texMaskBounds = texture->normalizedBounds; + command.texColourAtlas = texture.index; + command.texColourBounds = texture.normalizedBounds; + command.texMaskAtlas = texture.index; + command.texMaskBounds = texture.normalizedBounds; command.palettes = palettes; command.colour = palettes.x - (special ? 1 : 0); command.bounds = { left, top, right, bottom }; @@ -688,8 +688,8 @@ void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 t DrawRectCommand& command = _commandBuffers.rects.allocate(); command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom }; - command.texColourAtlas = texture->index; - command.texColourBounds = texture->normalizedBounds; + command.texColourAtlas = texture.index; + command.texColourBounds = texture.normalizedBounds; command.texMaskAtlas = 0; command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f }; command.palettes = palettes; @@ -709,8 +709,8 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskIm return; } - auto textureMask = _textureCache->GetOrLoadImageTexture(maskImage); - auto textureColour = _textureCache->GetOrLoadImageTexture(colourImage); + const auto textureMask = _textureCache->GetOrLoadImageTexture(maskImage); + const auto textureColour = _textureCache->GetOrLoadImageTexture(colourImage); uint8 zoomLevel = (1 << _dpi->zoom_level); @@ -751,10 +751,10 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskIm DrawRectCommand& command = _commandBuffers.rects.allocate(); command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom }; - command.texColourAtlas = textureColour->index; - command.texColourBounds = textureColour->normalizedBounds; - command.texMaskAtlas = textureMask->index; - command.texMaskBounds = textureMask->normalizedBounds; + command.texColourAtlas = textureColour.index; + command.texColourBounds = textureColour.normalizedBounds; + command.texMaskAtlas = textureMask.index; + command.texMaskBounds = textureMask.normalizedBounds; command.palettes = { 0, 0, 0 }; command.flags = DrawRectCommand::FLAG_MASK; command.colour = 0; @@ -773,7 +773,7 @@ void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uin return; } - auto texture = _textureCache->GetOrLoadImageTexture(image); + const auto texture = _textureCache->GetOrLoadImageTexture(image); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; @@ -804,8 +804,8 @@ void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uin command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom }; command.texColourAtlas = 0; command.texColourBounds = { 0.0f, 0.0f, 0.0f, 0.0f }; - command.texMaskAtlas = texture->index; - command.texMaskBounds = texture->normalizedBounds; + command.texMaskAtlas = texture.index; + command.texMaskBounds = texture.normalizedBounds; command.palettes = { 0, 0, 0 }; command.flags = DrawRectCommand::FLAG_NO_TEXTURE | DrawRectCommand::FLAG_MASK; command.colour = colour & 0xFF; @@ -821,7 +821,7 @@ void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * p return; } - auto texture = _textureCache->GetOrLoadGlyphTexture(image, palette); + const auto texture = _textureCache->GetOrLoadGlyphTexture(image, palette); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp index cf3cc5b1ec..df7119cfeb 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.cpp @@ -23,6 +23,13 @@ #include +constexpr uint32 UNUSED_INDEX = 0xFFFFFFFF; + +TextureCache::TextureCache() +{ + std::fill(_indexMap.begin(), _indexMap.end(), UNUSED_INDEX); +} + TextureCache::~TextureCache() { FreeTextures(); @@ -30,31 +37,61 @@ TextureCache::~TextureCache() void TextureCache::InvalidateImage(uint32 image) { - auto kvp = _imageTextureMap.find(image); - if (kvp != _imageTextureMap.end()) + uint32 index = _indexMap[image]; + if (index == UNUSED_INDEX) + return; + + AtlasTextureInfo& elem = _textureCache.at(index); + + _atlases[elem.index].Free(elem); + _indexMap[image] = UNUSED_INDEX; + + if (index == _textureCache.size() - 1) { - _atlases[kvp->second.index].Free(kvp->second); - _imageTextureMap.erase(kvp); + // Last element can be popped back. + _textureCache.pop_back(); + } + else + { + // Swap last element with element to erase and then pop back. + AtlasTextureInfo& last = _textureCache.back(); + + // Move last to current. + elem = last; + + // Change index for moved element. + _indexMap[last.image] = index; + + _textureCache.pop_back(); } } -const CachedTextureInfo* TextureCache::GetOrLoadImageTexture(uint32 image) +BasicTextureInfo TextureCache::GetOrLoadImageTexture(uint32 image) { image &= 0x7FFFF; - auto kvp = _imageTextureMap.find(image); - if (kvp != _imageTextureMap.end()) + uint32 index = _indexMap[image]; + if (index != UNUSED_INDEX) { - return &kvp->second; + const auto& info = _textureCache[index]; + return + { + info.index, + info.normalizedBounds, + }; } - auto cacheInfo = LoadImageTexture(image); - auto cacheItr = _imageTextureMap.insert(std::make_pair(image, cacheInfo)); + index = (uint32)_textureCache.size(); - return &(cacheItr.first->second); + AtlasTextureInfo info = LoadImageTexture(image); + + _textureCache.push_back(info); + _indexMap[image] = index; + + return info; } -CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) +BasicTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) { GlyphId glyphId; glyphId.Image = image; @@ -63,13 +100,18 @@ CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * pale auto kvp = _glyphTextureMap.find(glyphId); if (kvp != _glyphTextureMap.end()) { - return kvp->second; + const auto& info = kvp->second; + return + { + info.index, + info.normalizedBounds, + }; } auto cacheInfo = LoadGlyphTexture(image, palette); - _glyphTextureMap[glyphId] = cacheInfo; + auto it = _glyphTextureMap.insert(std::make_pair(glyphId, cacheInfo)); - return cacheInfo; + return (*it.first).second; } void TextureCache::CreateTextures() @@ -149,33 +191,27 @@ void TextureCache::EnlargeAtlasesTexture(GLuint newEntries) _atlasesTextureIndices = newIndices; } -CachedTextureInfo TextureCache::LoadImageTexture(uint32 image) +AtlasTextureInfo TextureCache::LoadImageTexture(uint32 image) { rct_drawpixelinfo dpi = GetImageAsDPI(image, 0); auto cacheInfo = AllocateImage(dpi.width, dpi.height); + cacheInfo.image = image; glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasesTexture); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, cacheInfo.bounds.x, cacheInfo.bounds.y, cacheInfo.index, dpi.width, dpi.height, 1, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi.bits); DeleteDPI(dpi); - cacheInfo.computedBounds = - { - cacheInfo.normalizedBounds.x, - cacheInfo.normalizedBounds.y, - (cacheInfo.normalizedBounds.z - cacheInfo.normalizedBounds.x) / (float)(cacheInfo.bounds.z - cacheInfo.bounds.x), - (cacheInfo.normalizedBounds.w - cacheInfo.normalizedBounds.y) / (float)(cacheInfo.bounds.w - cacheInfo.bounds.y) - }; - return cacheInfo; } -CachedTextureInfo TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) +AtlasTextureInfo TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) { rct_drawpixelinfo dpi = GetGlyphAsDPI(image, palette); auto cacheInfo = AllocateImage(dpi.width, dpi.height); + cacheInfo.image = image; glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasesTexture); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, cacheInfo.bounds.x, cacheInfo.bounds.y, cacheInfo.index, dpi.width, dpi.height, 1, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi.bits); @@ -185,7 +221,7 @@ CachedTextureInfo TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) return cacheInfo; } -CachedTextureInfo TextureCache::AllocateImage(sint32 imageWidth, sint32 imageHeight) +AtlasTextureInfo TextureCache::AllocateImage(sint32 imageWidth, sint32 imageHeight) { CreateTextures(); @@ -247,7 +283,8 @@ void TextureCache::FreeTextures() { // Free array texture glDeleteTextures(1, &_atlasesTexture); - _imageTextureMap.clear(); + _textureCache.clear(); + std::fill(_indexMap.begin(), _indexMap.end(), UNUSED_INDEX); } rct_drawpixelinfo TextureCache::CreateDPI(sint32 width, sint32 height) diff --git a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h index d6f2abf725..38ebfe958c 100644 --- a/src/openrct2-ui/drawing/engines/opengl/TextureCache.h +++ b/src/openrct2-ui/drawing/engines/opengl/TextureCache.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include "OpenGLAPI.h" @@ -60,14 +61,18 @@ constexpr sint32 TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048; // Must be a power of 2! constexpr sint32 TEXTURE_CACHE_SMALLEST_SLOT = 32; -// Location of an image (texture atlas index, slot and normalized coordinates) -struct CachedTextureInfo +struct BasicTextureInfo { GLuint index; + vec4 normalizedBounds; +}; + +// Location of an image (texture atlas index, slot and normalized coordinates) +struct AtlasTextureInfo : public BasicTextureInfo +{ GLuint slot; ivec4 bounds; - vec4 normalizedBounds; - vec4 computedBounds; + uint32 image; }; // Represents a texture atlas that images of a given maximum size can be allocated from @@ -107,7 +112,7 @@ public: } } - CachedTextureInfo Allocate(sint32 actualWidth, sint32 actualHeight) + AtlasTextureInfo Allocate(sint32 actualWidth, sint32 actualHeight) { assert(_freeSlots.size() > 0); @@ -116,16 +121,16 @@ public: auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight); - return - { - _index, - slot, - bounds, - NormalizeCoordinates(bounds) - }; + AtlasTextureInfo info; + info.index = _index; + info.slot = slot; + info.bounds = bounds; + info.normalizedBounds = NormalizeCoordinates(bounds); + + return info; } - void Free(const CachedTextureInfo& info) + void Free(const AtlasTextureInfo& info) { assert(_index == info.index); @@ -195,18 +200,18 @@ private: GLuint _atlasesTextureIndices = 0; GLint _atlasesTextureIndicesLimit = 0; std::vector _atlases; - - std::unordered_map _glyphTextureMap; - std::unordered_map _imageTextureMap; + std::unordered_map _glyphTextureMap; + std::vector _textureCache; + std::array _indexMap; GLuint _paletteTexture = 0; public: - TextureCache() = default; + TextureCache(); ~TextureCache(); void InvalidateImage(uint32 image); - const CachedTextureInfo* GetOrLoadImageTexture(uint32 image); - CachedTextureInfo GetOrLoadGlyphTexture(uint32 image, uint8 * palette); + BasicTextureInfo GetOrLoadImageTexture(uint32 image); + BasicTextureInfo GetOrLoadGlyphTexture(uint32 image, uint8 * palette); GLuint GetAtlasesTexture(); GLuint GetPaletteTexture(); @@ -216,9 +221,9 @@ private: void CreateTextures(); void GeneratePaletteTexture(); void EnlargeAtlasesTexture(GLuint newEntries); - CachedTextureInfo LoadImageTexture(uint32 image); - CachedTextureInfo LoadGlyphTexture(uint32 image, uint8 * palette); - CachedTextureInfo AllocateImage(sint32 imageWidth, sint32 imageHeight); + AtlasTextureInfo LoadImageTexture(uint32 image); + AtlasTextureInfo LoadGlyphTexture(uint32 image, uint8 * palette); + AtlasTextureInfo AllocateImage(sint32 imageWidth, sint32 imageHeight); rct_drawpixelinfo GetImageAsDPI(uint32 image, uint32 tertiaryColour); rct_drawpixelinfo GetGlyphAsDPI(uint32 image, uint8 * palette); void FreeTextures();