From 5dae37f55882c4f777f2bde981a0c61719568442 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 23 Aug 2022 22:24:24 +0200 Subject: [PATCH] Added support for caching SVG images. --- es-core/src/components/BadgeComponent.cpp | 2 +- es-core/src/components/FlexboxComponent.h | 2 +- es-core/src/components/ImageComponent.cpp | 63 +++++++---- es-core/src/components/ImageComponent.h | 7 +- es-core/src/components/NinePatchComponent.cpp | 17 +-- es-core/src/components/RatingComponent.cpp | 21 +++- .../components/primary/CarouselComponent.h | 4 +- es-core/src/resources/TextureDataManager.cpp | 6 +- es-core/src/resources/TextureResource.cpp | 104 +++++++++++------- es-core/src/resources/TextureResource.h | 11 +- 10 files changed, 147 insertions(+), 90 deletions(-) diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp index 6eaf09fd9..87d4c5753 100644 --- a/es-core/src/components/BadgeComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -313,7 +313,7 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, ImageComponent badgeImage {false, false}; badgeImage.setImage(mBadgeIcons[slot]); item.baseImage = badgeImage; - item.overlayImage = ImageComponent {}; + item.overlayImage = ImageComponent {false, false}; if (slot == "folder") { if (elem->has("customFolderLinkIcon")) diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index e525808cc..529eb01c6 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -23,7 +23,7 @@ public: ImageComponent baseImage; // Optional overlay image that can be sized and positioned relative to the base image. - ImageComponent overlayImage; + ImageComponent overlayImage {false, false}; glm::vec2 overlayPosition {0.5f, 0.5f}; float overlaySize {0.5f}; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 2d13b6ce0..af1d17bd5 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -52,13 +52,13 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic) updateColors(); } -void ImageComponent::resize() +void ImageComponent::resize(bool rasterize) { if (!mTexture) return; const glm::vec2 textureSize {mTexture->getSourceImageSize()}; - if (textureSize == glm::vec2 {}) + if (textureSize == glm::vec2 {0.0f, 0.0f}) return; if (mTexture->isTiled()) { @@ -95,14 +95,14 @@ void ImageComponent::resize() mSize.x *= resizeScale.x; mSize.y *= resizeScale.x; - float cropPercent = (mSize.y - mTargetSize.y) / (mSize.y * 2.0f); + float cropPercent {(mSize.y - mTargetSize.y) / (mSize.y * 2.0f)}; crop(0.0f, cropPercent, 0.0f, cropPercent); } else { mSize.x *= resizeScale.y; mSize.y *= resizeScale.y; - float cropPercent = (mSize.x - mTargetSize.x) / (mSize.x * 2.0f); + float cropPercent {(mSize.x - mTargetSize.x) / (mSize.x * 2.0f)}; crop(cropPercent, 0.0f, cropPercent, 0.0f); } mSize.y = std::max(mSize.y, mTargetSize.y); @@ -111,7 +111,7 @@ void ImageComponent::resize() else { // If both components are set, we just stretch. // If no components are set, we don't resize at all. - mSize = mTargetSize == glm::vec2 {} ? textureSize : mTargetSize; + mSize = mTargetSize == glm::vec2 {0.0f, 0.0f} ? textureSize : mTargetSize; // If only one component is set, we resize in a way that maintains aspect ratio. if (!mTargetSize.x && mTargetSize.y) { @@ -130,9 +130,10 @@ void ImageComponent::resize() mSize.x = glm::clamp(mSize.x, 1.0f, mRenderer->getScreenWidth() * 2.0f); mSize.y = glm::clamp(mSize.y, 1.0f, mRenderer->getScreenHeight() * 2.0f); - mTexture->rasterizeAt(mSize.x, mSize.y); - - onSizeChanged(); + if (rasterize) { + mTexture->rasterizeAt(mSize.x, mSize.y); + onSizeChanged(); + } } void ImageComponent::setImage(const std::string& path, bool tile) @@ -143,18 +144,40 @@ void ImageComponent::setImage(const std::string& path, bool tile) mDynamic = false; } + const bool isScalable {path != "" ? Utils::String::toLower(path.substr( + path.size() - 4, std::string::npos)) == ".svg" : + false}; + + // Create an initial blank texture if needed. if (path.empty() || !ResourceManager::getInstance().fileExists(path)) { if (mDefaultPath.empty() || !ResourceManager::getInstance().fileExists(mDefaultPath)) mTexture.reset(); else mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic, mLinearInterpolation); + resize(true); } else { + // For raster images we just load and resize but for SVG images we first need to resize + // without rasterizing in order to calculate the correct image size. Then we delete and + // reload the texture at the requested size in order to add a valid cache entry. Finally + // we perform the actual rasterization to have the cache entry updated with the proper + // texture. For SVG images this requires that every call to setImage is made only after + // a call to setResize or setMaxSize (so the requested size is known upfront). mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation); - } - resize(); + if (isScalable) { + resize(false); + mTexture.reset(); + mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, mLinearInterpolation, + false, mSize.x, mSize.y); + mTexture->rasterizeAt(mSize.x, mSize.y); + onSizeChanged(); + } + else { + resize(true); + } + } } void ImageComponent::setImage(const char* data, size_t length, bool tile) @@ -190,14 +213,6 @@ void ImageComponent::setMaxSize(const float width, const float height) resize(); } -void ImageComponent::setMinSize(const float width, const float height) -{ - mTargetSize = glm::vec2 {width, height}; - mTargetIsMax = false; - mTargetIsMin = true; - resize(); -} - void ImageComponent::cropLeft(const float percent) { assert(percent >= 0.0f && percent <= 1.0f); @@ -238,10 +253,10 @@ void ImageComponent::uncrop() void ImageComponent::cropTransparentPadding(const float maxSizeX, const float maxSizeY) { - if (mSize == glm::vec2 {}) + if (mSize == glm::vec2 {0.0f, 0.0f}) return; - std::vector imageRGBA = mTexture.get()->getRawRGBAData(); + std::vector imageRGBA {mTexture.get()->getRawRGBAData()}; if (imageRGBA.size() == 0) return; @@ -273,7 +288,7 @@ void ImageComponent::cropTransparentPadding(const float maxSizeX, const float ma mSize.x -= mSize.x * (cropLeft + cropRight); mSize.y -= mSize.y * (cropTop + cropBottom); - float scaleFactor = originalSize.y / mSize.y; + float scaleFactor {originalSize.y / mSize.y}; if (scaleFactor * mSize.x < maxSizeX) scaleFactor = maxSizeX / mSize.x; @@ -347,7 +362,7 @@ void ImageComponent::updateVertices() // We go through this mess to make sure everything is properly rounded. // If we just round vertices at the end, edge cases occur near sizes of 0.5. - const glm::vec2 topLeft {}; + const glm::vec2 topLeft {0.0f, 0.0f}; const glm::vec2 bottomRight {mSize}; const float px {mTexture->isTiled() ? mSize.x / getTextureSize().x : 1.0f}; const float py {mTexture->isTiled() ? mSize.y / getTextureSize().y : 1.0f}; @@ -492,7 +507,7 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, (properties ^ ThemeFlags::SIZE) | ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); - const ThemeData::ThemeElement* elem = theme->getElement(view, element, "image"); + const ThemeData::ThemeElement* elem {theme->getElement(view, element, "image")}; if (!elem) return; @@ -543,7 +558,7 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, setDefaultImage(elem->get("default")); if (properties & PATH && elem->has("path")) { - bool tile = (elem->has("tile") && elem->get("tile")); + bool tile {elem->has("tile") && elem->get("tile")}; setImage(elem->get("path"), tile); } diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index da6857008..429ecc480 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -46,9 +46,6 @@ public: void setMaxSize(const float width, const float height); void setMaxSize(const glm::vec2& size) { setMaxSize(size.x, size.y); } - void setMinSize(const float width, const float height); - void setMinSize(const glm::vec2& size) { setMinSize(size.x, size.y); } - glm::vec2 getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; } // Applied AFTER image positioning and sizing. @@ -113,8 +110,8 @@ private: // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). // Used internally whenever the resizing parameters or texture change. This function also - // initiates the SVG rasterization. - void resize(); + // initiates the SVG rasterization unless explicitly told not to. + void resize(bool rasterize = true); Renderer::Vertex mVertices[4]; diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index 582c37bf8..39e438ac6 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -51,7 +51,7 @@ void NinePatchComponent::buildVertices() if (mVertices != nullptr) delete[] mVertices; - glm::vec2 relCornerSize {}; + glm::vec2 relCornerSize {0.0f, 0.0f}; // Don't scale the rasterized version of the frame as it would look bad. if (mPath.substr(mPath.size() - 4, std::string::npos) == ".png") { @@ -65,8 +65,9 @@ void NinePatchComponent::buildVertices() (mSharpCorners == true ? 0.0568f : 0.09f) / 2.0f)); } - mTexture = TextureResource::get(mPath, false, false, false); - glm::vec2 texSize = relCornerSize * 3.0f; + glm::vec2 texSize {relCornerSize * 3.0f}; + mTexture = TextureResource::get(mPath, false, false, false, false, false, + static_cast(texSize.x), static_cast(texSize.y)); mTexture->rasterizeAt(texSize.x, texSize.y); @@ -86,11 +87,11 @@ void NinePatchComponent::buildVertices() // The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom // left corner origin vs. verticies having a top left origin. // clang-format off - const float texSizeX[3]{relCornerSize.x / texSize.x, (texSize.x - relCornerSize.x * 2.0f) / texSize.x, relCornerSize.x / texSize.x}; - const float texSizeY[3]{-relCornerSize.y / texSize.y, -(texSize.y - relCornerSize.y * 2.0f) / texSize.y, -relCornerSize.y / texSize.y}; + const float texSizeX[3] {relCornerSize.x / texSize.x, (texSize.x - relCornerSize.x * 2.0f) / texSize.x, relCornerSize.x / texSize.x}; + const float texSizeY[3] {-relCornerSize.y / texSize.y, -(texSize.y - relCornerSize.y * 2.0f) / texSize.y, -relCornerSize.y / texSize.y}; - const float texPosX[3]{0.0f, texSizeX[0], texSizeX[0] + texSizeX[1]}; - const float texPosY[3]{1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1]}; + const float texPosX[3] {0.0f, texSizeX[0], texSizeX[0] + texSizeX[1]}; + const float texPosY[3] {1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1]}; // clang-format on int v = 0; @@ -178,7 +179,7 @@ void NinePatchComponent::applyTheme(const std::shared_ptr& theme, GuiComponent::applyTheme(theme, view, element, properties); using namespace ThemeFlags; - const ThemeData::ThemeElement* elem = theme->getElement(view, element, "ninepatch"); + const ThemeData::ThemeElement* elem {theme->getElement(view, element, "ninepatch")}; if (!elem) return; diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index bf72b7c62..c00b404dd 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -223,17 +223,30 @@ void RatingComponent::applyTheme(const std::shared_ptr& theme, unsigned int properties) { using namespace ThemeFlags; - const ThemeData::ThemeElement* elem = theme->getElement(view, element, "rating"); + const ThemeData::ThemeElement* elem {theme->getElement(view, element, "rating")}; + if (!elem) return; - bool imgChanged = false; + // Make sure the size is not unreasonably large (which may be caused by a mistake in + // the theme configuration). + mSize.x = glm::clamp(mSize.x, 0.0f, mRenderer->getScreenWidth() / 2.0f); + mSize.y = glm::clamp(mSize.y, 0.0f, mRenderer->getScreenHeight() / 2.0f); + + if (mSize.y == 0.0f) + mSize.y = mSize.x / NUM_RATING_STARS; + else if (mSize.x == 0.0f) + mSize.x = mSize.y * NUM_RATING_STARS; + + bool imgChanged {false}; if (properties & PATH && elem->has("filledPath")) { - mFilledTexture = TextureResource::get(elem->get("filledPath"), true); + mFilledTexture = TextureResource::get(elem->get("filledPath"), true, false, + true, false, false, mSize.y, mSize.y); imgChanged = true; } if (properties & PATH && elem->has("unfilledPath")) { - mUnfilledTexture = TextureResource::get(elem->get("unfilledPath"), true); + mUnfilledTexture = TextureResource::get(elem->get("unfilledPath"), true, false, + true, false, false, mSize.y, mSize.y); imgChanged = true; } diff --git a/es-core/src/components/primary/CarouselComponent.h b/es-core/src/components/primary/CarouselComponent.h index 84f0ddb1d..76c2cc0bc 100644 --- a/es-core/src/components/primary/CarouselComponent.h +++ b/es-core/src/components/primary/CarouselComponent.h @@ -223,8 +223,8 @@ void CarouselComponent::addEntry(Entry& entry, const std::shared_ptr(false, dynamic); item->setLinearInterpolation(mLinearInterpolation); - item->setImage(entry.data.itemPath); item->setMaxSize(mItemSize * mItemScale); + item->setImage(entry.data.itemPath); item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->setRotateByTargetSize(true); entry.data.item = item; @@ -233,8 +233,8 @@ void CarouselComponent::addEntry(Entry& entry, const std::shared_ptr(false, dynamic); defaultItem->setLinearInterpolation(mLinearInterpolation); - defaultItem->setImage(entry.data.defaultItemPath); defaultItem->setMaxSize(mItemSize * mItemScale); + defaultItem->setImage(entry.data.defaultItemPath); defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL); defaultItem->setRotateByTargetSize(true); entry.data.item = defaultItem; diff --git a/es-core/src/resources/TextureDataManager.cpp b/es-core/src/resources/TextureDataManager.cpp index 7bb7e9453..b0dd8ac07 100644 --- a/es-core/src/resources/TextureDataManager.cpp +++ b/es-core/src/resources/TextureDataManager.cpp @@ -36,7 +36,7 @@ TextureDataManager::~TextureDataManager() std::shared_ptr TextureDataManager::add(const TextureResource* key, bool tiled) { remove(key); - std::shared_ptr data = std::make_shared(tiled); + std::shared_ptr data {std::make_shared(tiled)}; mTextures.push_front(data); mTextureLookup[key] = mTextures.cbegin(); return data; @@ -114,8 +114,8 @@ void TextureDataManager::load(std::shared_ptr tex, bool block) if (tex->isLoaded()) return; // Not loaded. Make sure there is room. - size_t size = TextureResource::getTotalMemUsage(); - size_t settingVRAM = static_cast(Settings::getInstance()->getInt("MaxVRAM")); + size_t size {TextureResource::getTotalMemUsage()}; + size_t settingVRAM {static_cast(Settings::getInstance()->getInt("MaxVRAM"))}; if (settingVRAM < 80) { LOG(LogWarning) << "MaxVRAM is too low at " << settingVRAM diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index cdd8fbee2..be70a3c80 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -3,7 +3,7 @@ // EmulationStation Desktop Edition // TextureResource.cpp // -// Handles OpenGL textures. +// Handles textures including loading, unloading and cache management. // #include "resources/TextureResource.h" @@ -11,6 +11,9 @@ #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" +#define DEBUG_RASTER_CACHING false +#define DEBUG_SVG_CACHING false + TextureResource::TextureResource( const std::string& path, bool tile, bool dynamic, bool linearMagnify, bool forceRasterization) : mTextureData {nullptr} @@ -30,6 +33,7 @@ TextureResource::TextureResource( sTextureDataManager.load(data, true); } else { + mTextureData = std::shared_ptr(new TextureData(tile)); data = mTextureData; data->initFromPath(path); @@ -47,7 +51,7 @@ TextureResource::TextureResource( // loaded and unloaded. This would normally be a video texture, where the player // reserves a texture to later be used for the video rendering. mTextureData = std::shared_ptr(new TextureData(tile)); - mSize = glm::ivec2 {}; + mSize = glm::ivec2 {0.0f, 0.0f}; } sAllTextures.insert(this); } @@ -85,32 +89,25 @@ void TextureResource::initFromMemory(const char* data, size_t length) mSourceSize = glm::vec2 {mTextureData->sourceWidth(), mTextureData->sourceHeight()}; } -void TextureResource::manualUnload(std::string path, bool tile) +void TextureResource::manualUnload(const std::string& path, bool tile) { - const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); + const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)}; - // TODO: We always attempt to unload both the linear and nearest interpolation entries - // which is a bit of a hack. Rewrite this to only unload the requested image. - { - TextureKeyType key {canonicalPath, tile, false}; - auto foundTexture = sTextureMap.find(key); - - if (foundTexture != sTextureMap.cend()) - sTextureMap.erase(foundTexture); - } - - { - TextureKeyType key {canonicalPath, tile, true}; - auto foundTexture = sTextureMap.find(key); - - if (foundTexture != sTextureMap.cend()) - sTextureMap.erase(foundTexture); + // TODO: We always attempt to unload both the linear and nearest interpolation entries. + // Rewrite this to only unload the requested image. + for (auto it = sTextureMap.begin(); it != sTextureMap.end();) { + if (std::get<0>((*it).first) == canonicalPath && std::get<1>((*it).first) == tile) { + sTextureMap.erase(it++); + } + else { + ++it; + } } } std::vector TextureResource::getRawRGBAData() { - std::shared_ptr data = sTextureDataManager.get(this); + std::shared_ptr data {sTextureDataManager.get(this)}; if (data) return data.get()->getRawRGBAData(); @@ -120,7 +117,7 @@ std::vector TextureResource::getRawRGBAData() std::string TextureResource::getTextureFilePath() { - std::shared_ptr data = sTextureDataManager.get(this); + std::shared_ptr data {sTextureDataManager.get(this)}; if (data) return data->getTextureFilePath(); @@ -132,7 +129,7 @@ bool TextureResource::isTiled() const { if (mTextureData != nullptr) return mTextureData->tiled(); - std::shared_ptr data = sTextureDataManager.get(this); + std::shared_ptr data {sTextureDataManager.get(this)}; return data->tiled(); } @@ -152,9 +149,11 @@ std::shared_ptr TextureResource::get(const std::string& path, bool forceLoad, bool dynamic, bool linearMagnify, - bool forceRasterization) + bool forceRasterization, + size_t width, + size_t height) { - const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); + const std::string canonicalPath {Utils::FileSystem::getCanonicalPath(path)}; if (canonicalPath.empty()) { std::shared_ptr tex( new TextureResource("", tile, false, linearMagnify, forceRasterization)); @@ -163,25 +162,54 @@ std::shared_ptr TextureResource::get(const std::string& path, return tex; } - TextureKeyType key {canonicalPath, tile, linearMagnify}; + const bool isScalable {Utils::String::toLower(canonicalPath.substr( + canonicalPath.size() - 4, std::string::npos)) == ".svg"}; + + TextureKeyType key {canonicalPath, tile, linearMagnify, isScalable, width, height}; auto foundTexture = sTextureMap.find(key); + std::string resolutionInfo; + + if (DEBUG_SVG_CACHING && isScalable) { + resolutionInfo.append(" (resolution ") + .append(std::to_string(width)) + .append("x") + .append(std::to_string(height)) + .append(")"); + } + if (foundTexture != sTextureMap.cend()) { - if (!foundTexture->second.expired()) + if (!foundTexture->second.expired()) { + if ((DEBUG_SVG_CACHING && isScalable) || (DEBUG_RASTER_CACHING && !isScalable)) { + LOG(LogDebug) << "TextureResource::get(): Cache hit for " + << (isScalable ? "SVG" : "raster") << " image \"" << canonicalPath + << "\"" << resolutionInfo; + } return foundTexture->second.lock(); + } + else if ((DEBUG_SVG_CACHING && isScalable) || (DEBUG_RASTER_CACHING && !isScalable)) { + LOG(LogDebug) << "TextureResource::get(): Cache expired for " + << (isScalable ? "SVG" : "raster") << " image \"" << canonicalPath << "\"" + << resolutionInfo; + } + } + else if ((DEBUG_SVG_CACHING && isScalable && width != 0.0f && height != 0.0f) || + (DEBUG_RASTER_CACHING && !isScalable)) { + LOG(LogDebug) << "TextureResource::get(): Cache miss for " + << (isScalable ? "SVG" : "raster") << " image \"" << canonicalPath << "\"" + << resolutionInfo; } // Need to create it. - std::shared_ptr tex; - tex = std::shared_ptr( - new TextureResource(std::get<0>(key), tile, dynamic, linearMagnify, forceRasterization)); - std::shared_ptr data = sTextureDataManager.get(tex.get()); + std::shared_ptr tex {std::shared_ptr( + new TextureResource(std::get<0>(key), tile, dynamic, linearMagnify, forceRasterization))}; + std::shared_ptr data {sTextureDataManager.get(tex.get())}; - // Is it an SVG? - if (Utils::String::toLower( - std::get<0>(key).substr(std::get<0>(key).size() - 4, std::string::npos)) != ".svg") { - // Probably not. Add it to our map. We don't add SVGs because 2 SVGs might be - // rasterized at different sizes. + if (!isScalable || (isScalable && width != 0.0f && height != 0.0f)) { + if ((DEBUG_SVG_CACHING && isScalable) || (DEBUG_RASTER_CACHING && !isScalable)) { + LOG(LogDebug) << "TextureResource::get(): Adding " << (isScalable ? "SVG" : "raster") + << " image to cache: \"" << canonicalPath << "\"" << resolutionInfo; + } sTextureMap[key] = std::weak_ptr(tex); } @@ -200,7 +228,7 @@ std::shared_ptr TextureResource::get(const std::string& path, void TextureResource::rasterizeAt(float width, float height) { if (mTextureData != nullptr) { - glm::vec2 textureSize = mTextureData.get()->getSize(); + glm::vec2 textureSize {mTextureData.get()->getSize()}; if (textureSize.x == width && textureSize.y == height && !mTextureData.get()->getPendingRasterization()) return; @@ -221,7 +249,7 @@ void TextureResource::rasterizeAt(float width, float height) size_t TextureResource::getTotalMemUsage() { - size_t total = 0; + size_t total {0}; // Count up all textures that manage their own texture data. for (auto tex : sAllTextures) { if (tex->mTextureData != nullptr) diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index cf44e2cc5..7ee219f1e 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -3,7 +3,7 @@ // EmulationStation Desktop Edition // TextureResource.h // -// Handles OpenGL textures. +// Handles textures including loading, unloading and cache management. // #ifndef ES_CORE_RESOURCES_TEXTURE_RESOURCE_H @@ -31,10 +31,12 @@ public: bool forceLoad = false, bool dynamic = true, bool linearMagnify = false, - bool forceRasterization = false); + bool forceRasterization = false, + size_t width = 0, + size_t height = 0); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); virtual void initFromMemory(const char* data, size_t length); - static void manualUnload(std::string path, bool tile); + static void manualUnload(const std::string& path, bool tile); static void manualUnloadAll() { sTextureMap.clear(); } // Returns the raw pixel values. @@ -91,7 +93,8 @@ private: glm::vec2 mSourceSize; bool mForceLoad; - using TextureKeyType = std::tuple; + // File path, tile, linear interpolation, scalable/SVG, width, height. + using TextureKeyType = std::tuple; // Map of textures, used to prevent duplicate textures. static inline std::map> sTextureMap; // Set of all textures, used for memory management.