diff --git a/data/shaders/drawimage.frag b/data/shaders/drawimage.frag index 81e9085c8c..87a96aa1eb 100644 --- a/data/shaders/drawimage.frag +++ b/data/shaders/drawimage.frag @@ -1,11 +1,12 @@ #version 150 uniform vec4 uPalette[256]; -uniform usampler2D uTexture; +uniform usampler2DArray uTexture; flat in ivec4 fClip; flat in int fFlags; in vec4 fColour; +flat in int fTexAtlasIndex; in vec2 fTexColourCoords; in vec2 fTexMaskCoords; flat in int fMask; @@ -23,8 +24,8 @@ void main() discard; } - vec4 mask = uPalette[texture(uTexture, fTexMaskCoords).r]; - vec4 texel = uPalette[texture(uTexture, fTexColourCoords).r]; + vec4 mask = uPalette[texture(uTexture, vec3(fTexMaskCoords, float(fTexAtlasIndex))).r]; + vec4 texel = uPalette[texture(uTexture, vec3(fTexColourCoords, float(fTexAtlasIndex))).r]; if (fMask != 0) { diff --git a/data/shaders/drawimage.vert b/data/shaders/drawimage.vert index 2b2f8a0097..0ff5c67588 100644 --- a/data/shaders/drawimage.vert +++ b/data/shaders/drawimage.vert @@ -3,6 +3,7 @@ uniform ivec2 uScreenSize; in ivec4 ivClip; +in int ivTexAtlasIndex; in vec4 ivTexColourBounds; in vec4 ivTexMaskBounds; in int ivFlags; @@ -17,6 +18,7 @@ out vec2 fPosition; flat out ivec4 fClip; flat out int fFlags; out vec4 fColour; +flat out int fTexAtlasIndex; out vec2 fTexColourCoords; out vec2 fTexMaskCoords; flat out int fMask; @@ -58,6 +60,7 @@ void main() fFlags = ivFlags; fColour = ivColour; fMask = ivMask; + fTexAtlasIndex = ivTexAtlasIndex; gl_Position = vec4(pos, 0.0, 1.0); } diff --git a/src/drawing/engines/opengl/DrawImageShader.cpp b/src/drawing/engines/opengl/DrawImageShader.cpp index 48adf55b18..7d2b3b1d81 100644 --- a/src/drawing/engines/opengl/DrawImageShader.cpp +++ b/src/drawing/engines/opengl/DrawImageShader.cpp @@ -36,6 +36,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") glBindBuffer(GL_ARRAY_BUFFER, _vboInstances); glVertexAttribIPointer(vClip, 4, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, clip)); + glVertexAttribIPointer(vTexAtlasIndex, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texAtlasIndex)); glVertexAttribPointer(vTexColourBounds, 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texColourBounds)); glVertexAttribPointer(vTexMaskBounds, 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texMaskBounds)); glVertexAttribIPointer(vFlags, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, flags)); @@ -45,6 +46,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") glEnableVertexAttribArray(vIndex); glEnableVertexAttribArray(vClip); + glEnableVertexAttribArray(vTexAtlasIndex); glEnableVertexAttribArray(vTexColourBounds); glEnableVertexAttribArray(vTexMaskBounds); glEnableVertexAttribArray(vFlags); @@ -53,6 +55,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") glEnableVertexAttribArray(vMask); glVertexAttribDivisor(vClip, 1); + glVertexAttribDivisor(vTexAtlasIndex, 1); glVertexAttribDivisor(vTexColourBounds, 1); glVertexAttribDivisor(vTexMaskBounds, 1); glVertexAttribDivisor(vFlags, 1); @@ -81,6 +84,7 @@ void DrawImageShader::GetLocations() vIndex = GetAttributeLocation("vIndex"); vClip = GetAttributeLocation("ivClip"); + vTexAtlasIndex = GetAttributeLocation("ivTexAtlasIndex"); vTexColourBounds = GetAttributeLocation("ivTexColourBounds"); vTexMaskBounds = GetAttributeLocation("ivTexMaskBounds"); vFlags = GetAttributeLocation("ivFlags"); diff --git a/src/drawing/engines/opengl/DrawImageShader.h b/src/drawing/engines/opengl/DrawImageShader.h index 03474f184e..425735ffa7 100644 --- a/src/drawing/engines/opengl/DrawImageShader.h +++ b/src/drawing/engines/opengl/DrawImageShader.h @@ -24,6 +24,7 @@ // Per-instance data for images struct DrawImageInstance { vec4i clip; + int texAtlasIndex; vec4f texColourBounds; vec4f texMaskBounds; int flags; @@ -41,6 +42,7 @@ private: GLuint vIndex; GLuint vClip; + GLuint vTexAtlasIndex; GLuint vTexColourBounds; GLuint vTexMaskBounds; GLuint vFlags; diff --git a/src/drawing/engines/opengl/OpenGLAPI.cpp b/src/drawing/engines/opengl/OpenGLAPI.cpp index 1fb3e165b6..2c738c3436 100644 --- a/src/drawing/engines/opengl/OpenGLAPI.cpp +++ b/src/drawing/engines/opengl/OpenGLAPI.cpp @@ -68,7 +68,8 @@ static const char * TryLoadAllProcAddresses() SetupOpenGLFunction(glTexImage2D); SetupOpenGLFunction(glTexParameteri); SetupOpenGLFunction(glViewport); - SetupOpenGLFunction(glTexSubImage2D); + SetupOpenGLFunction(glTexSubImage3D); + SetupOpenGLFunction(glTexImage3D); // 2.0+ functions SetupOpenGLFunction(glAttachShader); diff --git a/src/drawing/engines/opengl/OpenGLAPI.h b/src/drawing/engines/opengl/OpenGLAPI.h index 2c7e7ce7a1..a3cab1ae9f 100644 --- a/src/drawing/engines/opengl/OpenGLAPI.h +++ b/src/drawing/engines/opengl/OpenGLAPI.h @@ -40,7 +40,8 @@ #define glTexImage2D __static__glTexImage2D #define glTexParameteri __static__glTexParameteri #define glViewport __static__glViewport -#define glTexSubImage2D __static__glTexSubImage2D +#define glTexSubImage3D __static__glTexSubImage3D +#define glTexImage3D __static__glTexImage3D #endif @@ -68,7 +69,8 @@ #undef glTexImage2D #undef glTexParameteri #undef glViewport -#undef glTexSubImage2D +#undef glTexSubImage3D +#undef glTexImage3D // 1.1 function signatures typedef void (APIENTRYP PFNGLBEGINPROC )(GLenum mode); @@ -89,7 +91,8 @@ typedef void (APIENTRYP PFNGLREADPIXELSPROC )(GLint x, GLint y, GLsizei wid typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC )(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC )(GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLVIEWPORTPROC )(GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* data); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC )(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid * data); #ifdef NO_EXTERN_GLAPI // Defines the function pointers @@ -121,7 +124,8 @@ GLAPI_DECL PFNGLREADPIXELSPROC glReadPixels GLAP GLAPI_DECL PFNGLTEXIMAGE2DPROC glTexImage2D GLAPI_SET; GLAPI_DECL PFNGLTEXPARAMETERIPROC glTexParameteri GLAPI_SET; GLAPI_DECL PFNGLVIEWPORTPROC glViewport GLAPI_SET; -GLAPI_DECL PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D GLAPI_SET; +GLAPI_DECL PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D GLAPI_SET; +GLAPI_DECL PFNGLTEXIMAGE3DPROC glTexImage3D GLAPI_SET; // 2.0+ function pointers GLAPI_DECL PFNGLATTACHSHADERPROC glAttachShader GLAPI_SET; diff --git a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp index 384e76b487..5659337ea4 100644 --- a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -943,7 +943,7 @@ void OpenGLDrawingContext::FlushLines() { void OpenGLDrawingContext::FlushImages() { if (_commandBuffers.images.size() == 0) return; - OpenGLAPI::SetTexture(0, GL_TEXTURE_2D, _textureCache->GetAtlasTexture()); + OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetAtlasTextureArray()); std::vector instances; instances.reserve(_commandBuffers.images.size()); @@ -952,8 +952,9 @@ void OpenGLDrawingContext::FlushImages() { DrawImageInstance instance; instance.clip = {command.clip[0], command.clip[1], command.clip[2], command.clip[3]}; - instance.texColourBounds = command.texColour.bounds; - instance.texMaskBounds = command.texMask.bounds; + instance.texAtlasIndex = command.texColour.index; + instance.texColourBounds = command.texColour.normalizedBounds; + instance.texMaskBounds = command.texMask.normalizedBounds; instance.flags = command.flags; instance.colour = command.colour; instance.bounds = {command.bounds[0], command.bounds[1], command.bounds[2], command.bounds[3]}; diff --git a/src/drawing/engines/opengl/TextureCache.cpp b/src/drawing/engines/opengl/TextureCache.cpp index c6bc1d8aa3..74e35dffc7 100644 --- a/src/drawing/engines/opengl/TextureCache.cpp +++ b/src/drawing/engines/opengl/TextureCache.cpp @@ -17,6 +17,7 @@ #ifndef DISABLE_OPENGL #include +#include #include "../../../core/Memory.hpp" #include "TextureCache.h" @@ -41,19 +42,19 @@ void TextureCache::SetPalette(const SDL_Color * palette) void TextureCache::InvalidateImage(uint32 image) { - InitializeAtlasTexture(); + InitialiseAtlases(); auto kvp = _imageTextureMap.find(image); if (kvp != _imageTextureMap.end()) { + _atlases[kvp->second.index].Free(kvp->second); _imageTextureMap.erase(kvp); - _freeSlots.push_back(kvp->second.slot); } } CachedTextureInfo TextureCache::GetOrLoadImageTexture(uint32 image) { - InitializeAtlasTexture(); + InitialiseAtlases(); auto kvp = _imageTextureMap.find(image & 0x7FFFF); if (kvp != _imageTextureMap.end()) @@ -64,14 +65,12 @@ CachedTextureInfo TextureCache::GetOrLoadImageTexture(uint32 image) auto cacheInfo = LoadImageTexture(image); _imageTextureMap[image & 0x7FFFF] = cacheInfo; - //printf("%d slots left\n", (int) _freeSlots.size()); // TODO REMOVE - return cacheInfo; } CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) { - InitializeAtlasTexture(); + InitialiseAtlases(); GlyphId glyphId; glyphId.Image = image; @@ -86,65 +85,49 @@ CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * pale auto cacheInfo = LoadGlyphTexture(image, palette); _glyphTextureMap[glyphId] = cacheInfo; - //printf("%d slots left\n", (int) _freeSlots.size()); // TODO REMOVE - return cacheInfo; } -void TextureCache::InitializeAtlasTexture() { - if (!_atlasTextureInitialized) { - glGenTextures(1, &_atlasTexture); - glBindTexture(GL_TEXTURE_2D, _atlasTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, TEXTURE_CACHE_ATLAS_WIDTH, TEXTURE_CACHE_ATLAS_HEIGHT, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +void TextureCache::InitialiseAtlases() { + if (!_atlasInitialised) { + // Create an array texture to hold all of the atlases + glGenTextures(1, &_atlasTextureArray); + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasTextureArray); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8UI, TEXTURE_CACHE_ATLAS_WIDTH, TEXTURE_CACHE_ATLAS_HEIGHT, _atlases.size(), 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - _freeSlots.resize(TEXTURE_CACHE_MAX_IMAGES); - for (size_t i = 0; i < _freeSlots.size(); i++) _freeSlots[i] = i; - - _atlasTextureInitialized = true; + _atlasInitialised = true; } } CachedTextureInfo TextureCache::LoadImageTexture(uint32 image) { rct_drawpixelinfo * dpi = GetImageAsDPI(image, 0); - - GLuint slot = _freeSlots.back(); - _freeSlots.pop_back(); - - if (dpi->width > TEXTURE_CACHE_MAX_IMAGE_WIDTH) dpi->width = TEXTURE_CACHE_MAX_IMAGE_WIDTH; - if (dpi->height > TEXTURE_CACHE_MAX_IMAGE_HEIGHT) dpi->height = TEXTURE_CACHE_MAX_IMAGE_HEIGHT; - - vec4i coords = CalculateAtlasCoordinates(slot, dpi->width, dpi->height); - - glBindTexture(GL_TEXTURE_2D, _atlasTexture); - glTexSubImage2D(GL_TEXTURE_2D, 0, coords.x, coords.y, dpi->width, dpi->height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); + auto cacheInfo = AllocateFromAppropriateAtlas(dpi->width, dpi->height); + + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasTextureArray); + 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); - return {slot, ConvertToNormalizedCoordinates(coords)}; + return cacheInfo; } CachedTextureInfo TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) { rct_drawpixelinfo * dpi = GetGlyphAsDPI(image, palette); - GLuint slot = _freeSlots.back(); - _freeSlots.pop_back(); + auto cacheInfo = AllocateFromAppropriateAtlas(dpi->width, dpi->height); - if (dpi->width > TEXTURE_CACHE_MAX_IMAGE_WIDTH) dpi->width = TEXTURE_CACHE_MAX_IMAGE_WIDTH; - if (dpi->height > TEXTURE_CACHE_MAX_IMAGE_HEIGHT) dpi->height = TEXTURE_CACHE_MAX_IMAGE_HEIGHT; - - vec4i coords = CalculateAtlasCoordinates(slot, dpi->width, dpi->height); - - glBindTexture(GL_TEXTURE_2D, _atlasTexture); - glTexSubImage2D(GL_TEXTURE_2D, 0, coords.x, coords.y, dpi->width, dpi->height, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasTextureArray); + 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); - return{slot, ConvertToNormalizedCoordinates(coords)}; + return cacheInfo; } void * TextureCache::GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 * outWidth, uint32 * outHeight) @@ -163,6 +146,16 @@ void * TextureCache::GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 return pixels32; } +CachedTextureInfo TextureCache::AllocateFromAppropriateAtlas(int imageWidth, int imageHeight) { + for (Atlas& atlas : _atlases) { + if (atlas.GetFreeSlots() > 0 && atlas.SupportsImage(imageWidth, imageHeight)) { + return atlas.Allocate(imageWidth, imageHeight); + } + } + + throw std::runtime_error("no atlas with free slots left that supports image!"); +} + rct_drawpixelinfo * TextureCache::GetImageAsDPI(uint32 image, uint32 tertiaryColour) { rct_g1_element * g1Element = gfx_get_g1_element(image & 0x7FFFF); @@ -233,28 +226,7 @@ void * TextureCache::ConvertDPIto32bpp(const rct_drawpixelinfo * dpi) void TextureCache::FreeTextures() { // Free array texture - glDeleteTextures(1, &_atlasTexture); -} - -vec4i TextureCache::CalculateAtlasCoordinates(GLuint slot, int width, int height) { - int row = slot / TEXTURE_CACHE_IMAGES_U; - int col = slot % TEXTURE_CACHE_IMAGES_U; - - return vec4i{ - TEXTURE_CACHE_MAX_IMAGE_WIDTH * col, - TEXTURE_CACHE_MAX_IMAGE_HEIGHT * row, - TEXTURE_CACHE_MAX_IMAGE_WIDTH * col + width, - TEXTURE_CACHE_MAX_IMAGE_HEIGHT * row + height, - }; -} - -vec4f TextureCache::ConvertToNormalizedCoordinates(vec4i coordinates) { - return vec4f{ - coordinates.x / (float) TEXTURE_CACHE_ATLAS_WIDTH, - coordinates.y / (float) TEXTURE_CACHE_ATLAS_HEIGHT, - coordinates.z / (float) TEXTURE_CACHE_ATLAS_WIDTH, - coordinates.w / (float) TEXTURE_CACHE_ATLAS_HEIGHT - }; + glDeleteTextures(1, &_atlasTextureArray); } rct_drawpixelinfo * TextureCache::CreateDPI(sint32 width, sint32 height) @@ -280,8 +252,8 @@ void TextureCache::DeleteDPI(rct_drawpixelinfo* dpi) delete dpi; } -GLuint TextureCache::GetAtlasTexture() { - return _atlasTexture; +GLuint TextureCache::GetAtlasTextureArray() { + return _atlasTextureArray; } #endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/TextureCache.h b/src/drawing/engines/opengl/TextureCache.h index 57a549dd9e..17adcc93ee 100644 --- a/src/drawing/engines/opengl/TextureCache.h +++ b/src/drawing/engines/opengl/TextureCache.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include "../../../common.h" #include "OpenGLAPI.h" @@ -52,27 +53,108 @@ struct GlyphId }; // TODO: Derive from hardware limits instead -// TODO: Support > 64x64 images +// TODO: Handle no more slots remaining (allocate more atlases?) +// TODO: Handle images larger than 256x256 constexpr int TEXTURE_CACHE_ATLAS_WIDTH = 8192; constexpr int TEXTURE_CACHE_ATLAS_HEIGHT = 8192; -constexpr int TEXTURE_CACHE_MAX_IMAGE_WIDTH = 64; -constexpr int TEXTURE_CACHE_MAX_IMAGE_HEIGHT = 64; -constexpr int TEXTURE_CACHE_IMAGES_U = TEXTURE_CACHE_ATLAS_WIDTH / TEXTURE_CACHE_MAX_IMAGE_WIDTH; -constexpr int TEXTURE_CACHE_IMAGES_V = TEXTURE_CACHE_ATLAS_HEIGHT / TEXTURE_CACHE_MAX_IMAGE_HEIGHT; -constexpr int TEXTURE_CACHE_MAX_IMAGES = TEXTURE_CACHE_IMAGES_U * TEXTURE_CACHE_IMAGES_V; +// Location of an image (texture atlas index, slot and normalized coordinates) struct CachedTextureInfo { + GLuint index; GLuint slot; - vec4f bounds; + vec4i bounds; + vec4f normalizedBounds; +}; + +// Represents a texture atlas that images of a given maximum size can be allocated from +// Atlases are all stored in the same 2D texture array, occupying the specified index +class Atlas { +private: + GLuint _index; + int _imageWidth, _imageHeight; + std::vector _freeSlots; + + int _cols, _rows; + +public: + Atlas(GLuint index, int imageWidth, int imageHeight) { + _index = index; + _imageWidth = imageWidth; + _imageHeight = imageHeight; + + _cols = TEXTURE_CACHE_ATLAS_WIDTH / imageWidth; + _rows = TEXTURE_CACHE_ATLAS_HEIGHT / imageHeight; + + _freeSlots.resize(_cols * _rows); + for (size_t i = 0; i < _freeSlots.size(); i++) { + _freeSlots[i] = i; + } + } + + CachedTextureInfo Allocate(int actualWidth, int actualHeight) { + assert(_freeSlots.size() > 0); + + GLuint slot = _freeSlots.back(); + _freeSlots.pop_back(); + + auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight); + +#ifdef DEBUG + printf("texture atlas (%d, %d) has %d slots left\n", _imageWidth, _imageHeight, GetFreeSlots()); +#endif + + return {_index, slot, bounds, NormalizeCoordinates(bounds)}; + } + + void Free(const CachedTextureInfo& info) { + assert(_index == info.index); + + _freeSlots.push_back(info.slot); + } + + bool SupportsImage(int actualWidth, int actualHeight) const { + return actualWidth <= _imageWidth && actualHeight <= _imageHeight; + } + + int GetFreeSlots() const { + return (int) _freeSlots.size(); + } + +private: + vec4i GetSlotCoordinates(GLuint slot, int actualWidth, int actualHeight) { + int row = slot / _cols; + int col = slot % _cols; + + return vec4i{ + _imageWidth * col, + _imageHeight * row, + _imageWidth * col + actualWidth, + _imageHeight * row + actualHeight, + }; + } + + static vec4f NormalizeCoordinates(const vec4i& coords) { + return vec4f{ + coords.x / (float) TEXTURE_CACHE_ATLAS_WIDTH, + coords.y / (float) TEXTURE_CACHE_ATLAS_HEIGHT, + coords.z / (float) TEXTURE_CACHE_ATLAS_WIDTH, + coords.w / (float) TEXTURE_CACHE_ATLAS_HEIGHT + }; + } }; class TextureCache { private: - bool _atlasTextureInitialized = false; - GLuint _atlasTexture; + bool _atlasInitialised = false; - std::vector _freeSlots; + GLuint _atlasTextureArray; + + // Atlases should be ordered from small to large image support + std::array _atlases = { + Atlas{0, 64, 64}, + Atlas{1, 256, 256} + }; std::unordered_map _imageTextureMap; std::unordered_map _glyphTextureMap; @@ -87,20 +169,19 @@ public: CachedTextureInfo GetOrLoadImageTexture(uint32 image); CachedTextureInfo GetOrLoadGlyphTexture(uint32 image, uint8 * palette); - GLuint GetAtlasTexture(); + GLuint GetAtlasTextureArray(); private: - void InitializeAtlasTexture(); + void InitialiseAtlases(); CachedTextureInfo LoadImageTexture(uint32 image); CachedTextureInfo LoadGlyphTexture(uint32 image, uint8 * palette); + CachedTextureInfo AllocateFromAppropriateAtlas(int imageWidth, int imageHeight); void * GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 * outWidth, uint32 * outHeight); rct_drawpixelinfo * GetImageAsDPI(uint32 image, uint32 tertiaryColour); void * GetGlyphAsARGB(uint32 image, uint8 * palette, uint32 * outWidth, uint32 * outHeight); rct_drawpixelinfo * GetGlyphAsDPI(uint32 image, uint8 * palette); void * ConvertDPIto32bpp(const rct_drawpixelinfo * dpi); void FreeTextures(); - vec4i CalculateAtlasCoordinates(GLuint slot, int width, int height); - vec4f ConvertToNormalizedCoordinates(vec4i coordinates); static rct_drawpixelinfo * CreateDPI(sint32 width, sint32 height); static void DeleteDPI(rct_drawpixelinfo * dpi);