mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-31 04:25:40 +00:00
Merge pull request #87 from RetroPie/revert-86-wsod_fix_pr
Revert "Fix WSOD by loading textures on demand in a separate thread when a us…"
This commit is contained in:
commit
388bbae43b
|
@ -2,6 +2,7 @@
|
||||||
#include "Renderer.h"
|
#include "Renderer.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "resources/SVGResource.h"
|
||||||
|
|
||||||
RatingComponent::RatingComponent(Window* window) : GuiComponent(window)
|
RatingComponent::RatingComponent(Window* window) : GuiComponent(window)
|
||||||
{
|
{
|
||||||
|
@ -44,13 +45,16 @@ void RatingComponent::onSizeChanged()
|
||||||
else if(mSize.x() == 0)
|
else if(mSize.x() == 0)
|
||||||
mSize[0] = mSize.y() * NUM_RATING_STARS;
|
mSize[0] = mSize.y() * NUM_RATING_STARS;
|
||||||
|
|
||||||
|
auto filledSVG = dynamic_cast<SVGResource*>(mFilledTexture.get());
|
||||||
|
auto unfilledSVG = dynamic_cast<SVGResource*>(mUnfilledTexture.get());
|
||||||
|
|
||||||
if(mSize.y() > 0)
|
if(mSize.y() > 0)
|
||||||
{
|
{
|
||||||
size_t heightPx = (size_t)round(mSize.y());
|
size_t heightPx = (size_t)round(mSize.y());
|
||||||
if (mFilledTexture)
|
if(filledSVG)
|
||||||
mFilledTexture->rasterizeAt(heightPx, heightPx);
|
filledSVG->rasterizeAt(heightPx, heightPx);
|
||||||
if(mUnfilledTexture)
|
if(unfilledSVG)
|
||||||
mUnfilledTexture->rasterizeAt(heightPx, heightPx);
|
unfilledSVG->rasterizeAt(heightPx, heightPx);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVertices();
|
updateVertices();
|
||||||
|
|
|
@ -178,12 +178,6 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
|
||||||
s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists);
|
s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists);
|
||||||
s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); });
|
s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); });
|
||||||
|
|
||||||
// maximum vram
|
|
||||||
auto max_vram = std::make_shared<SliderComponent>(mWindow, 0.f, 1000.f, 10.f, "Mb");
|
|
||||||
max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM")));
|
|
||||||
s->addWithLabel("VRAM LIMIT", max_vram);
|
|
||||||
s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", (int)round(max_vram->getValue())); });
|
|
||||||
|
|
||||||
mWindow->pushGui(s);
|
mWindow->pushGui(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -74,10 +74,6 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
|
||||||
}else if(strcmp(argv[i], "--scrape") == 0)
|
}else if(strcmp(argv[i], "--scrape") == 0)
|
||||||
{
|
{
|
||||||
scrape_cmdline = true;
|
scrape_cmdline = true;
|
||||||
}else if(strcmp(argv[i], "--max-vram") == 0)
|
|
||||||
{
|
|
||||||
int maxVRAM = atoi(argv[i + 1]);
|
|
||||||
Settings::getInstance()->setInt("MaxVRAM", maxVRAM);
|
|
||||||
}else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
|
}else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -103,7 +99,6 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
|
||||||
"--scrape scrape using command line interface\n"
|
"--scrape scrape using command line interface\n"
|
||||||
"--windowed not fullscreen, should be used with --resolution\n"
|
"--windowed not fullscreen, should be used with --resolution\n"
|
||||||
"--vsync [1/on or 0/off] turn vsync on or off (default is on)\n"
|
"--vsync [1/on or 0/off] turn vsync on or off (default is on)\n"
|
||||||
"--max-vram [size] Max VRAM to use in Mb before swapping. 0 for unlimited\n"
|
|
||||||
"--help, -h summon a sentient, angry tuba\n\n"
|
"--help, -h summon a sentient, angry tuba\n\n"
|
||||||
"More information available in README.md.\n";
|
"More information available in README.md.\n";
|
||||||
return false; //exit after printing help
|
return false; //exit after printing help
|
||||||
|
|
|
@ -43,13 +43,13 @@ void SystemView::populate()
|
||||||
// make logo
|
// make logo
|
||||||
if(theme->getElement("system", "logo", "image"))
|
if(theme->getElement("system", "logo", "image"))
|
||||||
{
|
{
|
||||||
ImageComponent* logo = new ImageComponent(mWindow, false, false);
|
ImageComponent* logo = new ImageComponent(mWindow);
|
||||||
logo->setMaxSize(Eigen::Vector2f(logoSize().x(), logoSize().y()));
|
logo->setMaxSize(Eigen::Vector2f(logoSize().x(), logoSize().y()));
|
||||||
logo->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH);
|
logo->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH);
|
||||||
logo->setPosition((logoSize().x() - logo->getSize().x()) / 2, (logoSize().y() - logo->getSize().y()) / 2); // center
|
logo->setPosition((logoSize().x() - logo->getSize().x()) / 2, (logoSize().y() - logo->getSize().y()) / 2); // center
|
||||||
e.data.logo = std::shared_ptr<GuiComponent>(logo);
|
e.data.logo = std::shared_ptr<GuiComponent>(logo);
|
||||||
|
|
||||||
ImageComponent* logoSelected = new ImageComponent(mWindow, false, false);
|
ImageComponent* logoSelected = new ImageComponent(mWindow);
|
||||||
logoSelected->setMaxSize(Eigen::Vector2f(logoSize().x() * SELECTED_SCALE, logoSize().y() * SELECTED_SCALE * 0.70f));
|
logoSelected->setMaxSize(Eigen::Vector2f(logoSize().x() * SELECTED_SCALE, logoSize().y() * SELECTED_SCALE * 0.70f));
|
||||||
logoSelected->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH);
|
logoSelected->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH);
|
||||||
logoSelected->setPosition((logoSize().x() - logoSelected->getSize().x()) / 2,
|
logoSelected->setPosition((logoSize().x() - logoSelected->getSize().x()) / 2,
|
||||||
|
|
|
@ -406,9 +406,7 @@ void ViewController::reloadAll()
|
||||||
mCurrentView = getGameListView(mState.getSystem());
|
mCurrentView = getGameListView(mState.getSystem());
|
||||||
}else if(mState.viewing == SYSTEM_SELECT)
|
}else if(mState.viewing == SYSTEM_SELECT)
|
||||||
{
|
{
|
||||||
SystemData* system = mState.getSystem();
|
mSystemListView->goToSystem(mState.getSystem(), false);
|
||||||
goToSystemView(SystemData::sSystemVector.front());
|
|
||||||
mSystemListView->goToSystem(system, false);
|
|
||||||
mCurrentView = mSystemListView;
|
mCurrentView = mSystemListView;
|
||||||
}else{
|
}else{
|
||||||
goToSystemView(SystemData::sSystemVector.front());
|
goToSystemView(SystemData::sSystemVector.front());
|
||||||
|
|
|
@ -53,9 +53,8 @@ set(CORE_HEADERS
|
||||||
# Resources
|
# Resources
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/SVGResource.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureData.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureDataManager.h
|
|
||||||
|
|
||||||
# Embedded assets (needed by ResourceManager)
|
# Embedded assets (needed by ResourceManager)
|
||||||
${emulationstation-all_SOURCE_DIR}/data/Resources.h
|
${emulationstation-all_SOURCE_DIR}/data/Resources.h
|
||||||
|
@ -109,9 +108,8 @@ set(CORE_SOURCES
|
||||||
# Resources
|
# Resources
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/SVGResource.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureData.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureDataManager.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(EMBEDDED_ASSET_SOURCES
|
set(EMBEDDED_ASSET_SOURCES
|
||||||
|
|
|
@ -69,7 +69,6 @@ void Settings::setDefaults()
|
||||||
mIntMap["ScreenSaverTime"] = 5*60*1000; // 5 minutes
|
mIntMap["ScreenSaverTime"] = 5*60*1000; // 5 minutes
|
||||||
mIntMap["ScraperResizeWidth"] = 400;
|
mIntMap["ScraperResizeWidth"] = 400;
|
||||||
mIntMap["ScraperResizeHeight"] = 0;
|
mIntMap["ScraperResizeHeight"] = 0;
|
||||||
mIntMap["MaxVRAM"] = 100;
|
|
||||||
|
|
||||||
mStringMap["TransitionStyle"] = "fade";
|
mStringMap["TransitionStyle"] = "fade";
|
||||||
mStringMap["ThemeSet"] = "";
|
mStringMap["ThemeSet"] = "";
|
||||||
|
@ -155,4 +154,4 @@ void Settings::setMethodName(const std::string& name, type value) \
|
||||||
SETTINGS_GETSET(bool, mBoolMap, getBool, setBool);
|
SETTINGS_GETSET(bool, mBoolMap, getBool, setBool);
|
||||||
SETTINGS_GETSET(int, mIntMap, getInt, setInt);
|
SETTINGS_GETSET(int, mIntMap, getInt, setInt);
|
||||||
SETTINGS_GETSET(float, mFloatMap, getFloat, setFloat);
|
SETTINGS_GETSET(float, mFloatMap, getFloat, setFloat);
|
||||||
SETTINGS_GETSET(const std::string&, mStringMap, getString, setString);
|
SETTINGS_GETSET(const std::string&, mStringMap, getString, setString);
|
|
@ -160,12 +160,11 @@ void Window::update(int deltaTime)
|
||||||
ss << std::fixed << std::setprecision(2) << ((float)mFrameTimeElapsed / (float)mFrameCountElapsed) << "ms";
|
ss << std::fixed << std::setprecision(2) << ((float)mFrameTimeElapsed / (float)mFrameCountElapsed) << "ms";
|
||||||
|
|
||||||
// vram
|
// vram
|
||||||
float textureVramUsageMb = TextureResource::getTotalMemUsage() / 1000.0f / 1000.0f;
|
float textureVramUsageMb = TextureResource::getTotalMemUsage() / 1000.0f / 1000.0f;;
|
||||||
float textureTotalUsageMb = TextureResource::getTotalTextureSize() / 1000.0f / 1000.0f;
|
|
||||||
float fontVramUsageMb = Font::getTotalMemUsage() / 1000.0f / 1000.0f;;
|
float fontVramUsageMb = Font::getTotalMemUsage() / 1000.0f / 1000.0f;;
|
||||||
|
float totalVramUsageMb = textureVramUsageMb + fontVramUsageMb;
|
||||||
|
ss << "\nVRAM: " << totalVramUsageMb << "mb (texs: " << textureVramUsageMb << "mb, fonts: " << fontVramUsageMb << "mb)";
|
||||||
|
|
||||||
ss << "\nFont VRAM: " << fontVramUsageMb << " Tex VRAM: " << textureVramUsageMb <<
|
|
||||||
" Tex Max: " << textureTotalUsageMb;
|
|
||||||
mFrameDataText = std::unique_ptr<TextCache>(mDefaultFonts.at(1)->buildTextCache(ss.str(), 50.f, 50.f, 0xFF00FFFF));
|
mFrameDataText = std::unique_ptr<TextCache>(mDefaultFonts.at(1)->buildTextCache(ss.str(), 50.f, 50.f, 0xFF00FFFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +242,7 @@ void Window::renderLoadingScreen()
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFFFF);
|
Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFFFF);
|
||||||
|
|
||||||
ImageComponent splash(this, true);
|
ImageComponent splash(this);
|
||||||
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
||||||
splash.setImage(":/splash.svg");
|
splash.setImage(":/splash.svg");
|
||||||
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2, (Renderer::getScreenHeight() - splash.getSize().y()) / 2 * 0.6f);
|
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2, (Renderer::getScreenHeight() - splash.getSize().y()) / 2 * 0.6f);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Renderer.h"
|
#include "Renderer.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "resources/SVGResource.h"
|
||||||
|
|
||||||
Eigen::Vector2i ImageComponent::getTextureSize() const
|
Eigen::Vector2i ImageComponent::getTextureSize() const
|
||||||
{
|
{
|
||||||
|
@ -21,9 +22,8 @@ Eigen::Vector2f ImageComponent::getCenter() const
|
||||||
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
|
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageComponent::ImageComponent(Window* window, bool forceLoad, bool dynamic) : GuiComponent(window),
|
ImageComponent::ImageComponent(Window* window) : GuiComponent(window),
|
||||||
mTargetIsMax(false), mFlipX(false), mFlipY(false), mOrigin(0.0, 0.0), mTargetSize(0, 0), mColorShift(0xFFFFFFFF),
|
mTargetIsMax(false), mFlipX(false), mFlipY(false), mOrigin(0.0, 0.0), mTargetSize(0, 0), mColorShift(0xFFFFFFFF)
|
||||||
mForceLoad(forceLoad), mDynamic(dynamic)
|
|
||||||
{
|
{
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,9 @@ void ImageComponent::resize()
|
||||||
if(!mTexture)
|
if(!mTexture)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Eigen::Vector2f textureSize = mTexture->getSourceImageSize();
|
SVGResource* svg = dynamic_cast<SVGResource*>(mTexture.get());
|
||||||
|
|
||||||
|
const Eigen::Vector2f textureSize = svg ? svg->getSourceImageSize() : Eigen::Vector2f((float)mTexture->getSize().x(), (float)mTexture->getSize().y());
|
||||||
if(textureSize.isZero())
|
if(textureSize.isZero())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -88,8 +90,12 @@ void ImageComponent::resize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// mSize.y() should already be rounded
|
|
||||||
mTexture->rasterizeAt((int)round(mSize.x()), (int)round(mSize.y()));
|
if(svg)
|
||||||
|
{
|
||||||
|
// mSize.y() should already be rounded
|
||||||
|
svg->rasterizeAt((int)round(mSize.x()), (int)round(mSize.y()));
|
||||||
|
}
|
||||||
|
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
@ -104,7 +110,7 @@ void ImageComponent::setImage(std::string path, bool tile)
|
||||||
if(path.empty() || !ResourceManager::getInstance()->fileExists(path))
|
if(path.empty() || !ResourceManager::getInstance()->fileExists(path))
|
||||||
mTexture.reset();
|
mTexture.reset();
|
||||||
else
|
else
|
||||||
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic);
|
mTexture = TextureResource::get(path, tile);
|
||||||
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
@ -241,10 +247,7 @@ void ImageComponent::render(const Eigen::Affine3f& parentTrans)
|
||||||
if(mTexture->isInitialized())
|
if(mTexture->isInitialized())
|
||||||
{
|
{
|
||||||
// actually draw the image
|
// actually draw the image
|
||||||
// The bind() function returns false if the texture is not currently loaded. A blank
|
mTexture->bind();
|
||||||
// texture is bound in this case but we want to handle a fade so it doesn't just 'jump' in
|
|
||||||
// when it finally loads
|
|
||||||
fadeIn(mTexture->bind());
|
|
||||||
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
@ -275,47 +278,6 @@ void ImageComponent::render(const Eigen::Affine3f& parentTrans)
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::fadeIn(bool textureLoaded)
|
|
||||||
{
|
|
||||||
if (!mForceLoad)
|
|
||||||
{
|
|
||||||
if (!textureLoaded)
|
|
||||||
{
|
|
||||||
// Start the fade if this is the first time we've encountered the unloaded texture
|
|
||||||
if (!mFading)
|
|
||||||
{
|
|
||||||
// Start with a zero opacity and flag it as fading
|
|
||||||
mFadeOpacity = 0;
|
|
||||||
mFading = true;
|
|
||||||
// Set the colours to be translucent
|
|
||||||
mColorShift = (mColorShift >> 8 << 8) | 0;
|
|
||||||
updateColors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mFading)
|
|
||||||
{
|
|
||||||
// The texture is loaded and we need to fade it in. The fade is based on the frame rate
|
|
||||||
// and is 1/4 second if running at 60 frames per second although the actual value is not
|
|
||||||
// that important
|
|
||||||
int opacity = mFadeOpacity + 255 / 15;
|
|
||||||
// See if we've finished fading
|
|
||||||
if (opacity >= 255)
|
|
||||||
{
|
|
||||||
mFadeOpacity = 255;
|
|
||||||
mFading = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mFadeOpacity = (unsigned char)opacity;
|
|
||||||
}
|
|
||||||
// Apply the combination of the target opacity and current fade
|
|
||||||
float newOpacity = (float)mOpacity * ((float)mFadeOpacity / 255.0f);
|
|
||||||
mColorShift = (mColorShift >> 8 << 8) | (unsigned char)newOpacity;
|
|
||||||
updateColors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImageComponent::hasImage()
|
bool ImageComponent::hasImage()
|
||||||
{
|
{
|
||||||
return (bool)mTexture;
|
return (bool)mTexture;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
class ImageComponent : public GuiComponent
|
class ImageComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ImageComponent(Window* window, bool forceLoad = false, bool dynamic = true);
|
ImageComponent(Window* window);
|
||||||
virtual ~ImageComponent();
|
virtual ~ImageComponent();
|
||||||
|
|
||||||
//Loads the image at the given filepath. Will tile if tile is true (retrieves texture as tiling, creates vertices accordingly).
|
//Loads the image at the given filepath. Will tile if tile is true (retrieves texture as tiling, creates vertices accordingly).
|
||||||
|
@ -81,15 +81,10 @@ private:
|
||||||
|
|
||||||
void updateVertices();
|
void updateVertices();
|
||||||
void updateColors();
|
void updateColors();
|
||||||
void fadeIn(bool textureLoaded);
|
|
||||||
|
|
||||||
unsigned int mColorShift;
|
unsigned int mColorShift;
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> mTexture;
|
std::shared_ptr<TextureResource> mTexture;
|
||||||
unsigned char mFadeOpacity;
|
|
||||||
bool mFading;
|
|
||||||
bool mForceLoad;
|
|
||||||
bool mDynamic;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
101
es-core/src/resources/SVGResource.cpp
Normal file
101
es-core/src/resources/SVGResource.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include "SVGResource.h"
|
||||||
|
#include "nanosvg/nanosvg.h"
|
||||||
|
#include "nanosvg/nanosvgrast.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Util.h"
|
||||||
|
#include "ImageIO.h"
|
||||||
|
|
||||||
|
#define DPI 96
|
||||||
|
|
||||||
|
SVGResource::SVGResource(const std::string& path, bool tile) : TextureResource(path, tile), mSVGImage(NULL)
|
||||||
|
{
|
||||||
|
mLastWidth = 0;
|
||||||
|
mLastHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGResource::~SVGResource()
|
||||||
|
{
|
||||||
|
deinitSVG();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGResource::unload(std::shared_ptr<ResourceManager>& rm)
|
||||||
|
{
|
||||||
|
deinitSVG();
|
||||||
|
TextureResource::unload(rm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGResource::initFromMemory(const char* file, size_t length)
|
||||||
|
{
|
||||||
|
deinit();
|
||||||
|
deinitSVG();
|
||||||
|
|
||||||
|
// nsvgParse excepts a modifiable, null-terminated string
|
||||||
|
char* copy = (char*)malloc(length + 1);
|
||||||
|
assert(copy != NULL);
|
||||||
|
memcpy(copy, file, length);
|
||||||
|
copy[length] = '\0';
|
||||||
|
|
||||||
|
mSVGImage = nsvgParse(copy, "px", DPI);
|
||||||
|
free(copy);
|
||||||
|
|
||||||
|
if(!mSVGImage)
|
||||||
|
{
|
||||||
|
LOG(LogError) << "Error parsing SVG image.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mLastWidth && mLastHeight)
|
||||||
|
rasterizeAt(mLastWidth, mLastHeight);
|
||||||
|
else
|
||||||
|
rasterizeAt((size_t)round(mSVGImage->width), (size_t)round(mSVGImage->height));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGResource::rasterizeAt(size_t width, size_t height)
|
||||||
|
{
|
||||||
|
if(!mSVGImage || (width == 0 && height == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(width == 0)
|
||||||
|
{
|
||||||
|
// auto scale width to keep aspect
|
||||||
|
width = (size_t)round((height / mSVGImage->height) * mSVGImage->width);
|
||||||
|
}else if(height == 0)
|
||||||
|
{
|
||||||
|
// auto scale height to keep aspect
|
||||||
|
height = (size_t)round((width / mSVGImage->width) * mSVGImage->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(width != (size_t)round(mSVGImage->width) && height != (size_t)round(mSVGImage->height))
|
||||||
|
{
|
||||||
|
mLastWidth = width;
|
||||||
|
mLastHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* imagePx = (unsigned char*)malloc(width * height * 4);
|
||||||
|
assert(imagePx != NULL);
|
||||||
|
|
||||||
|
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||||
|
nsvgRasterize(rast, mSVGImage, 0, 0, height / mSVGImage->height, imagePx, width, height, width * 4);
|
||||||
|
nsvgDeleteRasterizer(rast);
|
||||||
|
|
||||||
|
ImageIO::flipPixelsVert(imagePx, width, height);
|
||||||
|
|
||||||
|
initFromPixels(imagePx, width, height);
|
||||||
|
free(imagePx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector2f SVGResource::getSourceImageSize() const
|
||||||
|
{
|
||||||
|
if(mSVGImage)
|
||||||
|
return Eigen::Vector2f(mSVGImage->width, mSVGImage->height);
|
||||||
|
|
||||||
|
return Eigen::Vector2f::Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGResource::deinitSVG()
|
||||||
|
{
|
||||||
|
if(mSVGImage)
|
||||||
|
nsvgDelete(mSVGImage);
|
||||||
|
|
||||||
|
mSVGImage = NULL;
|
||||||
|
}
|
27
es-core/src/resources/SVGResource.h
Normal file
27
es-core/src/resources/SVGResource.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
|
||||||
|
struct NSVGimage;
|
||||||
|
|
||||||
|
class SVGResource : public TextureResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~SVGResource();
|
||||||
|
|
||||||
|
virtual void unload(std::shared_ptr<ResourceManager>& rm) override;
|
||||||
|
|
||||||
|
virtual void initFromMemory(const char* image, size_t length) override;
|
||||||
|
|
||||||
|
void rasterizeAt(size_t width, size_t height);
|
||||||
|
Eigen::Vector2f getSourceImageSize() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend TextureResource;
|
||||||
|
SVGResource(const std::string& path, bool tile);
|
||||||
|
void deinitSVG();
|
||||||
|
|
||||||
|
NSVGimage* mSVGImage;
|
||||||
|
size_t mLastWidth;
|
||||||
|
size_t mLastHeight;
|
||||||
|
};
|
|
@ -1,259 +0,0 @@
|
||||||
#include "resources/TextureData.h"
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "ImageIO.h"
|
|
||||||
#include "string.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "nanosvg/nanosvg.h"
|
|
||||||
#include "nanosvg/nanosvgrast.h"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define DPI 96
|
|
||||||
|
|
||||||
TextureData::TextureData(bool tile) : mTile(tile), mTextureID(0), mDataRGBA(nullptr), mScalable(false),
|
|
||||||
mWidth(0), mHeight(0), mSourceWidth(0.0f), mSourceHeight(0.0f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureData::~TextureData()
|
|
||||||
{
|
|
||||||
releaseVRAM();
|
|
||||||
releaseRAM();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureData::initFromPath(const std::string& path)
|
|
||||||
{
|
|
||||||
// Just set the path. It will be loaded later
|
|
||||||
mPath = path;
|
|
||||||
// Only textures with paths are reloadable
|
|
||||||
mReloadable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::initSVGFromMemory(const unsigned char* fileData, size_t length)
|
|
||||||
{
|
|
||||||
// If already initialised then don't read again
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mDataRGBA)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nsvgParse excepts a modifiable, null-terminated string
|
|
||||||
char* copy = (char*)malloc(length + 1);
|
|
||||||
assert(copy != NULL);
|
|
||||||
memcpy(copy, fileData, length);
|
|
||||||
copy[length] = '\0';
|
|
||||||
|
|
||||||
NSVGimage* svgImage = nsvgParse(copy, "px", DPI);
|
|
||||||
free(copy);
|
|
||||||
if (!svgImage)
|
|
||||||
{
|
|
||||||
LOG(LogError) << "Error parsing SVG image.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to rasterise this texture at a specific resolution. If the source size
|
|
||||||
// variables are set then use them otherwise set them from the parsed file
|
|
||||||
if ((mSourceWidth == 0.0f) && (mSourceHeight == 0.0f))
|
|
||||||
{
|
|
||||||
mSourceWidth = svgImage->width;
|
|
||||||
mSourceHeight = svgImage->height;
|
|
||||||
}
|
|
||||||
mWidth = (size_t)round(mSourceWidth);
|
|
||||||
mHeight = (size_t)round(mSourceHeight);
|
|
||||||
|
|
||||||
if (mWidth == 0)
|
|
||||||
{
|
|
||||||
// auto scale width to keep aspect
|
|
||||||
mWidth = (size_t)round(((float)mHeight / svgImage->height) * svgImage->width);
|
|
||||||
}
|
|
||||||
else if (mHeight == 0)
|
|
||||||
{
|
|
||||||
// auto scale height to keep aspect
|
|
||||||
mHeight = (size_t)round(((float)mWidth / svgImage->width) * svgImage->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char* dataRGBA = new unsigned char[mWidth * mHeight * 4];
|
|
||||||
|
|
||||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
|
||||||
nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, dataRGBA, mWidth, mHeight, mWidth * 4);
|
|
||||||
nsvgDeleteRasterizer(rast);
|
|
||||||
|
|
||||||
ImageIO::flipPixelsVert(dataRGBA, mWidth, mHeight);
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
mDataRGBA = dataRGBA;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::initImageFromMemory(const unsigned char* fileData, size_t length)
|
|
||||||
{
|
|
||||||
size_t width, height;
|
|
||||||
|
|
||||||
// If already initialised then don't read again
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mDataRGBA)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(fileData), length, width, height);
|
|
||||||
if (imageRGBA.size() == 0)
|
|
||||||
{
|
|
||||||
LOG(LogError) << "Could not initialize texture from memory, invalid data! (file path: " << mPath << ", data ptr: " << (size_t)fileData << ", reported size: " << length << ")";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSourceWidth = width;
|
|
||||||
mSourceHeight = height;
|
|
||||||
mScalable = false;
|
|
||||||
|
|
||||||
return initFromRGBA(imageRGBA.data(), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::initFromRGBA(const unsigned char* dataRGBA, size_t width, size_t height)
|
|
||||||
{
|
|
||||||
// If already initialised then don't read again
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mDataRGBA)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Take a copy
|
|
||||||
mDataRGBA = new unsigned char[width * height * 4];
|
|
||||||
memcpy(mDataRGBA, dataRGBA, width * height * 4);
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::load()
|
|
||||||
{
|
|
||||||
bool retval = false;
|
|
||||||
|
|
||||||
// Need to load. See if there is a file
|
|
||||||
if (!mPath.empty())
|
|
||||||
{
|
|
||||||
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
|
|
||||||
const ResourceData& data = rm->getFileData(mPath);
|
|
||||||
// is it an SVG?
|
|
||||||
if (mPath.substr(mPath.size() - 4, std::string::npos) == ".svg")
|
|
||||||
{
|
|
||||||
mScalable = true;
|
|
||||||
retval = initSVGFromMemory((const unsigned char*)data.ptr.get(), data.length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
retval = initImageFromMemory((const unsigned char*)data.ptr.get(), data.length);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::isLoaded()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mDataRGBA || (mTextureID != 0))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureData::uploadAndBind()
|
|
||||||
{
|
|
||||||
// See if it's already been uploaded
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mTextureID != 0)
|
|
||||||
{
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Load it if necessary
|
|
||||||
if (!mDataRGBA)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Make sure we're ready to upload
|
|
||||||
if ((mWidth == 0) || (mHeight == 0) || (mDataRGBA == nullptr))
|
|
||||||
return false;
|
|
||||||
glGetError();
|
|
||||||
//now for the openGL texture stuff
|
|
||||||
glGenTextures(1, &mTextureID);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, mDataRGBA);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
|
|
||||||
const GLint wrapMode = mTile ? GL_REPEAT : GL_CLAMP_TO_EDGE;
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureData::releaseVRAM()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mTextureID != 0)
|
|
||||||
{
|
|
||||||
glDeleteTextures(1, &mTextureID);
|
|
||||||
mTextureID = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureData::releaseRAM()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
delete[] mDataRGBA;
|
|
||||||
mDataRGBA = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureData::width()
|
|
||||||
{
|
|
||||||
if (mWidth == 0)
|
|
||||||
load();
|
|
||||||
return mWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureData::height()
|
|
||||||
{
|
|
||||||
if (mHeight == 0)
|
|
||||||
load();
|
|
||||||
return mHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
float TextureData::sourceWidth()
|
|
||||||
{
|
|
||||||
if (mSourceWidth == 0)
|
|
||||||
load();
|
|
||||||
return mSourceWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
float TextureData::sourceHeight()
|
|
||||||
{
|
|
||||||
if (mSourceHeight == 0)
|
|
||||||
load();
|
|
||||||
return mSourceHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureData::setSourceSize(float width, float height)
|
|
||||||
{
|
|
||||||
if (mScalable)
|
|
||||||
{
|
|
||||||
if ((mSourceWidth != width) || (mSourceHeight != height))
|
|
||||||
{
|
|
||||||
mSourceWidth = width;
|
|
||||||
mSourceHeight = height;
|
|
||||||
releaseVRAM();
|
|
||||||
releaseRAM();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureData::getVRAMUsage()
|
|
||||||
{
|
|
||||||
if ((mTextureID != 0) || (mDataRGBA != nullptr))
|
|
||||||
return mWidth * mHeight * 4;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include "platform.h"
|
|
||||||
#include <mutex>
|
|
||||||
#include GLHEADER
|
|
||||||
|
|
||||||
class TextureResource;
|
|
||||||
|
|
||||||
class TextureData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextureData(bool tile);
|
|
||||||
~TextureData();
|
|
||||||
|
|
||||||
// These functions populate mDataRGBA but do not upload the texture to VRAM
|
|
||||||
|
|
||||||
//!!!! Needs to be canonical path. Caller should check for duplicates before calling this
|
|
||||||
void initFromPath(const std::string& path);
|
|
||||||
bool initSVGFromMemory(const unsigned char* fileData, size_t length);
|
|
||||||
bool initImageFromMemory(const unsigned char* fileData, size_t length);
|
|
||||||
bool initFromRGBA(const unsigned char* dataRGBA, size_t width, size_t height);
|
|
||||||
|
|
||||||
// Read the data into memory if necessary
|
|
||||||
bool load();
|
|
||||||
|
|
||||||
bool isLoaded();
|
|
||||||
|
|
||||||
// Upload the texture to VRAM if necessary and bind. Returns true if bound ok or
|
|
||||||
// false if either not loaded
|
|
||||||
bool uploadAndBind();
|
|
||||||
|
|
||||||
// Release the texture from VRAM
|
|
||||||
void releaseVRAM();
|
|
||||||
|
|
||||||
// Release the texture from conventional RAM
|
|
||||||
void releaseRAM();
|
|
||||||
|
|
||||||
// Get the amount of VRAM currenty used by this texture
|
|
||||||
size_t getVRAMUsage();
|
|
||||||
|
|
||||||
size_t width();
|
|
||||||
size_t height();
|
|
||||||
float sourceWidth();
|
|
||||||
float sourceHeight();
|
|
||||||
void setSourceSize(float width, float height);
|
|
||||||
|
|
||||||
bool tiled() { return mTile; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex mMutex;
|
|
||||||
bool mTile;
|
|
||||||
std::string mPath;
|
|
||||||
GLuint mTextureID;
|
|
||||||
unsigned char* mDataRGBA;
|
|
||||||
size_t mWidth;
|
|
||||||
size_t mHeight;
|
|
||||||
float mSourceWidth;
|
|
||||||
float mSourceHeight;
|
|
||||||
bool mScalable;
|
|
||||||
bool mReloadable;
|
|
||||||
};
|
|
|
@ -1,226 +0,0 @@
|
||||||
#include "resources/TextureDataManager.h"
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "Settings.h"
|
|
||||||
|
|
||||||
TextureDataManager::TextureDataManager()
|
|
||||||
{
|
|
||||||
unsigned char data[5 * 5 * 4];
|
|
||||||
mBlank = std::shared_ptr<TextureData>(new TextureData(false));
|
|
||||||
for (int i = 0; i < (5 * 5); ++i)
|
|
||||||
{
|
|
||||||
data[i*4] = (i % 2) * 255;
|
|
||||||
data[i*4+1] = (i % 2) * 255;
|
|
||||||
data[i*4+2] = (i % 2) * 255;
|
|
||||||
data[i*4+3] = 0;
|
|
||||||
}
|
|
||||||
mBlank->initFromRGBA(data, 5, 5);
|
|
||||||
mLoader = new TextureLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureDataManager::~TextureDataManager()
|
|
||||||
{
|
|
||||||
delete mLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TextureData> TextureDataManager::add(const TextureResource* key, bool tiled)
|
|
||||||
{
|
|
||||||
remove(key);
|
|
||||||
std::shared_ptr<TextureData> data(new TextureData(tiled));
|
|
||||||
mTextures.push_front(data);
|
|
||||||
mTextureLookup[key] = mTextures.begin();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureDataManager::remove(const TextureResource* key)
|
|
||||||
{
|
|
||||||
// Find the entry in the list
|
|
||||||
auto it = mTextureLookup.find(key);
|
|
||||||
if (it != mTextureLookup.end())
|
|
||||||
{
|
|
||||||
// Remove the list entry
|
|
||||||
mTextures.erase((*it).second);
|
|
||||||
// And the lookup
|
|
||||||
mTextureLookup.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TextureData> TextureDataManager::get(const TextureResource* key)
|
|
||||||
{
|
|
||||||
// If it's in the cache then we want to remove it from it's current location and
|
|
||||||
// move it to the top
|
|
||||||
std::shared_ptr<TextureData> tex;
|
|
||||||
auto it = mTextureLookup.find(key);
|
|
||||||
if (it != mTextureLookup.end())
|
|
||||||
{
|
|
||||||
tex = *(*it).second;
|
|
||||||
// Remove the list entry
|
|
||||||
mTextures.erase((*it).second);
|
|
||||||
// Put it at the top
|
|
||||||
mTextures.push_front(tex);
|
|
||||||
// Store it back in the lookup
|
|
||||||
mTextureLookup[key] = mTextures.begin();
|
|
||||||
|
|
||||||
// Make sure it's loaded or queued for loading
|
|
||||||
load(tex);
|
|
||||||
}
|
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureDataManager::bind(const TextureResource* key)
|
|
||||||
{
|
|
||||||
std::shared_ptr<TextureData> tex = get(key);
|
|
||||||
bool bound = false;
|
|
||||||
if (tex != nullptr)
|
|
||||||
bound = tex->uploadAndBind();
|
|
||||||
if (!bound)
|
|
||||||
mBlank->uploadAndBind();
|
|
||||||
return bound;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureDataManager::getTotalSize()
|
|
||||||
{
|
|
||||||
size_t total = 0;
|
|
||||||
for (auto tex : mTextures)
|
|
||||||
total += tex->width() * tex->height() * 4;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureDataManager::getCommittedSize()
|
|
||||||
{
|
|
||||||
size_t total = 0;
|
|
||||||
for (auto tex : mTextures)
|
|
||||||
total += tex->getVRAMUsage();
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureDataManager::getQueueSize()
|
|
||||||
{
|
|
||||||
return mLoader->getQueueSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureDataManager::load(std::shared_ptr<TextureData> tex, bool block)
|
|
||||||
{
|
|
||||||
// See if it's already loaded
|
|
||||||
if (tex->isLoaded())
|
|
||||||
return;
|
|
||||||
// Not loaded. Make sure there is room
|
|
||||||
size_t size = TextureResource::getTotalMemUsage();
|
|
||||||
size_t max_texture = (size_t)Settings::getInstance()->getInt("MaxVRAM") * 1024 * 1024;
|
|
||||||
|
|
||||||
size_t in = size;
|
|
||||||
|
|
||||||
for (auto it = mTextures.rbegin(); it != mTextures.rend(); ++it)
|
|
||||||
{
|
|
||||||
if (size < max_texture)
|
|
||||||
break;
|
|
||||||
//size -= (*it)->getVRAMUsage();
|
|
||||||
(*it)->releaseVRAM();
|
|
||||||
(*it)->releaseRAM();
|
|
||||||
// It may be already in the loader queue. In this case it wouldn't have been using
|
|
||||||
// any VRAM yet but it will be. Remove it from the loader queue
|
|
||||||
mLoader->remove(*it);
|
|
||||||
size = TextureResource::getTotalMemUsage();
|
|
||||||
}
|
|
||||||
if (!block)
|
|
||||||
mLoader->load(tex);
|
|
||||||
else
|
|
||||||
tex->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureLoader::TextureLoader() : mExit(false)
|
|
||||||
{
|
|
||||||
mThread = new std::thread(&TextureLoader::threadProc, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureLoader::~TextureLoader()
|
|
||||||
{
|
|
||||||
// Just abort any waiting texture
|
|
||||||
mTextureDataQ.clear();
|
|
||||||
mTextureDataLookup.clear();
|
|
||||||
|
|
||||||
// Exit the thread
|
|
||||||
mExit = true;
|
|
||||||
mEvent.notify_one();
|
|
||||||
mThread->join();
|
|
||||||
delete mThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureLoader::threadProc()
|
|
||||||
{
|
|
||||||
while (!mExit)
|
|
||||||
{
|
|
||||||
std::shared_ptr<TextureData> textureData;
|
|
||||||
{
|
|
||||||
// Wait for an event to say there is something in the queue
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
mEvent.wait(lock);
|
|
||||||
if (!mTextureDataQ.empty())
|
|
||||||
{
|
|
||||||
textureData = mTextureDataQ.front();
|
|
||||||
mTextureDataQ.pop_front();
|
|
||||||
mTextureDataLookup.erase(mTextureDataLookup.find(textureData.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Queue has been released here but we might have a texture to process
|
|
||||||
while (textureData)
|
|
||||||
{
|
|
||||||
textureData->load();
|
|
||||||
|
|
||||||
// See if there is another item in the queue
|
|
||||||
textureData = nullptr;
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (!mTextureDataQ.empty())
|
|
||||||
{
|
|
||||||
textureData = mTextureDataQ.front();
|
|
||||||
mTextureDataQ.pop_front();
|
|
||||||
mTextureDataLookup.erase(mTextureDataLookup.find(textureData.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureLoader::load(std::shared_ptr<TextureData> textureData)
|
|
||||||
{
|
|
||||||
// Make sure it's not already loaded
|
|
||||||
if (!textureData->isLoaded())
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
// Remove it from the queue if it is already there
|
|
||||||
auto td = mTextureDataLookup.find(textureData.get());
|
|
||||||
if (td != mTextureDataLookup.end())
|
|
||||||
{
|
|
||||||
mTextureDataQ.erase((*td).second);
|
|
||||||
mTextureDataLookup.erase(td);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put it on the start of the queue as we want the newly requested textures to load first
|
|
||||||
mTextureDataQ.push_front(textureData);
|
|
||||||
mTextureDataLookup[textureData.get()] = mTextureDataQ.begin();
|
|
||||||
mEvent.notify_one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureLoader::remove(std::shared_ptr<TextureData> textureData)
|
|
||||||
{
|
|
||||||
// Just remove it from the queue so we don't attempt to load it
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
auto td = mTextureDataLookup.find(textureData.get());
|
|
||||||
if (td != mTextureDataLookup.end())
|
|
||||||
{
|
|
||||||
mTextureDataQ.erase((*td).second);
|
|
||||||
mTextureDataLookup.erase(td);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t TextureLoader::getQueueSize()
|
|
||||||
{
|
|
||||||
// Gets the amount of video memory that will be used once all textures in
|
|
||||||
// the queue are loaded
|
|
||||||
size_t mem = 0;
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
for (auto tex : mTextureDataQ)
|
|
||||||
{
|
|
||||||
mem += tex->width() * tex->height() * 4;
|
|
||||||
}
|
|
||||||
return mem;
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "platform.h"
|
|
||||||
#include "resources/TextureData.h"
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class TextureResource;
|
|
||||||
|
|
||||||
class TextureLoader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextureLoader();
|
|
||||||
~TextureLoader();
|
|
||||||
|
|
||||||
void load(std::shared_ptr<TextureData> textureData);
|
|
||||||
void remove(std::shared_ptr<TextureData> textureData);
|
|
||||||
|
|
||||||
size_t getQueueSize();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void processQueue();
|
|
||||||
void threadProc();
|
|
||||||
|
|
||||||
std::list<std::shared_ptr<TextureData> > mTextureDataQ;
|
|
||||||
std::map<TextureData*, std::list<std::shared_ptr<TextureData> >::iterator > mTextureDataLookup;
|
|
||||||
|
|
||||||
std::thread* mThread;
|
|
||||||
std::mutex mMutex;
|
|
||||||
std::condition_variable mEvent;
|
|
||||||
bool mExit;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// This class manages the loading and unloading of textures
|
|
||||||
//
|
|
||||||
// When textures are added, the texture data is just stored as-is. The texture
|
|
||||||
// data should only have been constructed and not loaded for this to work correctly.
|
|
||||||
// When the get() function is called it indicates that a texture wants to be used so
|
|
||||||
// at this point the texture data is loaded (via a call to load()).
|
|
||||||
//
|
|
||||||
// Once the load is complete (which may not be on the first call to get() if the
|
|
||||||
// data is loaded in a background thread) then the get() function call uploadAndBind()
|
|
||||||
// to upload to VRAM if necessary and bind the texture. This is followed by a call
|
|
||||||
// to releaseRAM() which frees the memory buffer if the texture can be reloaded from
|
|
||||||
// disk if needed again
|
|
||||||
//
|
|
||||||
class TextureDataManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextureDataManager();
|
|
||||||
~TextureDataManager();
|
|
||||||
|
|
||||||
std::shared_ptr<TextureData> add(const TextureResource* key, bool tiled);
|
|
||||||
|
|
||||||
// The texturedata being removed may be loading in a different thread. However it will
|
|
||||||
// be referenced by a smart point so we only need to remove it from our array and it
|
|
||||||
// will be deleted when the other thread has finished with it
|
|
||||||
void remove(const TextureResource* key);
|
|
||||||
|
|
||||||
std::shared_ptr<TextureData> get(const TextureResource* key);
|
|
||||||
bool bind(const TextureResource* key);
|
|
||||||
|
|
||||||
// Get the total size of all textures managed by this object, loaded and unloaded in bytes
|
|
||||||
size_t getTotalSize();
|
|
||||||
// Get the total size of all committed textures (in VRAM) in bytes
|
|
||||||
size_t getCommittedSize();
|
|
||||||
// Get the total size of all load-pending textures in the queue - these will
|
|
||||||
// be committed to VRAM as the queue is processed
|
|
||||||
size_t getQueueSize();
|
|
||||||
// Load a texture, freeing resources as necessary to make space
|
|
||||||
void load(std::shared_ptr<TextureData> tex, bool block = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::list<std::shared_ptr<TextureData> > mTextures;
|
|
||||||
std::map<const TextureResource*, std::list<std::shared_ptr<TextureData> >::iterator > mTextureLookup;
|
|
||||||
std::shared_ptr<TextureData> mBlank;
|
|
||||||
TextureLoader* mLoader;
|
|
||||||
};
|
|
||||||
|
|
|
@ -5,113 +5,108 @@
|
||||||
#include "ImageIO.h"
|
#include "ImageIO.h"
|
||||||
#include "Renderer.h"
|
#include "Renderer.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "Settings.h"
|
#include "resources/SVGResource.h"
|
||||||
|
|
||||||
TextureDataManager TextureResource::sTextureDataManager;
|
|
||||||
std::map< TextureResource::TextureKeyType, std::weak_ptr<TextureResource> > TextureResource::sTextureMap;
|
std::map< TextureResource::TextureKeyType, std::weak_ptr<TextureResource> > TextureResource::sTextureMap;
|
||||||
std::set<TextureResource*> TextureResource::sAllTextures;
|
std::list< std::weak_ptr<TextureResource> > TextureResource::sTextureList;
|
||||||
|
|
||||||
TextureResource::TextureResource(const std::string& path, bool tile, bool dynamic) : mTextureData(nullptr), mForceLoad(false)
|
TextureResource::TextureResource(const std::string& path, bool tile) :
|
||||||
|
mTextureID(0), mPath(path), mTextureSize(Eigen::Vector2i::Zero()), mTile(tile)
|
||||||
{
|
{
|
||||||
// Create a texture data object for this texture
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
// If there is a path then the 'dynamic' flag tells us whether to use the texture
|
|
||||||
// data manager to manage loading/unloading of this texture
|
|
||||||
std::shared_ptr<TextureData> data;
|
|
||||||
if (dynamic)
|
|
||||||
{
|
|
||||||
data = sTextureDataManager.add(this, tile);
|
|
||||||
data->initFromPath(path);
|
|
||||||
// Force the texture manager to load it using a blocking load
|
|
||||||
sTextureDataManager.load(data, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mTextureData = std::shared_ptr<TextureData>(new TextureData(tile));
|
|
||||||
data = mTextureData;
|
|
||||||
data->initFromPath(path);
|
|
||||||
// Load it so we can read the width/height
|
|
||||||
data->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
mSize << data->width(), data->height();
|
|
||||||
mSourceSize << data->sourceWidth(), data->sourceHeight();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create a texture managed by this class because it cannot be dynamically loaded and unloaded
|
|
||||||
mTextureData = std::shared_ptr<TextureData>(new TextureData(tile));
|
|
||||||
}
|
|
||||||
sAllTextures.insert(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureResource::~TextureResource()
|
TextureResource::~TextureResource()
|
||||||
{
|
{
|
||||||
if (mTextureData == nullptr)
|
deinit();
|
||||||
sTextureDataManager.remove(this);
|
}
|
||||||
|
|
||||||
sAllTextures.erase(sAllTextures.find(this));
|
void TextureResource::unload(std::shared_ptr<ResourceManager>& rm)
|
||||||
|
{
|
||||||
|
deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureResource::reload(std::shared_ptr<ResourceManager>& rm)
|
||||||
|
{
|
||||||
|
if(!mPath.empty())
|
||||||
|
{
|
||||||
|
const ResourceData& data = rm->getFileData(mPath);
|
||||||
|
initFromMemory((const char*)data.ptr.get(), data.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureResource::initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height)
|
void TextureResource::initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height)
|
||||||
{
|
{
|
||||||
// This is only valid if we have a local texture data object
|
deinit();
|
||||||
assert(mTextureData != nullptr);
|
|
||||||
mTextureData->releaseVRAM();
|
assert(width > 0 && height > 0);
|
||||||
mTextureData->releaseRAM();
|
|
||||||
mTextureData->initFromRGBA(dataRGBA, width, height);
|
//now for the openGL texture stuff
|
||||||
// Cache the image dimensions
|
glGenTextures(1, &mTextureID);
|
||||||
mSize << width, height;
|
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||||
mSourceSize << mTextureData->sourceWidth(), mTextureData->sourceHeight();
|
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dataRGBA);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
const GLint wrapMode = mTile ? GL_REPEAT : GL_CLAMP_TO_EDGE;
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
|
||||||
|
|
||||||
|
mTextureSize << width, height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureResource::initFromMemory(const char* data, size_t length)
|
void TextureResource::initFromMemory(const char* data, size_t length)
|
||||||
{
|
{
|
||||||
// This is only valid if we have a local texture data object
|
size_t width, height;
|
||||||
assert(mTextureData != nullptr);
|
std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(data), length, width, height);
|
||||||
mTextureData->releaseVRAM();
|
|
||||||
mTextureData->releaseRAM();
|
if(imageRGBA.size() == 0)
|
||||||
mTextureData->initImageFromMemory((const unsigned char*)data, length);
|
{
|
||||||
// Get the size from the texture data
|
LOG(LogError) << "Could not initialize texture from memory, invalid data! (file path: " << mPath << ", data ptr: " << (size_t)data << ", reported size: " << length << ")";
|
||||||
mSize << mTextureData->width(), mTextureData->height();
|
return;
|
||||||
mSourceSize << mTextureData->sourceWidth(), mTextureData->sourceHeight();
|
}
|
||||||
|
|
||||||
|
initFromPixels(imageRGBA.data(), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Eigen::Vector2i TextureResource::getSize() const
|
void TextureResource::deinit()
|
||||||
{
|
{
|
||||||
return mSize;
|
if(mTextureID != 0)
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &mTextureID);
|
||||||
|
mTextureID = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Eigen::Vector2i& TextureResource::getSize() const
|
||||||
|
{
|
||||||
|
return mTextureSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureResource::isTiled() const
|
bool TextureResource::isTiled() const
|
||||||
{
|
{
|
||||||
if (mTextureData != nullptr)
|
return mTile;
|
||||||
return mTextureData->tiled();
|
|
||||||
std::shared_ptr<TextureData> data = sTextureDataManager.get(this);
|
|
||||||
return data->tiled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureResource::bind()
|
void TextureResource::bind() const
|
||||||
{
|
{
|
||||||
if (mTextureData != nullptr)
|
if(mTextureID != 0)
|
||||||
{
|
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||||
mTextureData->uploadAndBind();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
LOG(LogError) << "Tried to bind uninitialized texture!";
|
||||||
return sTextureDataManager.bind(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> TextureResource::get(const std::string& path, bool tile, bool forceLoad, bool dynamic)
|
|
||||||
|
std::shared_ptr<TextureResource> TextureResource::get(const std::string& path, bool tile)
|
||||||
{
|
{
|
||||||
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
|
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
|
||||||
|
|
||||||
const std::string canonicalPath = getCanonicalPath(path);
|
const std::string canonicalPath = getCanonicalPath(path);
|
||||||
|
|
||||||
if(canonicalPath.empty())
|
if(canonicalPath.empty())
|
||||||
{
|
{
|
||||||
std::shared_ptr<TextureResource> tex(new TextureResource("", tile, false));
|
std::shared_ptr<TextureResource> tex(new TextureResource("", tile));
|
||||||
rm->addReloadable(tex); //make sure we get properly deinitialized even though we do nothing on reinitialization
|
rm->addReloadable(tex); //make sure we get properly deinitialized even though we do nothing on reinitialization
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
@ -126,100 +121,58 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path, b
|
||||||
|
|
||||||
// need to create it
|
// need to create it
|
||||||
std::shared_ptr<TextureResource> tex;
|
std::shared_ptr<TextureResource> tex;
|
||||||
tex = std::shared_ptr<TextureResource>(new TextureResource(key.first, tile, dynamic));
|
|
||||||
std::shared_ptr<TextureData> data = sTextureDataManager.get(tex.get());
|
|
||||||
|
|
||||||
// is it an SVG?
|
// is it an SVG?
|
||||||
if(key.first.substr(key.first.size() - 4, std::string::npos) != ".svg")
|
if(key.first.substr(key.first.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
|
// probably
|
||||||
|
// don't add it to our map because 2 svgs might be rasterized at different sizes
|
||||||
|
tex = std::shared_ptr<SVGResource>(new SVGResource(key.first, tile));
|
||||||
|
sTextureList.push_back(tex); // add it to our list though
|
||||||
|
rm->addReloadable(tex);
|
||||||
|
tex->reload(rm);
|
||||||
|
return tex;
|
||||||
|
}else{
|
||||||
|
// normal texture
|
||||||
|
tex = std::shared_ptr<TextureResource>(new TextureResource(key.first, tile));
|
||||||
sTextureMap[key] = std::weak_ptr<TextureResource>(tex);
|
sTextureMap[key] = std::weak_ptr<TextureResource>(tex);
|
||||||
|
sTextureList.push_back(tex);
|
||||||
|
rm->addReloadable(tex);
|
||||||
|
tex->reload(ResourceManager::getInstance());
|
||||||
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add it to the reloadable list
|
|
||||||
rm->addReloadable(tex);
|
|
||||||
|
|
||||||
// Force load it if necessary. Note that it may get dumped from VRAM if we run low
|
|
||||||
if (forceLoad)
|
|
||||||
{
|
|
||||||
tex->mForceLoad = forceLoad;
|
|
||||||
data->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For scalable source images in textures we want to set the resolution to rasterize at
|
|
||||||
void TextureResource::rasterizeAt(size_t width, size_t height)
|
|
||||||
{
|
|
||||||
std::shared_ptr<TextureData> data;
|
|
||||||
if (mTextureData != nullptr)
|
|
||||||
data = mTextureData;
|
|
||||||
else
|
|
||||||
data = sTextureDataManager.get(this);
|
|
||||||
mSourceSize << (float)width, (float)height;
|
|
||||||
data->setSourceSize((float)width, (float)height);
|
|
||||||
if (mForceLoad || (mTextureData != nullptr))
|
|
||||||
data->load();
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Vector2f TextureResource::getSourceImageSize() const
|
|
||||||
{
|
|
||||||
return mSourceSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureResource::isInitialized() const
|
bool TextureResource::isInitialized() const
|
||||||
{
|
{
|
||||||
return true;
|
return mTextureID != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TextureResource::getMemUsage() const
|
||||||
|
{
|
||||||
|
if(!mTextureID || mTextureSize.x() == 0 || mTextureSize.y() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mTextureSize.x() * mTextureSize.y() * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TextureResource::getTotalMemUsage()
|
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)
|
auto it = sTextureList.begin();
|
||||||
|
while(it != sTextureList.end())
|
||||||
{
|
{
|
||||||
if (tex->mTextureData != nullptr)
|
if((*it).expired())
|
||||||
total += tex->mTextureData->getVRAMUsage();
|
{
|
||||||
|
// remove expired textures from the list
|
||||||
|
it = sTextureList.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
total += (*it).lock()->getMemUsage();
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
// Now get the committed memory from the manager
|
|
||||||
total += sTextureDataManager.getCommittedSize();
|
|
||||||
// And the size of the loading queue
|
|
||||||
total += sTextureDataManager.getQueueSize();
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TextureResource::getTotalTextureSize()
|
|
||||||
{
|
|
||||||
size_t total = 0;
|
|
||||||
// Count up all textures that manage their own texture data
|
|
||||||
for (auto tex : sAllTextures)
|
|
||||||
{
|
|
||||||
if (tex->mTextureData != nullptr)
|
|
||||||
total += tex->getSize().x() * tex->getSize().y() * 4;
|
|
||||||
}
|
|
||||||
// Now get the total memory from the manager
|
|
||||||
total += sTextureDataManager.getTotalSize();
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureResource::unload(std::shared_ptr<ResourceManager>& rm)
|
|
||||||
{
|
|
||||||
// Release the texture's resources
|
|
||||||
std::shared_ptr<TextureData> data;
|
|
||||||
if (mTextureData == nullptr)
|
|
||||||
data = sTextureDataManager.get(this);
|
|
||||||
else
|
|
||||||
data = mTextureData;
|
|
||||||
|
|
||||||
data->releaseVRAM();
|
|
||||||
data->releaseRAM();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureResource::reload(std::shared_ptr<ResourceManager>& rm)
|
|
||||||
{
|
|
||||||
// For dynamically loaded textures the texture manager will load them on demand.
|
|
||||||
// For manually loaded textures we have to reload them here
|
|
||||||
if (mTextureData)
|
|
||||||
mTextureData->load();
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,12 +3,8 @@
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
|
||||||
#include <list>
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "resources/TextureData.h"
|
|
||||||
#include "resources/TextureDataManager.h"
|
|
||||||
#include GLHEADER
|
#include GLHEADER
|
||||||
|
|
||||||
// An OpenGL texture.
|
// An OpenGL texture.
|
||||||
|
@ -16,43 +12,40 @@
|
||||||
class TextureResource : public IReloadable
|
class TextureResource : public IReloadable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<TextureResource> get(const std::string& path, bool tile = false, bool forceLoad = false, bool dynamic = true);
|
static std::shared_ptr<TextureResource> get(const std::string& path, bool tile = false);
|
||||||
void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height);
|
|
||||||
virtual void initFromMemory(const char* file, size_t length);
|
|
||||||
|
|
||||||
// For scalable source images in textures we want to set the resolution to rasterize at
|
|
||||||
void rasterizeAt(size_t width, size_t height);
|
|
||||||
Eigen::Vector2f getSourceImageSize() const;
|
|
||||||
|
|
||||||
virtual ~TextureResource();
|
virtual ~TextureResource();
|
||||||
|
|
||||||
|
virtual void unload(std::shared_ptr<ResourceManager>& rm) override;
|
||||||
|
virtual void reload(std::shared_ptr<ResourceManager>& rm) override;
|
||||||
|
|
||||||
bool isInitialized() const;
|
bool isInitialized() const;
|
||||||
bool isTiled() const;
|
bool isTiled() const;
|
||||||
|
const Eigen::Vector2i& getSize() const;
|
||||||
|
void bind() const;
|
||||||
|
|
||||||
|
// Warning: will NOT correctly reinitialize when this texture is reloaded (e.g. ES starts/stops playing a game).
|
||||||
|
virtual void initFromMemory(const char* file, size_t length);
|
||||||
|
|
||||||
const Eigen::Vector2i getSize() const;
|
// Warning: will NOT correctly reinitialize when this texture is reloaded (e.g. ES starts/stops playing a game).
|
||||||
bool bind();
|
void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height);
|
||||||
|
|
||||||
|
size_t getMemUsage() const; // returns an approximation of the VRAM used by this texture (in bytes)
|
||||||
static size_t getTotalMemUsage(); // returns an approximation of total VRAM used by textures (in bytes)
|
static size_t getTotalMemUsage(); // returns an approximation of total VRAM used by textures (in bytes)
|
||||||
static size_t getTotalTextureSize(); // returns the number of bytes that would be used if all textures were in memory
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TextureResource(const std::string& path, bool tile, bool dynamic);
|
TextureResource(const std::string& path, bool tile);
|
||||||
virtual void unload(std::shared_ptr<ResourceManager>& rm);
|
void deinit();
|
||||||
virtual void reload(std::shared_ptr<ResourceManager>& rm);
|
|
||||||
|
Eigen::Vector2i mTextureSize;
|
||||||
|
const std::string mPath;
|
||||||
|
const bool mTile;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mTextureData is used for textures that are not loaded from a file - these ones
|
GLuint mTextureID;
|
||||||
// are permanently allocated and cannot be loaded and unloaded based on resources
|
|
||||||
std::shared_ptr<TextureData> mTextureData;
|
|
||||||
|
|
||||||
// The texture data manager manages loading and unloading of filesystem based textures
|
|
||||||
static TextureDataManager sTextureDataManager;
|
|
||||||
|
|
||||||
Eigen::Vector2i mSize;
|
|
||||||
Eigen::Vector2f mSourceSize;
|
|
||||||
bool mForceLoad;
|
|
||||||
|
|
||||||
typedef std::pair<std::string, bool> TextureKeyType;
|
typedef std::pair<std::string, bool> TextureKeyType;
|
||||||
static std::map< TextureKeyType, std::weak_ptr<TextureResource> > sTextureMap; // map of textures, used to prevent duplicate textures
|
static std::map< TextureKeyType, std::weak_ptr<TextureResource> > sTextureMap; // map of textures, used to prevent duplicate textures
|
||||||
static std::set<TextureResource*> sAllTextures; // Set of all textures, used for memory management
|
|
||||||
|
static std::list< std::weak_ptr<TextureResource> > sTextureList; // list of all textures, used for memory approximations
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue