From 77177deb610c4b92eb0482053730e961d85950f3 Mon Sep 17 00:00:00 2001 From: Ingo Date: Sat, 22 Nov 2014 01:16:24 +0100 Subject: [PATCH] Bugfix and Enhancement for Clut, Issue 2584, Kudos to Fl?ssie --- rtdata/languages/default | 2 + rtengine/clutstore.cc | 148 ++++++++++++++++++-------------------- rtengine/clutstore.h | 22 +++--- rtengine/simpleprocess.cc | 5 ++ rtgui/filmsimulation.cc | 5 +- rtgui/options.cc | 25 ++++--- rtgui/options.h | 2 +- rtgui/preferences.cc | 38 ++++++++-- rtgui/preferences.h | 1 + 9 files changed, 145 insertions(+), 103 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index c4a5513e9..1cc7655e6 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -739,6 +739,8 @@ PREFERENCES_CIEART;CIECAM02 optimization PREFERENCES_CIEART_LABEL;Use float precision instead of double PREFERENCES_CIEART_TOOLTIP;If enabled, CIECAM02 calculations are performed in the single-precision floating-point format instead of the double-precision one. This provides a small increase in speed at the expense of a negligible loss of quality. PREFERENCES_CLIPPINGIND;Clipping Indication +PREFERENCES_CLUTSCACHE;HaldCLUT cache +PREFERENCES_CLUTSCACHE_LABEL;Max number of cached Cluts PREFERENCES_CLUTSDIR;HaldCLUT directory PREFERENCES_CMETRICINTENT;Colorimetric intent PREFERENCES_CUSTPROFBUILDHINT;Executable (or script) file called when a new initial processing profile should be generated for an image.\n\nThe path of the communication file (*.ini style, a.k.a. "Keyfile") is added as a command line parameter. It contains various parameters required for the scripts and image Exif to allow a rules-based processing profile generation.\n\nWARNING: You are responsible for using double quotes where necessary if you're using paths containing spaces. diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc index 2604b2e2d..8e3670a80 100644 --- a/rtengine/clutstore.cc +++ b/rtengine/clutstore.cc @@ -2,6 +2,7 @@ #include "rt_math.h" #include "stdimagesource.h" #include "safegtk.h" +#include "../rtgui/options.h" rtengine::CLUTStore clutStore; @@ -10,49 +11,67 @@ using namespace rtengine; const float MAXVAL8 = 255.; CLUTStore::CLUTStore() -: m_refCount( 0 ) { } -CLUT* CLUTStore::getClut( Glib::ustring filename ) +CLUT* CLUTStore::getClut( const Glib::ustring& filename ) { - //MyMutex::MyLock lock(m_mutex); // copypasted from iccstore CLUT *result = 0; - if ( !m_lastHaldClut.isValid() || m_lastFilename != filename ) - { - takeUpClut(); - m_lastHaldClut.load( filename ); - } - if ( m_lastHaldClut.isValid() ) - { - result = &m_lastHaldClut; - m_lastFilename = filename; - ++m_refCount; - } - else - { - m_mutex.unlock(); + m_mutex.lock(); + Cluts::iterator cluts_it = m_cluts.find(filename); + if (cluts_it == m_cluts.end()) { + if (m_cluts.size() >= options.clutCacheSize) { + // Evict a "random" entry from cache + Cluts::iterator victim_it = m_cluts.begin(); + if (--victim_it->second.first == -1) { + delete victim_it->second.second; + m_cluts.erase(victim_it); + } + } + cluts_it = m_cluts.insert(std::make_pair(filename, std::make_pair(0, new HaldCLUT))).first; + cluts_it->second.second->load( filename ); } + if (cluts_it->second.second->isValid()) { + result = cluts_it->second.second; + ++cluts_it->second.first; + } else { + delete cluts_it->second.second; + m_cluts.erase(cluts_it); + } + m_mutex.unlock(); + return result; } -void CLUTStore::takeUpClut() +void CLUTStore::releaseClut( const CLUT* clut ) { - m_mutex.lock(); -} - -void CLUTStore::releaseClut( CLUT *clut ) -{ - if ( clut && --m_refCount == 0 ) - { - m_mutex.unlock(); - } + m_mutex.lock(); + for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end(); ++cluts_it) { + if (cluts_it->second.second == clut) { + if (--cluts_it->second.first == -1) { + delete cluts_it->second.second; + m_cluts.erase(cluts_it); + } + break; + } + } + m_mutex.unlock(); } void CLUTStore::clearCache() { - m_lastHaldClut.clear(); - m_lastFilename.clear(); + m_mutex.lock(); + for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end();) { + if (--cluts_it->second.first == -1) { + delete cluts_it->second.second; + Cluts::iterator tmp = cluts_it; + ++cluts_it; + m_cluts.erase(tmp); + } else { + ++cluts_it; + } + } + m_mutex.unlock(); } void rtengine::splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName ) @@ -61,26 +80,21 @@ void rtengine::splitClutFilename( Glib::ustring filename, Glib::ustring &name, G name = filename; //remove dirs size_t lastSlashPos = filename.find_last_of( "/" ); - if ( lastSlashPos == Glib::ustring::npos ) - { + if ( lastSlashPos == Glib::ustring::npos ) { lastSlashPos = filename.find_last_of( "\\" ); } size_t lastDotPos = filename.find_last_of( '.' ); - if ( lastDotPos != Glib::ustring::npos ) - { + if ( lastDotPos != Glib::ustring::npos ) { name = filename.substr( 0, lastDotPos ); extension = filename.substr( lastDotPos + 1, Glib::ustring::npos ); } + profileName = "sRGB"; // sRGB by default static std::vector workingProfiles = rtengine::getWorkingProfiles(); - for ( std::vector::iterator it = workingProfiles.begin(); - it != workingProfiles.end(); - ++it ) - { + for ( std::vector::iterator it = workingProfiles.begin(); it != workingProfiles.end(); ++it ) { Glib::ustring ¤tProfile = *it; - if ( std::search( name.rbegin(), name.rend(), currentProfile.rbegin(), currentProfile.rend() ) == name.rbegin() ) - { + if ( std::search( name.rbegin(), name.rend(), currentProfile.rbegin(), currentProfile.rend() ) == name.rbegin() ) { profileName = currentProfile; name = name.substr( 0, name.size() - currentProfile.size() ); break; @@ -94,35 +108,25 @@ HaldCLUT::HaldCLUT() : m_clutImage( 0 ), m_level (0), m_profile( "sRGB" ) -{} - -HaldCLUT::~HaldCLUT() { } -void HaldCLUT::clear() +HaldCLUT::~HaldCLUT() { - if ( m_clutImage ) - { + if ( m_clutImage ) { m_clutImage->free(); m_clutImage = 0; } - m_filename.clear(); } void HaldCLUT::load( Glib::ustring filename ) { - if ( m_filename != filename ) - { - clear(); - m_clutImage = loadFile( filename, "", m_level ); - Glib::ustring name, ext; - splitClutFilename( filename, name, ext, m_profile ); - if ( m_clutImage ) - { - m_filename = filename; - } - } + m_clutImage = loadFile( filename, "", m_level ); + Glib::ustring name, ext; + splitClutFilename( filename, name, ext, m_profile ); + if ( m_clutImage ) { + m_filename = filename; + } } Glib::ustring HaldCLUT::profile() const @@ -134,8 +138,7 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol { Imagefloat *result = 0; StdImageSource imgSrc; - if ( !safe_file_test( filename, Glib::FILE_TEST_EXISTS ) || imgSrc.load(filename) ) - { + if ( !safe_file_test( filename, Glib::FILE_TEST_EXISTS ) || imgSrc.load(filename) ) { return result; } @@ -144,18 +147,15 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol bool valid = false; //test on Hald format, copypasted from http://www.quelsolaar.com/technology/clut.html - if ( fw == fh ) - { + if ( fw == fh ) { outLevel = 1; for(; outLevel * outLevel * outLevel < fw; outLevel++); - if( !( outLevel * outLevel * outLevel > fw ) ) - { + if( !( outLevel * outLevel * outLevel > fw ) ) { valid = true; } } - if ( valid ) - { + if ( valid ) { ColorTemp currWB = imgSrc.getWB(); Imagefloat* baseImg = new Imagefloat (fw, fh); PreviewProps pp (0, 0, fw, fh, 1); @@ -164,8 +164,7 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol icm.working = workingColorSpace; imgSrc.getImage (currWB, TR_NONE, baseImg, pp, procparams::ToneCurveParams(), icm, procparams::RAWParams()); - if ( !workingColorSpace.empty() ) - { + if ( !workingColorSpace.empty() ) { imgSrc.convertColorSpace(baseImg, icm, currWB, procparams::RAWParams()); } result = baseImg; @@ -181,10 +180,8 @@ void HaldCLUT::loadClut( Imagefloat *img, RawClut &outClut ) outClut.resize( x_size * y_size * 3 ); int clutIdx = 0; //int level = m_level * m_level; (unused) - for(int y = 0; y < y_size; y++) - { - for(int x = 0; x < x_size; x++) - { + for(int y = 0; y < y_size; y++) { + for(int x = 0; x < x_size; x++) { outClut[ clutIdx * 3 ] = img->r( y, x ) * MAXVAL8; outClut[ clutIdx * 3 + 1 ] = img->g( y, x ) * MAXVAL8; outClut[ clutIdx * 3 + 2 ] = img->b( y, x ) * MAXVAL8; @@ -202,12 +199,9 @@ Imagefloat* HaldCLUT::generateIdentImage( int level ) int cubeSideSize = level * level; float step = MAXVALF / (cubeSideSize - 1); int pos = 0; - for( int b = 0; b < cubeSideSize; ++b ) - { - for ( int g = 0; g < cubeSideSize; ++g ) - { - for ( int r = 0; r < cubeSideSize; ++r ) - { + for( int b = 0; b < cubeSideSize; ++b ) { + for ( int g = 0; g < cubeSideSize; ++g ) { + for ( int r = 0; r < cubeSideSize; ++r ) { int x = pos / imageWidth; int y = pos % imageWidth; resultImg->r( x, y ) = step * r; diff --git a/rtengine/clutstore.h b/rtengine/clutstore.h index ff62df2cb..2ef9de490 100644 --- a/rtengine/clutstore.h +++ b/rtengine/clutstore.h @@ -5,6 +5,7 @@ #include "../rtgui/threadutils.h" #include "imagefloat.h" #include +#include namespace rtengine { @@ -25,7 +26,6 @@ public: ~HaldCLUT(); void load( Glib::ustring filename ); bool isValid() const; - void clear(); void getRGB( float r, float g, float b, float &outR, float &outG, float &outB ) const; Glib::ustring profile() const; @@ -51,17 +51,17 @@ class CLUTStore { public: CLUTStore(); - CLUT* getClut( Glib::ustring filename ); - void takeUpClut(); - void releaseClut( CLUT *clut ); - void clearCache(); + + CLUT* getClut( const Glib::ustring& filename ); + void releaseClut( const CLUT* clut ); + + void clearCache(); private: - int m_refCount; + typedef std::map > Cluts; + + Cluts m_cluts; MyMutex m_mutex; - - HaldCLUT m_lastHaldClut; - Glib::ustring m_lastFilename; }; void splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName ); @@ -79,12 +79,12 @@ public: ClutPtr() : m_point( 0 ) {} explicit ClutPtr(CLUT *p) : m_point( p ) {} ~ClutPtr() { clutStore.releaseClut( m_point ); } - CLUT* operator-> () const { return m_point; } + const CLUT* operator-> () const { return m_point; } operator bool() const { return m_point != 0; } void set( CLUT *p ) { m_point = p; } private: - ClutPtr& operator=(ClutPtr const& cp ) { /*only for clean warning messages*/return *this; } + ClutPtr& operator=(ClutPtr const& cp ); CLUT *m_point; }; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index d07ef7749..f8e8e7c9b 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -22,6 +22,7 @@ #include "improcfun.h" #include "curves.h" #include "iccstore.h" +#include "clutstore.h" #include "processingjob.h" #include #include "../rtgui/options.h" @@ -678,6 +679,10 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p if (settings->verbose) printf("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", autor, autog, autob); + // if clut was used and size of clut cache == 1 we free the memory used by the clutstore (default clut cache size = 1 for 32 bit OS) + if ( params.filmSimulation.enabled && !params.filmSimulation.clutFilename.empty() && options.clutCacheSize == 1) + clutStore.clearCache(); + // freeing up some memory customToneCurve1.Reset(); customToneCurve2.Reset(); diff --git a/rtgui/filmsimulation.cc b/rtgui/filmsimulation.cc index 9fbedd45c..553432e7f 100644 --- a/rtgui/filmsimulation.cc +++ b/rtgui/filmsimulation.cc @@ -113,8 +113,9 @@ void FilmSimulation::read( const rtengine::procparams::ProcParams* pp, const Par m_clutComboBox->setSelectedClut("NULL"); } if ( !m_enabled->get_inconsistent() && !pp->filmSimulation.enabled ) - { - clutStore.clearCache(); + { + if (options.clutCacheSize == 1) + clutStore.clearCache(); } updateDisable( false ); diff --git a/rtgui/options.cc b/rtgui/options.cc index 5cdfaddb9..400d99ce2 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -25,7 +25,12 @@ #include "addsetids.h" #include "guiutils.h" #include "../rtengine/safegtk.h" -#include "version.h" +#include "version.h" + +#ifdef _OPENMP +#include +#endif + #ifdef WIN32 #include @@ -353,9 +358,13 @@ void Options::setDefaults () { histogramBar = true; histogramFullMode = false; - rgbDenoiseThreadLimit = 0; - - filledProfile = false; + rgbDenoiseThreadLimit = 0; +#if defined( _OPENMP ) && defined( __x86_64__ ) + clutCacheSize = omp_get_num_procs(); +#else + clutCacheSize = 1; +#endif + filledProfile = false; showProfileSelector = true; FileBrowserToolbarSingleRow = false; @@ -726,7 +735,7 @@ if (keyFile.has_group ("Clipping Indication")) { } if (keyFile.has_group ("Performance")) { - if (keyFile.has_key ("Performance", "RgbDenoiseThreadLimit")) rgbDenoiseThreadLimit = keyFile.get_integer ("Performance", "RgbDenoiseThreadLimit"); + if (keyFile.has_key ("Performance", "RgbDenoiseThreadLimit")) rgbDenoiseThreadLimit = keyFile.get_integer ("Performance", "RgbDenoiseThreadLimit"); if( keyFile.has_key ("Performance", "NRauto")) rtSettings.nrauto = keyFile.get_double("Performance", "NRauto"); if( keyFile.has_key ("Performance", "NRautomax")) rtSettings.nrautomax = keyFile.get_double("Performance", "NRautomax"); if( keyFile.has_key ("Performance", "NRhigh")) rtSettings.nrhigh = keyFile.get_double("Performance", "NRhigh"); @@ -736,8 +745,7 @@ if (keyFile.has_group ("Performance")) { if (keyFile.has_key ("Performance", "LevNRAUT")) rtSettings.leveldnaut = keyFile.get_integer("Performance", "LevNRAUT"); if (keyFile.has_key ("Performance", "LevNRLISS")) rtSettings.leveldnliss = keyFile.get_integer("Performance", "LevNRLISS"); if (keyFile.has_key ("Performance", "SIMPLNRAUT")) rtSettings.leveldnautsimpl = keyFile.get_integer("Performance", "SIMPLNRAUT"); - - + if (keyFile.has_key ("Performance", "ClutCacheSize")) clutCacheSize = keyFile.get_integer ("Performance", "ClutCacheSize"); } if (keyFile.has_group ("GUI")) { @@ -1009,7 +1017,7 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_integer ("Clipping Indication", "ShadowThreshold", shadowThreshold); keyFile.set_boolean ("Clipping Indication", "BlinkClipped", blinkClipped); - keyFile.set_integer ("Performance", "RgbDenoiseThreadLimit", rgbDenoiseThreadLimit); + keyFile.set_integer ("Performance", "RgbDenoiseThreadLimit", rgbDenoiseThreadLimit); keyFile.set_double ("Performance", "NRauto", rtSettings.nrauto); keyFile.set_double ("Performance", "NRautomax", rtSettings.nrautomax); keyFile.set_double ("Performance", "NRhigh", rtSettings.nrhigh); @@ -1019,6 +1027,7 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_integer ("Performance", "LevNRAUT", rtSettings.leveldnaut); keyFile.set_integer ("Performance", "LevNRLISS", rtSettings.leveldnliss); keyFile.set_integer ("Performance", "SIMPLNRAUT", rtSettings.leveldnautsimpl); + keyFile.set_integer ("Performance", "ClutCacheSize", clutCacheSize); keyFile.set_string ("Output", "Format", saveFormat.format); keyFile.set_integer ("Output", "JpegQuality", saveFormat.jpegQuality); diff --git a/rtgui/options.h b/rtgui/options.h index e31e103d9..79d30ded6 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -219,7 +219,7 @@ class Options { // Performance options int rgbDenoiseThreadLimit; // maximum number of threads for the denoising tool ; 0 = use the maximum available - + int clutCacheSize; bool filledProfile; // Used as reminder for the ProfilePanel "mode" bool menuGroupRank; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 5d14b45dc..9d87ddb31 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -27,6 +27,9 @@ #include #include "../rtengine/safegtk.h" #include "rtimage.h" +#ifdef _OPENMP +#include +#endif extern Options options; extern Glib::ustring argv0; @@ -593,7 +596,33 @@ Gtk::Widget* Preferences::getPerformancePanel () { mainContainer->pack_start(*threadLimitHB, Gtk::PACK_SHRINK, 4); fdenoise->add (*mainContainer); - mvbsd->pack_start (*fdenoise, Gtk::PACK_SHRINK, 4); + mvbsd->pack_start (*fdenoise, Gtk::PACK_SHRINK, 4); + + + Gtk::Frame* fclut = Gtk::manage( new Gtk::Frame (M("PREFERENCES_CLUTSCACHE")) ); + + Gtk::HBox* clutCacheSizeHB = Gtk::manage( new Gtk::HBox () ); + clutCacheSizeHB->set_border_width(4); + clutCacheSizeHB->set_spacing(4); +// clutCacheSizeHB->set_tooltip_text(M("PREFERENCES_CLUTCACHESIZE_TOOLTIP")); + Gtk::Label* CLUTLl = Gtk::manage( new Gtk::Label (M("PREFERENCES_CLUTSCACHE_LABEL") + ":", Gtk::ALIGN_LEFT)); + clutCacheSizeSB = Gtk::manage( new Gtk::SpinButton () ); + clutCacheSizeSB->set_digits (0); + clutCacheSizeSB->set_increments (1, 5); + clutCacheSizeSB->set_max_length(2); // Will this be sufficient? :) + +#ifdef _OPENMP + clutCacheSizeSB->set_range (1, 2*omp_get_num_procs()); +#else + clutCacheSizeSB->set_range (1, 8); +#endif + + clutCacheSizeHB->pack_start (*CLUTLl, Gtk::PACK_SHRINK, 0); + clutCacheSizeHB->pack_end (*clutCacheSizeSB, Gtk::PACK_SHRINK, 0); + fclut->add (*clutCacheSizeHB); + + + mvbsd->pack_start (*fclut, Gtk::PACK_SHRINK, 4); // return mainContainer; return mvbsd; @@ -1376,7 +1405,8 @@ void Preferences::storePreferences () { moptions.overwriteOutputFile = chOverwriteOutputFile->get_active (); moptions.UseIconNoText = ckbUseIconNoText->get_active(); - moptions.rgbDenoiseThreadLimit = rgbDenoiseTreadLimitSB->get_value_as_int(); + moptions.rgbDenoiseThreadLimit = rgbDenoiseTreadLimitSB->get_value_as_int(); + moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int(); // Sounds only on Windows and Linux #if defined(WIN32) || defined(__linux__) @@ -1522,7 +1552,7 @@ void Preferences::fillPreferences () { ckbUseIconNoText->set_active(moptions.UseIconNoText); rgbDenoiseTreadLimitSB->set_value(moptions.rgbDenoiseThreadLimit); - + clutCacheSizeSB->set_value(moptions.clutCacheSize); //darkFrameDir->set_filename( moptions.rtSettings.darkFramesPath ); //updateDFinfos(); darkFrameDir->set_current_folder( moptions.rtSettings.darkFramesPath ); @@ -1534,7 +1564,7 @@ void Preferences::fillPreferences () { flatFieldChanged (); clutsDir->set_current_folder( moptions.clutsDir ); - + addc.block (true); setc.block (true); if (moptions.baBehav.size() == ADDSET_PARAM_NUM) { diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 21bb42584..4c0f58bd0 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -124,6 +124,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener { Gtk::CheckButton* sameThumbSize; Gtk::SpinButton* rgbDenoiseTreadLimitSB; + Gtk::SpinButton* clutCacheSizeSB; Gtk::CheckButton* ckbmenuGroupRank; Gtk::CheckButton* ckbmenuGroupLabel;