2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-21 12:25:28 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-21 12:25:28 +00:00
|
|
|
// TextureResource.cpp
|
|
|
|
//
|
|
|
|
// Handles OpenGL textures.
|
|
|
|
//
|
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "resources/TextureResource.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "utils/FileSystemUtil.h"
|
2021-10-25 16:39:58 +00:00
|
|
|
#include "utils/StringUtil.h"
|
2014-03-20 01:13:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
TextureDataManager TextureResource::sTextureDataManager;
|
2021-07-07 18:31:46 +00:00
|
|
|
std::map<TextureResource::TextureKeyType, std::weak_ptr<TextureResource>>
|
|
|
|
TextureResource::sTextureMap;
|
2020-08-08 13:14:33 +00:00
|
|
|
std::set<TextureResource*> TextureResource::sAllTextures;
|
2013-07-09 05:44:24 +00:00
|
|
|
|
2021-10-29 17:43:07 +00:00
|
|
|
TextureResource::TextureResource(
|
|
|
|
const std::string& path, bool tile, bool dynamic, bool linearMagnify, bool forceRasterization)
|
2021-07-07 18:31:46 +00:00
|
|
|
: mTextureData(nullptr)
|
|
|
|
, mForceLoad(false)
|
2013-06-21 16:49:29 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// 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);
|
2021-08-19 18:16:42 +00:00
|
|
|
data->setLinearMagnify(linearMagnify);
|
2021-10-26 16:22:41 +00:00
|
|
|
data->setForceRasterization(forceRasterization);
|
2020-06-21 12:25:28 +00:00
|
|
|
// 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);
|
2021-08-19 18:16:42 +00:00
|
|
|
data->setLinearMagnify(linearMagnify);
|
2021-10-26 16:22:41 +00:00
|
|
|
data->setForceRasterization(forceRasterization);
|
2020-06-21 12:25:28 +00:00
|
|
|
// Load it so we can read the width/height.
|
|
|
|
data->load();
|
|
|
|
}
|
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
mSize = glm::ivec2{static_cast<int>(data->width()), static_cast<int>(data->height())};
|
|
|
|
mSourceSize = glm::vec2{data->sourceWidth(), data->sourceHeight()};
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Create a texture managed by this class because it cannot be dynamically
|
2020-08-08 13:14:33 +00:00
|
|
|
// loaded and unloaded. This would normally be a video texture, where the player
|
|
|
|
// reserves a texture to later be used for the video rendering.
|
2020-06-21 12:25:28 +00:00
|
|
|
mTextureData = std::shared_ptr<TextureData>(new TextureData(tile));
|
2021-08-17 16:41:45 +00:00
|
|
|
mSize = glm::ivec2{};
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
sAllTextures.insert(this);
|
2013-07-09 05:44:24 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:28:06 +00:00
|
|
|
TextureResource::~TextureResource()
|
2013-06-21 16:49:29 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mTextureData == nullptr)
|
|
|
|
sTextureDataManager.remove(this);
|
2013-06-21 16:49:29 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
sAllTextures.erase(sAllTextures.find(this));
|
2013-06-21 16:49:29 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:28:06 +00:00
|
|
|
void TextureResource::initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height)
|
2013-09-20 23:55:05 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// This is only valid if we have a local texture data object.
|
|
|
|
assert(mTextureData != nullptr);
|
|
|
|
mTextureData->releaseVRAM();
|
|
|
|
mTextureData->releaseRAM();
|
|
|
|
mTextureData->initFromRGBA(dataRGBA, width, height);
|
|
|
|
// Cache the image dimensions.
|
2021-08-17 16:41:45 +00:00
|
|
|
mSize = glm::ivec2{static_cast<int>(width), static_cast<int>(height)};
|
|
|
|
mSourceSize = glm::vec2{mTextureData->sourceWidth(), mTextureData->sourceHeight()};
|
2013-09-20 23:55:05 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:28:06 +00:00
|
|
|
void TextureResource::initFromMemory(const char* data, size_t length)
|
2013-07-09 05:44:24 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// This is only valid if we have a local texture data object.
|
|
|
|
assert(mTextureData != nullptr);
|
|
|
|
mTextureData->releaseVRAM();
|
|
|
|
mTextureData->releaseRAM();
|
2020-12-16 22:59:00 +00:00
|
|
|
mTextureData->initImageFromMemory(reinterpret_cast<const unsigned char*>(data), length);
|
2020-06-21 12:25:28 +00:00
|
|
|
// Get the size from the texture data.
|
2021-08-17 16:41:45 +00:00
|
|
|
mSize = glm::ivec2{static_cast<int>(mTextureData->width()),
|
|
|
|
static_cast<int>(mTextureData->height())};
|
|
|
|
mSourceSize = glm::vec2{mTextureData->sourceWidth(), mTextureData->sourceHeight()};
|
2013-07-09 05:44:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 16:18:00 +00:00
|
|
|
void TextureResource::manualUnload(std::string path, bool tile)
|
|
|
|
{
|
|
|
|
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path);
|
|
|
|
|
|
|
|
TextureKeyType key(canonicalPath, tile);
|
|
|
|
auto foundTexture = sTextureMap.find(key);
|
|
|
|
|
|
|
|
if (foundTexture != sTextureMap.cend()) {
|
|
|
|
sTextureMap.erase(foundTexture);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-12 19:08:35 +00:00
|
|
|
std::vector<unsigned char> TextureResource::getRawRGBAData()
|
|
|
|
{
|
|
|
|
std::shared_ptr<TextureData> data = sTextureDataManager.get(this);
|
|
|
|
|
|
|
|
if (data)
|
|
|
|
return data.get()->getRawRGBAData();
|
|
|
|
else
|
|
|
|
return std::vector<unsigned char>(0);
|
|
|
|
}
|
|
|
|
|
2021-07-02 18:33:50 +00:00
|
|
|
std::string TextureResource::getTextureFilePath()
|
|
|
|
{
|
|
|
|
std::shared_ptr<TextureData> data = sTextureDataManager.get(this);
|
|
|
|
|
|
|
|
if (data)
|
|
|
|
return data->getTextureFilePath();
|
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
bool TextureResource::isTiled() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mTextureData != nullptr)
|
|
|
|
return mTextureData->tiled();
|
|
|
|
std::shared_ptr<TextureData> data = sTextureDataManager.get(this);
|
|
|
|
return data->tiled();
|
2014-01-19 18:23:01 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:28:06 +00:00
|
|
|
bool TextureResource::bind()
|
2013-07-09 05:44:24 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mTextureData != nullptr) {
|
|
|
|
mTextureData->uploadAndBind();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return sTextureDataManager.bind(this);
|
|
|
|
}
|
2013-07-09 05:44:24 +00:00
|
|
|
}
|
|
|
|
|
2021-08-19 18:16:42 +00:00
|
|
|
std::shared_ptr<TextureResource> TextureResource::get(const std::string& path,
|
|
|
|
bool tile,
|
|
|
|
bool forceLoad,
|
|
|
|
bool dynamic,
|
|
|
|
bool linearMagnify,
|
2021-10-29 17:43:07 +00:00
|
|
|
bool forceRasterization)
|
2013-07-09 05:44:24 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<ResourceManager>& rm = ResourceManager::getInstance();
|
|
|
|
|
|
|
|
const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path);
|
|
|
|
if (canonicalPath.empty()) {
|
2021-10-29 17:43:07 +00:00
|
|
|
std::shared_ptr<TextureResource> tex(
|
|
|
|
new TextureResource("", tile, false, linearMagnify, forceRasterization));
|
2020-06-21 12:25:28 +00:00
|
|
|
// Make sure we get properly deinitialized even though we do nothing on reinitialization.
|
|
|
|
rm->addReloadable(tex);
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextureKeyType key(canonicalPath, tile);
|
|
|
|
auto foundTexture = sTextureMap.find(key);
|
|
|
|
|
|
|
|
if (foundTexture != sTextureMap.cend()) {
|
|
|
|
if (!foundTexture->second.expired())
|
|
|
|
return foundTexture->second.lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to create it.
|
|
|
|
std::shared_ptr<TextureResource> tex;
|
2021-10-29 17:43:07 +00:00
|
|
|
tex = std::shared_ptr<TextureResource>(
|
|
|
|
new TextureResource(key.first, tile, dynamic, linearMagnify, forceRasterization));
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<TextureData> data = sTextureDataManager.get(tex.get());
|
|
|
|
|
|
|
|
// Is it an SVG?
|
2021-10-25 16:39:58 +00:00
|
|
|
if (Utils::String::toLower(key.first.substr(key.first.size() - 4, std::string::npos)) !=
|
|
|
|
".svg") {
|
2020-08-15 07:28:47 +00:00
|
|
|
// Probably not. Add it to our map. We don't add SVGs because 2 SVGs might be
|
2020-06-21 12:25:28 +00:00
|
|
|
// rasterized at different sizes.
|
|
|
|
sTextureMap[key] = std::weak_ptr<TextureResource>(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;
|
2013-07-09 05:44:24 +00:00
|
|
|
}
|
2014-03-24 21:29:56 +00:00
|
|
|
|
2021-10-23 13:45:44 +00:00
|
|
|
void TextureResource::rasterizeAt(float width, float height)
|
2014-03-24 21:29:56 +00:00
|
|
|
{
|
2021-09-27 18:59:33 +00:00
|
|
|
if (mTextureData != nullptr) {
|
|
|
|
glm::vec2 textureSize = mTextureData.get()->getSize();
|
|
|
|
if (textureSize.x == width && textureSize.y == height)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<TextureData> data;
|
|
|
|
if (mTextureData != nullptr)
|
|
|
|
data = mTextureData;
|
|
|
|
else
|
|
|
|
data = sTextureDataManager.get(this);
|
2021-08-17 16:41:45 +00:00
|
|
|
mSourceSize = glm::vec2{static_cast<float>(width), static_cast<float>(height)};
|
2020-11-17 22:06:54 +00:00
|
|
|
data->setSourceSize(static_cast<float>(width), static_cast<float>(height));
|
2021-10-26 16:22:41 +00:00
|
|
|
if (mForceLoad || mTextureData != nullptr)
|
2020-06-21 12:25:28 +00:00
|
|
|
data->load();
|
2014-03-24 21:29:56 +00:00
|
|
|
}
|
2014-03-27 21:47:25 +00:00
|
|
|
|
|
|
|
size_t TextureResource::getTotalMemUsage()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
size_t total = 0;
|
|
|
|
// Count up all textures that manage their own texture data.
|
|
|
|
for (auto tex : sAllTextures) {
|
|
|
|
if (tex->mTextureData != nullptr)
|
|
|
|
total += tex->mTextureData->getVRAMUsage();
|
|
|
|
}
|
|
|
|
// Now get the committed memory from the manager.
|
|
|
|
total += sTextureDataManager.getCommittedSize();
|
|
|
|
// And the size of the loading queue.
|
|
|
|
total += sTextureDataManager.getQueueSize();
|
|
|
|
return total;
|
2017-01-22 23:28:06 +00:00
|
|
|
}
|
2014-03-27 21:47:25 +00:00
|
|
|
|
2017-01-22 23:28:06 +00:00
|
|
|
size_t TextureResource::getTotalTextureSize()
|
|
|
|
{
|
2021-08-17 16:41:45 +00:00
|
|
|
size_t total{0};
|
2020-06-21 12:25:28 +00:00
|
|
|
// Count up all textures that manage their own texture data.
|
|
|
|
for (auto tex : sAllTextures) {
|
|
|
|
if (tex->mTextureData != nullptr)
|
2021-08-17 16:41:45 +00:00
|
|
|
total += tex->getSize().x * tex->getSize().y * 4;
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
// Now get the total memory from the manager.
|
|
|
|
total += sTextureDataManager.getTotalSize();
|
|
|
|
return total;
|
2014-03-27 21:47:25 +00:00
|
|
|
}
|
2017-01-22 23:28:06 +00:00
|
|
|
|
2017-11-17 14:58:52 +00:00
|
|
|
void TextureResource::unload(std::shared_ptr<ResourceManager>& /*rm*/)
|
2017-01-22 23:28:06 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Release the texture's resources.
|
|
|
|
std::shared_ptr<TextureData> data;
|
|
|
|
if (mTextureData == nullptr)
|
|
|
|
data = sTextureDataManager.get(this);
|
|
|
|
else
|
|
|
|
data = mTextureData;
|
|
|
|
|
|
|
|
data->releaseVRAM();
|
|
|
|
data->releaseRAM();
|
2017-01-22 23:28:06 +00:00
|
|
|
}
|
|
|
|
|
2017-11-17 14:58:52 +00:00
|
|
|
void TextureResource::reload(std::shared_ptr<ResourceManager>& /*rm*/)
|
2017-01-22 23:28:06 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// 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();
|
2017-01-22 23:28:06 +00:00
|
|
|
}
|