From 53116cbb1aebd12757f12ef998b96e3b6d3d8275 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sat, 22 Feb 2020 22:12:09 +0100 Subject: [PATCH 1/8] Cache Images for faster parsing subsequent images (or frames in an animation) --- source/core/support/imageutil.cpp | 10 +- source/parser/ImageCache.cpp | 166 +++++++++++++++++++++++ source/parser/ImageCache.h | 79 +++++++++++ source/parser/parser.cpp | 15 +- windows/vs2015/povparser.vcxproj | 2 + windows/vs2015/povparser.vcxproj.filters | 6 + 6 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 source/parser/ImageCache.cpp create mode 100644 source/parser/ImageCache.h diff --git a/source/core/support/imageutil.cpp b/source/core/support/imageutil.cpp index ae50e194e..7bf3ffcbd 100644 --- a/source/core/support/imageutil.cpp +++ b/source/core/support/imageutil.cpp @@ -1387,10 +1387,14 @@ ImageData *Copy_Image(ImageData *Old) void Destroy_Image(ImageData *image) { - if ((image == nullptr) || (--(image->References) > 0)) - return; + return; + + // Images are now cached - delete image; + // if ((image == nullptr) || (--(image->References) > 0)) + // return; + // + // delete image; } ImageData::~ImageData() diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp new file mode 100644 index 000000000..382e94e34 --- /dev/null +++ b/source/parser/ImageCache.cpp @@ -0,0 +1,166 @@ +//****************************************************************************** +/// +/// @file parser/ImageCache.cpp +/// +/// This module implements a cache for images used in a scene so they only heve +/// to be loaded once during animation rendering or between manual renders +/// +/// @copyright +/// @parblock +/// +/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8. +/// Copyright 1991-2019 Persistence of Vision Raytracer Pty. Ltd. +/// +/// POV-Ray is free software: you can redistribute it and/or modify +/// it under the terms of the GNU Affero General Public License as +/// published by the Free Software Foundation, either version 3 of the +/// License, or (at your option) any later version. +/// +/// POV-Ray is distributed in the hope that it will be useful, +/// but WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/// GNU Affero General Public License for more details. +/// +/// You should have received a copy of the GNU Affero General Public License +/// along with this program. If not, see . +/// +/// ---------------------------------------------------------------------------- +/// +/// POV-Ray is based on the popular DKB raytracer version 2.12. +/// DKBTrace was originally written by David K. Buck. +/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. +/// +/// @endparblock +/// +//****************************************************************************** + + +// C++ variants of C standard header files +#include +#include +#include + +#include +#include + +#ifndef WIN32 +#include // Unix lib for getting last modified file date and time +#endif +#ifdef WIN32 +#define stat _stat // Windows lib for getting last modified file date and time +#endif + + +// POV-Ray header files (base module) +#include "base/base_fwd.h" +#include "base/messenger_fwd.h" +#include "base/povassert.h" +#include "base/stringtypes.h" +#include "base/textstream_fwd.h" +#include "base/textstreambuffer.h" +#include "base/image/image_fwd.h" + +// this must be the last file included +#include "base/povdebug.h" + + +namespace pov_image_cache +{ + using namespace pov_base; + using namespace std; + static inline std::string U16toString(const std::u16string& wstr); + static inline char* StrToChar(const std::string str); + static inline char* U16toChar(const std::u16string& wstr); + + struct ImageCacheEntry final + { + Image* image; + __time64_t lastModified; + }; + + static std::map Cache; // <- The actual cache + + // Gets the last modified time from the filesystem + __time64_t GetLastModifiedTime(const std::string filename) + { + char* cstrFilename = StrToChar(filename); + + struct stat result; + if (stat(cstrFilename, &result) == 0) + { + delete cstrFilename; + return result.st_mtime; + } + delete cstrFilename; + return 0; + } + + // Try to get the image from the cache + Image* GetCachedImage(const UCS2* filename) + { + std::string lookupFilename = U16toString(filename); + + std::map::iterator idx = Cache.find(lookupFilename); + if (idx != Cache.end()) + { + __time64_t lastModified = GetLastModifiedTime(lookupFilename); + if (lastModified == Cache[lookupFilename].lastModified) + return idx->second.image; //Cache[lookupFilename].image; + + // Remove old image from cache and release memory so the newer version can be loaded + delete idx->second.image; + Cache.erase(idx); + } + + return nullptr; + } + + // Store a new image into cache + void StoreImageInCache(const UCS2* filename, Image* image) + { + std::string lookupFilename = U16toString(filename); + __time64_t lastModified = GetLastModifiedTime(lookupFilename); + Cache[lookupFilename] = ImageCacheEntry{ image = image, lastModified = lastModified }; + } + + // May be called frome some menu item, personally, I'd just close PovRay and start a new process (different scenes often share resources in my case) + // Do not allow calling it while parsing or rendering! + void ClearCache() + { + std::map::iterator it = Cache.begin(); + + // Iterate over the map using Iterator till end. + while (it != Cache.end()) + { + delete it->second.image; + Cache.erase(it); + } + } + + static inline std::string U16toString(const std::u16string& wstr) + { + std::string str = ""; + char cstr[3] = "\0"; + mbstate_t mbs; + for (const auto& it : wstr) { + memset(&mbs, 0, sizeof(mbs));//set shift state to the initial state + memmove(cstr, "\0\0\0", 3); + c16rtomb(cstr, it, &mbs); + str.append(std::string(cstr)); + }//for + return str; + } + + static inline char* StrToChar(const std::string str) + { + char* cstring = new char[str.length() + 1]; + strcpy(cstring, str.c_str()); + return cstring; + } + + static inline char* U16toChar(const std::u16string& wstr) + { + return StrToChar(U16toString(wstr)); + } + +} \ No newline at end of file diff --git a/source/parser/ImageCache.h b/source/parser/ImageCache.h new file mode 100644 index 000000000..9f24c5116 --- /dev/null +++ b/source/parser/ImageCache.h @@ -0,0 +1,79 @@ +//****************************************************************************** +/// +/// @file parser/ImageCache.h +/// +/// Declarations related to the Image Cache. +/// +/// @copyright +/// @parblock +/// +/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8. +/// Copyright 1991-2019 Persistence of Vision Raytracer Pty. Ltd. +/// +/// POV-Ray is free software: you can redistribute it and/or modify +/// it under the terms of the GNU Affero General Public License as +/// published by the Free Software Foundation, either version 3 of the +/// License, or (at your option) any later version. +/// +/// POV-Ray is distributed in the hope that it will be useful, +/// but WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/// GNU Affero General Public License for more details. +/// +/// You should have received a copy of the GNU Affero General Public License +/// along with this program. If not, see . +/// +/// ---------------------------------------------------------------------------- +/// +/// POV-Ray is based on the popular DKB raytracer version 2.12. +/// DKBTrace was originally written by David K. Buck. +/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. +/// +/// @endparblock +/// +//****************************************************************************** + +#ifndef POVRAY_PARSER_IMAGE_CACHE_H +#define POVRAY_PARSER_IMAGE_CACHE_H + +#include +#include + +#ifndef WIN32 +#include // Unix lib for getting last modified file date and time +#endif +#ifdef WIN32 +#define stat _stat // Windows lib for getting last modified file date and time +#endif + + +// POV-Ray header files (base module) +#include "base/base_fwd.h" +#include "base/messenger_fwd.h" +#include "base/povassert.h" +#include "base/stringtypes.h" +#include "base/textstream_fwd.h" +#include "base/textstreambuffer.h" +#include "base/image/image_fwd.h" + +namespace pov +{ + class Blob_Element; + struct ContainedByShape; + struct GenericSpline; + class ImageData; + class Mesh; + struct PavementPattern; + struct TilingPattern; + struct TrueTypeFont; +} + +namespace pov_image_cache +{ + using namespace pov_base; + + Image* GetCachedImage(const UCS2* filename); + void StoreImageInCache(const UCS2* filename, Image* image); +}; + +#endif // POVRAY_PARSER_IMAGE_CACHE_H \ No newline at end of file diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp index 2473903c4..60031b6d3 100644 --- a/source/parser/parser.cpp +++ b/source/parser/parser.cpp @@ -113,7 +113,7 @@ #include "vm/fnpovfpu.h" // POV-Ray header files (parser module) -// (none at the moment) +#include "parser/ImageCache.h" // this must be the last file included #include "base/povdebug.h" @@ -122,6 +122,7 @@ namespace pov_parser { using namespace pov; +using namespace pov_image_cache; using std::min; using std::max; @@ -9971,8 +9972,12 @@ OStream *Parser::CreateFile(const UCS2String& filename, unsigned int stype, bool //****************************************************************************** -Image *Parser::Read_Image(int filetype, const UCS2 *filename, const ImageReadOptions& options) +Image *Parser::Read_Image(int filetype, const UCS2* filename, const ImageReadOptions& options) { + Image* img = pov_image_cache::GetCachedImage(filename); + if (img != nullptr) + return img; + unsigned int stype; Image::ImageFileType type; UCS2String ign; @@ -10040,7 +10045,11 @@ Image *Parser::Read_Image(int filetype, const UCS2 *filename, const ImageReadOpt if (file == nullptr) throw POV_EXCEPTION(kCannotOpenFileErr, "Cannot find image file."); - return Image::Read(type, file.get(), options); + img = Image::Read(type, file.get(), options); + + pov_image_cache::StoreImageInCache(filename, img); + + return img; } //****************************************************************************** diff --git a/windows/vs2015/povparser.vcxproj b/windows/vs2015/povparser.vcxproj index e90289687..bb5dcfdbd 100644 --- a/windows/vs2015/povparser.vcxproj +++ b/windows/vs2015/povparser.vcxproj @@ -400,6 +400,7 @@ + @@ -424,6 +425,7 @@ + diff --git a/windows/vs2015/povparser.vcxproj.filters b/windows/vs2015/povparser.vcxproj.filters index d050cbe31..1efdb1c0d 100644 --- a/windows/vs2015/povparser.vcxproj.filters +++ b/windows/vs2015/povparser.vcxproj.filters @@ -46,6 +46,9 @@ Parser Headers + + Parser Headers + @@ -93,5 +96,8 @@ Parser Source + + Parser Source + \ No newline at end of file From 783ff84c3a6509e6026c8b058a90f658dd5355d3 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sat, 22 Feb 2020 22:41:33 +0100 Subject: [PATCH 2/8] Remove #include --- source/parser/ImageCache.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index 382e94e34..c86b52f34 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -37,7 +37,6 @@ // C++ variants of C standard header files #include -#include #include #include From 0e4550b72bf0aacff994424bb6ec4aeb2922b914 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sat, 22 Feb 2020 23:10:27 +0100 Subject: [PATCH 3/8] Replaced with --- source/parser/ImageCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index c86b52f34..bc29ab318 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -37,7 +37,7 @@ // C++ variants of C standard header files #include -#include +#include #include #include From b5ef8705f4c37bd7987dbc6622003ff1ce00d967 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sat, 22 Feb 2020 23:36:09 +0100 Subject: [PATCH 4/8] Added #include --- source/parser/ImageCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index bc29ab318..e9d93fa46 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -38,6 +38,7 @@ // C++ variants of C standard header files #include #include +#include #include #include From 3989f81ec285c43a4ae8e0b3efbaa70a42cf1439 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sun, 23 Feb 2020 00:19:59 +0100 Subject: [PATCH 5/8] Use pov_base::UCS2toSysString instead of own function. --- source/parser/ImageCache.cpp | 42 +++++------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index e9d93fa46..d91012ada 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -37,8 +37,6 @@ // C++ variants of C standard header files #include -#include -#include #include #include @@ -59,6 +57,7 @@ #include "base/textstream_fwd.h" #include "base/textstreambuffer.h" #include "base/image/image_fwd.h" +#include "base/stringutilities.h" // this must be the last file included #include "base/povdebug.h" @@ -68,9 +67,7 @@ namespace pov_image_cache { using namespace pov_base; using namespace std; - static inline std::string U16toString(const std::u16string& wstr); - static inline char* StrToChar(const std::string str); - static inline char* U16toChar(const std::u16string& wstr); + //static inline char* StrToChar(const std::string str); struct ImageCacheEntry final { @@ -83,22 +80,20 @@ namespace pov_image_cache // Gets the last modified time from the filesystem __time64_t GetLastModifiedTime(const std::string filename) { - char* cstrFilename = StrToChar(filename); + const char* cstrFilename = filename.c_str(); struct stat result; if (stat(cstrFilename, &result) == 0) { - delete cstrFilename; return result.st_mtime; } - delete cstrFilename; return 0; } // Try to get the image from the cache Image* GetCachedImage(const UCS2* filename) { - std::string lookupFilename = U16toString(filename); + std::string lookupFilename = pov_base::UCS2toSysString(filename); std::map::iterator idx = Cache.find(lookupFilename); if (idx != Cache.end()) @@ -118,7 +113,7 @@ namespace pov_image_cache // Store a new image into cache void StoreImageInCache(const UCS2* filename, Image* image) { - std::string lookupFilename = U16toString(filename); + std::string lookupFilename = pov_base::UCS2toSysString(filename); __time64_t lastModified = GetLastModifiedTime(lookupFilename); Cache[lookupFilename] = ImageCacheEntry{ image = image, lastModified = lastModified }; } @@ -136,31 +131,4 @@ namespace pov_image_cache Cache.erase(it); } } - - static inline std::string U16toString(const std::u16string& wstr) - { - std::string str = ""; - char cstr[3] = "\0"; - mbstate_t mbs; - for (const auto& it : wstr) { - memset(&mbs, 0, sizeof(mbs));//set shift state to the initial state - memmove(cstr, "\0\0\0", 3); - c16rtomb(cstr, it, &mbs); - str.append(std::string(cstr)); - }//for - return str; - } - - static inline char* StrToChar(const std::string str) - { - char* cstring = new char[str.length() + 1]; - strcpy(cstring, str.c_str()); - return cstring; - } - - static inline char* U16toChar(const std::u16string& wstr) - { - return StrToChar(U16toString(wstr)); - } - } \ No newline at end of file From 63eb73ad666b9e9e6d9a20dbc5c34e4c1de708a0 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sun, 23 Feb 2020 01:27:15 +0100 Subject: [PATCH 6/8] Destroy the Image from ImageUtil (resolve C4150: deletion of pointer to incomplete type) --- source/core/support/imageutil.cpp | 5 +++++ source/core/support/imageutil.h | 1 + source/parser/ImageCache.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/source/core/support/imageutil.cpp b/source/core/support/imageutil.cpp index 7bf3ffcbd..606fc11d2 100644 --- a/source/core/support/imageutil.cpp +++ b/source/core/support/imageutil.cpp @@ -1397,6 +1397,11 @@ void Destroy_Image(ImageData *image) // delete image; } +void Remove_Cached_Image(Image* image) { + delete image; +} + + ImageData::~ImageData() { #ifdef POV_VIDCAP_IMPL diff --git a/source/core/support/imageutil.h b/source/core/support/imageutil.h index b4bf41bc2..c86959443 100644 --- a/source/core/support/imageutil.h +++ b/source/core/support/imageutil.h @@ -143,6 +143,7 @@ int map_pos(const Vector3d& EPoint, const ImageData* pImage, DBL *xcoor, DBL *yc ImageData *Copy_Image(ImageData *old); ImageData *Create_Image(void); void Destroy_Image(ImageData *image); +void Remove_Cached_Image(Image* image); /// @} /// diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index d91012ada..fa6bc126a 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -58,6 +58,7 @@ #include "base/textstreambuffer.h" #include "base/image/image_fwd.h" #include "base/stringutilities.h" +#include "core/support/imageutil.h" // this must be the last file included #include "base/povdebug.h" @@ -67,7 +68,7 @@ namespace pov_image_cache { using namespace pov_base; using namespace std; - //static inline char* StrToChar(const std::string str); + using namespace pov; struct ImageCacheEntry final { @@ -103,7 +104,8 @@ namespace pov_image_cache return idx->second.image; //Cache[lookupFilename].image; // Remove old image from cache and release memory so the newer version can be loaded - delete idx->second.image; + //delete idx->second.image; + pov::Remove_Cached_Image(idx->second.image); Cache.erase(idx); } @@ -127,7 +129,8 @@ namespace pov_image_cache // Iterate over the map using Iterator till end. while (it != Cache.end()) { - delete it->second.image; + //delete it->second.image; + pov::Remove_Cached_Image(it->second.image); Cache.erase(it); } } From 4196674a8ae55c4fc045dd038ca9c6f77a4e7b89 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sun, 23 Feb 2020 01:47:05 +0100 Subject: [PATCH 7/8] Replace __time64_t with long --- source/parser/ImageCache.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/source/parser/ImageCache.cpp b/source/parser/ImageCache.cpp index fa6bc126a..d48919619 100644 --- a/source/parser/ImageCache.cpp +++ b/source/parser/ImageCache.cpp @@ -73,20 +73,20 @@ namespace pov_image_cache struct ImageCacheEntry final { Image* image; - __time64_t lastModified; + long lastModified; }; static std::map Cache; // <- The actual cache // Gets the last modified time from the filesystem - __time64_t GetLastModifiedTime(const std::string filename) + long GetLastModifiedTime(const std::string filename) { const char* cstrFilename = filename.c_str(); struct stat result; if (stat(cstrFilename, &result) == 0) { - return result.st_mtime; + return (long)result.st_mtime; } return 0; } @@ -99,12 +99,11 @@ namespace pov_image_cache std::map::iterator idx = Cache.find(lookupFilename); if (idx != Cache.end()) { - __time64_t lastModified = GetLastModifiedTime(lookupFilename); + long lastModified = GetLastModifiedTime(lookupFilename); if (lastModified == Cache[lookupFilename].lastModified) return idx->second.image; //Cache[lookupFilename].image; // Remove old image from cache and release memory so the newer version can be loaded - //delete idx->second.image; pov::Remove_Cached_Image(idx->second.image); Cache.erase(idx); } @@ -116,7 +115,7 @@ namespace pov_image_cache void StoreImageInCache(const UCS2* filename, Image* image) { std::string lookupFilename = pov_base::UCS2toSysString(filename); - __time64_t lastModified = GetLastModifiedTime(lookupFilename); + long lastModified = GetLastModifiedTime(lookupFilename); Cache[lookupFilename] = ImageCacheEntry{ image = image, lastModified = lastModified }; } @@ -129,7 +128,6 @@ namespace pov_image_cache // Iterate over the map using Iterator till end. while (it != Cache.end()) { - //delete it->second.image; pov::Remove_Cached_Image(it->second.image); Cache.erase(it); } From 415f2c028ba43829ff7a53e6399c5c5a53206584 Mon Sep 17 00:00:00 2001 From: Louis Somers Date: Sun, 23 Feb 2020 21:11:53 +0100 Subject: [PATCH 8/8] Point the image to null to prevent it from being deleted when the wrapper gets deleted. --- source/core/support/imageutil.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/source/core/support/imageutil.cpp b/source/core/support/imageutil.cpp index 606fc11d2..3619cfe8f 100644 --- a/source/core/support/imageutil.cpp +++ b/source/core/support/imageutil.cpp @@ -1387,14 +1387,11 @@ ImageData *Copy_Image(ImageData *Old) void Destroy_Image(ImageData *image) { - return; - - // Images are now cached - - // if ((image == nullptr) || (--(image->References) > 0)) - // return; - // - // delete image; + if ((image == nullptr) || (--(image->References) > 0)) + return; + + image->data = nullptr; // Prevent the image from being deleted. Images are now cached. + delete image; } void Remove_Cached_Image(Image* image) {