mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
renderer/loader: revamping the caching mechanism.
The previous loader cache mechanism encountered a problem when the user changed the content of the cached data. In such cases, a new request would not be processed because the renderer would use the previously cached content. So far, the TVG cache mechanism utilizes a pointer hash key for the fastest hashing mechanism available. One limitation is that it assumes the address is unique for the data. To resolve this, we modified the caching policy. Now, the renderer will not cache copied data; it will only cache the given data when it is deemed shareable. issue: https://github.com/thorvg/thorvg/issues/2020
This commit is contained in:
parent
ecf030d650
commit
16215c16dc
4 changed files with 68 additions and 19 deletions
16
inc/thorvg.h
16
inc/thorvg.h
|
@ -1223,6 +1223,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Loads a picture data directly from a file.
|
* @brief Loads a picture data directly from a file.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p path as a key.
|
||||||
|
* This means that loading the same file again will not result in duplicate operations;
|
||||||
|
* instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* @param[in] path A path to the picture file.
|
* @param[in] path A path to the picture file.
|
||||||
*
|
*
|
||||||
* @retval Result::Success When succeed.
|
* @retval Result::Success When succeed.
|
||||||
|
@ -1238,6 +1242,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Loads a picture data from a memory block of a given size.
|
* @brief Loads a picture data from a memory block of a given size.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p data address as a key
|
||||||
|
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
|
||||||
|
* for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* @param[in] data A pointer to a memory location where the content of the picture file is stored.
|
* @param[in] data A pointer to a memory location where the content of the picture file is stored.
|
||||||
* @param[in] size The size in bytes of the memory occupied by the @p data.
|
* @param[in] size The size in bytes of the memory occupied by the @p data.
|
||||||
* @param[in] copy Decides whether the data should be copied into the engine local buffer.
|
* @param[in] copy Decides whether the data should be copied into the engine local buffer.
|
||||||
|
@ -1299,6 +1307,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Loads a raw data from a memory block with a given size.
|
* @brief Loads a raw data from a memory block with a given size.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p data address as a key
|
||||||
|
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
|
||||||
|
* for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* @param[in] paint A Tvg_Paint pointer to the picture object.
|
* @param[in] paint A Tvg_Paint pointer to the picture object.
|
||||||
* @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
|
* @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
|
||||||
* @param[in] w The width of the image @p data in pixels.
|
* @param[in] w The width of the image @p data in pixels.
|
||||||
|
@ -1544,6 +1556,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Loads a scalable font data(ttf) from a file.
|
* @brief Loads a scalable font data(ttf) from a file.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p path as a key.
|
||||||
|
* This means that loading the same file again will not result in duplicate operations;
|
||||||
|
* instead, ThorVG will reuse the previously loaded font data.
|
||||||
|
*
|
||||||
* @param[in] path The path to the font file.
|
* @param[in] path The path to the font file.
|
||||||
*
|
*
|
||||||
* @retval Result::Success When succeed.
|
* @retval Result::Success When succeed.
|
||||||
|
|
|
@ -1956,6 +1956,10 @@ TVG_API Tvg_Paint* tvg_picture_new();
|
||||||
/*!
|
/*!
|
||||||
* \brief Loads a picture data directly from a file.
|
* \brief Loads a picture data directly from a file.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p path as a key.
|
||||||
|
* This means that loading the same file again will not result in duplicate operations;
|
||||||
|
* instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
||||||
* \param[in] path The absolute path to the image file.
|
* \param[in] path The absolute path to the image file.
|
||||||
*
|
*
|
||||||
|
@ -1971,6 +1975,10 @@ TVG_API Tvg_Result tvg_picture_load(Tvg_Paint* paint, const char* path);
|
||||||
/*!
|
/*!
|
||||||
* \brief Loads a picture data from a memory block of a given size.
|
* \brief Loads a picture data from a memory block of a given size.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p data address as a key
|
||||||
|
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
|
||||||
|
* for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
||||||
* \param[in] data A pointer to a memory location where the content of the picture raw data is stored.
|
* \param[in] data A pointer to a memory location where the content of the picture raw data is stored.
|
||||||
* \param[in] w The width of the image @p data in pixels.
|
* \param[in] w The width of the image @p data in pixels.
|
||||||
|
@ -1992,6 +2000,10 @@ TVG_API Tvg_Result tvg_picture_load_raw(Tvg_Paint* paint, uint32_t *data, uint32
|
||||||
/*!
|
/*!
|
||||||
* \brief Loads a picture data from a memory block of a given size.
|
* \brief Loads a picture data from a memory block of a given size.
|
||||||
*
|
*
|
||||||
|
* ThorVG efficiently caches the loaded data using the specified @p data address as a key
|
||||||
|
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
|
||||||
|
* for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
|
||||||
|
*
|
||||||
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
* \param[in] paint A Tvg_Paint pointer to the picture object.
|
||||||
* \param[in] data A pointer to a memory location where the content of the picture file is stored.
|
* \param[in] data A pointer to a memory location where the content of the picture file is stored.
|
||||||
* \param[in] size The size in bytes of the memory occupied by the @p data.
|
* \param[in] size The size in bytes of the memory occupied by the @p data.
|
||||||
|
|
|
@ -32,17 +32,20 @@ struct LoadModule
|
||||||
INLIST_ITEM(LoadModule);
|
INLIST_ITEM(LoadModule);
|
||||||
|
|
||||||
//Use either hashkey(data) or hashpath(path)
|
//Use either hashkey(data) or hashpath(path)
|
||||||
uint64_t hashkey;
|
union {
|
||||||
|
uintptr_t hashkey;
|
||||||
char* hashpath = nullptr;
|
char* hashpath = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
FileType type; //current loader file type
|
FileType type; //current loader file type
|
||||||
uint16_t sharing = 0; //reference count
|
uint16_t sharing = 0; //reference count
|
||||||
bool readied = false; //read done already.
|
bool readied = false; //read done already.
|
||||||
|
bool pathcache = false; //cached by path
|
||||||
|
|
||||||
LoadModule(FileType type) : type(type) {}
|
LoadModule(FileType type) : type(type) {}
|
||||||
virtual ~LoadModule()
|
virtual ~LoadModule()
|
||||||
{
|
{
|
||||||
free(hashpath);
|
if (pathcache) free(hashpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool open(const string& path) { return false; }
|
virtual bool open(const string& path) { return false; }
|
||||||
|
@ -57,6 +60,12 @@ struct LoadModule
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cached()
|
||||||
|
{
|
||||||
|
if (hashkey) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool close()
|
virtual bool close()
|
||||||
{
|
{
|
||||||
if (sharing == 0) return true;
|
if (sharing == 0) return true;
|
||||||
|
|
|
@ -57,9 +57,9 @@
|
||||||
#include "tvgRawLoader.h"
|
#include "tvgRawLoader.h"
|
||||||
|
|
||||||
|
|
||||||
uint64_t HASH_KEY(const char* data, uint64_t size)
|
uintptr_t HASH_KEY(const char* data)
|
||||||
{
|
{
|
||||||
return (((uint64_t) data) << 32) | size;
|
return reinterpret_cast<uintptr_t>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
@ -219,7 +219,7 @@ static LoadModule* _findFromCache(const string& path)
|
||||||
auto loader = _activeLoaders.head;
|
auto loader = _activeLoaders.head;
|
||||||
|
|
||||||
while (loader) {
|
while (loader) {
|
||||||
if (loader->hashpath && !strcmp(loader->hashpath, path.c_str())) {
|
if (loader->pathcache && !strcmp(loader->hashpath, path.c_str())) {
|
||||||
++loader->sharing;
|
++loader->sharing;
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
auto loader = _activeLoaders.head;
|
auto loader = _activeLoaders.head;
|
||||||
|
|
||||||
auto key = HASH_KEY(data, size);
|
auto key = HASH_KEY(data);
|
||||||
|
|
||||||
while (loader) {
|
while (loader) {
|
||||||
if (loader->type == type && loader->hashkey == key) {
|
if (loader->type == type && loader->hashkey == key) {
|
||||||
|
@ -281,7 +281,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
|
||||||
{
|
{
|
||||||
if (!loader) return false;
|
if (!loader) return false;
|
||||||
if (loader->close()) {
|
if (loader->close()) {
|
||||||
{
|
if (loader->cached()) {
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.remove(loader);
|
_activeLoaders.remove(loader);
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
||||||
if (auto loader = _findByPath(path)) {
|
if (auto loader = _findByPath(path)) {
|
||||||
if (loader->open(path)) {
|
if (loader->open(path)) {
|
||||||
loader->hashpath = strdup(path.c_str());
|
loader->hashpath = strdup(path.c_str());
|
||||||
|
loader->pathcache = true;
|
||||||
{
|
{
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
|
@ -313,6 +314,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
||||||
if (auto loader = _find(static_cast<FileType>(i))) {
|
if (auto loader = _find(static_cast<FileType>(i))) {
|
||||||
if (loader->open(path)) {
|
if (loader->open(path)) {
|
||||||
loader->hashpath = strdup(path.c_str());
|
loader->hashpath = strdup(path.c_str());
|
||||||
|
loader->pathcache = true;
|
||||||
{
|
{
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
|
@ -338,7 +340,7 @@ LoadModule* LoaderMgr::loader(const char* key)
|
||||||
auto loader = _activeLoaders.head;
|
auto loader = _activeLoaders.head;
|
||||||
|
|
||||||
while (loader) {
|
while (loader) {
|
||||||
if (loader->hashpath && strstr(loader->hashpath, key)) {
|
if (loader->pathcache && strstr(loader->hashpath, key)) {
|
||||||
++loader->sharing;
|
++loader->sharing;
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
@ -350,15 +352,21 @@ LoadModule* LoaderMgr::loader(const char* key)
|
||||||
|
|
||||||
LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
|
LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||||
{
|
{
|
||||||
|
//Note that users could use the same data pointer with the different content.
|
||||||
|
//Thus caching is only valid for shareable.
|
||||||
|
if (!copy) {
|
||||||
if (auto loader = _findFromCache(data, size, mimeType)) return loader;
|
if (auto loader = _findFromCache(data, size, mimeType)) return loader;
|
||||||
|
}
|
||||||
|
|
||||||
//Try with the given MimeType
|
//Try with the given MimeType
|
||||||
if (!mimeType.empty()) {
|
if (!mimeType.empty()) {
|
||||||
if (auto loader = _findByType(mimeType)) {
|
if (auto loader = _findByType(mimeType)) {
|
||||||
if (loader->open(data, size, copy)) {
|
if (loader->open(data, size, copy)) {
|
||||||
loader->hashkey = HASH_KEY(data, size);
|
if (!copy) {
|
||||||
|
loader->hashkey = HASH_KEY(data);
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
|
}
|
||||||
return loader;
|
return loader;
|
||||||
} else {
|
} else {
|
||||||
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str());
|
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str());
|
||||||
|
@ -371,8 +379,8 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
||||||
auto loader = _find(static_cast<FileType>(i));
|
auto loader = _find(static_cast<FileType>(i));
|
||||||
if (loader) {
|
if (loader) {
|
||||||
if (loader->open(data, size, copy)) {
|
if (loader->open(data, size, copy)) {
|
||||||
loader->hashkey = HASH_KEY(data, size);
|
if (!copy) {
|
||||||
{
|
loader->hashkey = HASH_KEY(data);
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
}
|
}
|
||||||
|
@ -387,14 +395,18 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
||||||
|
|
||||||
LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
|
LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
|
||||||
{
|
{
|
||||||
|
//Note that users could use the same data pointer with the different content.
|
||||||
|
//Thus caching is only valid for shareable.
|
||||||
|
if (!copy) {
|
||||||
//TODO: should we check premultiplied??
|
//TODO: should we check premultiplied??
|
||||||
if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader;
|
if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader;
|
||||||
|
}
|
||||||
|
|
||||||
//function is dedicated for raw images only
|
//function is dedicated for raw images only
|
||||||
auto loader = new RawLoader;
|
auto loader = new RawLoader;
|
||||||
if (loader->open(data, w, h, copy)) {
|
if (loader->open(data, w, h, copy)) {
|
||||||
loader->hashkey = HASH_KEY((const char*)data, w * h);
|
if (!copy) {
|
||||||
{
|
loader->hashkey = HASH_KEY((const char*)data);
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue