ocl: binary program cache
authorAlexander Alekhin <alexander.alekhin@intel.com>
Thu, 12 Oct 2017 11:23:45 +0000 (14:23 +0300)
committerAlexander Alekhin <alexander.alekhin@intel.com>
Wed, 22 Nov 2017 09:56:38 +0000 (12:56 +0300)
modules/core/include/opencv2/core/ocl.hpp
modules/core/include/opencv2/core/utils/filesystem.hpp [new file with mode: 0644]
modules/core/include/opencv2/core/utils/filesystem.private.hpp [new file with mode: 0644]
modules/core/include/opencv2/core/utils/lock.private.hpp [new file with mode: 0644]
modules/core/src/glob.cpp
modules/core/src/ocl.cpp
modules/core/src/utils/filesystem.cpp [new file with mode: 0644]

index 5f13e04..540e2f8 100644 (file)
@@ -261,6 +261,8 @@ public:
     void setUseSVM(bool enabled);
 
     struct Impl;
+    inline Impl* getImpl() const { return (Impl*)p; }
+//protected:
     Impl* p;
 };
 
diff --git a/modules/core/include/opencv2/core/utils/filesystem.hpp b/modules/core/include/opencv2/core/utils/filesystem.hpp
new file mode 100644 (file)
index 0000000..e8ffa4b
--- /dev/null
@@ -0,0 +1,38 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_UTILS_FILESYSTEM_HPP
+#define OPENCV_UTILS_FILESYSTEM_HPP
+
+namespace cv { namespace utils { namespace fs {
+
+
+CV_EXPORTS bool exists(const cv::String& path);
+CV_EXPORTS bool isDirectory(const cv::String& path);
+
+
+CV_EXPORTS cv::String getcwd();
+
+
+CV_EXPORTS bool createDirectory(const cv::String& path);
+CV_EXPORTS bool createDirectories(const cv::String& path);
+
+#ifdef __OPENCV_BUILD
+// TODO
+//CV_EXPORTS cv::String getTempDirectory();
+
+/**
+ * @brief Returns directory to store OpenCV cache files
+ * Create sub-directory in common OpenCV cache directory if it doesn't exist.
+ * @param sub_directory_name name of sub-directory. NULL or "" value asks to return root cache directory.
+ * @param configuration_name optional name of configuration parameter name which overrides default behavior.
+ * @return Path to cache directory. Returns empty string if cache directories support is not available. Returns "disabled" if cache disabled by user.
+ */
+CV_EXPORTS cv::String getCacheDirectory(const char* sub_directory_name, const char* configuration_name = NULL);
+
+#endif
+
+}}} // namespace
+
+#endif // OPENCV_UTILS_FILESYSTEM_HPP
diff --git a/modules/core/include/opencv2/core/utils/filesystem.private.hpp b/modules/core/include/opencv2/core/utils/filesystem.private.hpp
new file mode 100644 (file)
index 0000000..28b1747
--- /dev/null
@@ -0,0 +1,64 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_UTILS_FILESYSTEM_PRIVATE_HPP
+#define OPENCV_UTILS_FILESYSTEM_PRIVATE_HPP
+
+// TODO Move to CMake?
+#ifndef OPENCV_HAVE_FILESYSTEM_SUPPORT
+#  if defined(__EMSCRIPTEN__) || defined(__native_client__)
+     /* no support */
+#  elif defined __ANDROID__ || defined __linux__ || defined _WIN32 || \
+        defined __FreeBSD__ || defined __bsdi__
+#      define OPENCV_HAVE_FILESYSTEM_SUPPORT 1
+#  elif defined(__APPLE__)
+#    include <TargetConditionals.h>
+#    if (defined(TARGET_OS_OSX) && TARGET_OS_OSX) || (!defined(TARGET_OS_OSX) && !TARGET_OS_IPHONE)
+#      define OPENCV_HAVE_FILESYSTEM_SUPPORT 1 // OSX only
+#    endif
+#  else
+     /* unknown */
+#  endif
+#  ifndef OPENCV_HAVE_FILESYSTEM_SUPPORT
+#    define OPENCV_HAVE_FILESYSTEM_SUPPORT 0
+#  endif
+#endif
+
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT
+namespace cv { namespace utils { namespace fs {
+
+/**
+ * File-based lock object.
+ *
+ * Provides interprocess synchronization mechanism.
+ * Platform dependent.
+ *
+ * Supports multiple readers / single writer access pattern (RW / readers–writer / shared-exclusive lock).
+ *
+ * File must exist.
+ * File can't be re-used (for example, I/O operations via std::fstream is not safe)
+ */
+class CV_EXPORTS FileLock {
+public:
+    explicit FileLock(const char* fname);
+    ~FileLock();
+
+    void lock(); //< acquire exclusive (writer) lock
+    void unlock(); //< release exclusive (writer) lock
+
+    void lock_shared(); //< acquire sharable (reader) lock
+    void unlock_shared(); //< release sharable (reader) lock
+
+    struct Impl;
+protected:
+    Impl* pImpl;
+
+private:
+    FileLock(const FileLock&); // disabled
+    FileLock& operator=(const FileLock&); // disabled
+};
+
+}}} // namespace
+#endif
+#endif // OPENCV_UTILS_FILESYSTEM_PRIVATE_HPP
diff --git a/modules/core/include/opencv2/core/utils/lock.private.hpp b/modules/core/include/opencv2/core/utils/lock.private.hpp
new file mode 100644 (file)
index 0000000..7148a39
--- /dev/null
@@ -0,0 +1,119 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_UTILS_LOCK_HPP
+#define OPENCV_UTILS_LOCK_HPP
+
+namespace cv { namespace utils {
+
+
+/** @brief A simple scoped lock (RAII-style locking for exclusive/write access).
+ *
+ * Emulate std::lock_guard (C++11), partially std::unique_lock (C++11),
+ */
+template <class _Mutex>
+class lock_guard {
+public:
+    typedef _Mutex Mutex;
+
+    explicit inline lock_guard(Mutex &m) : mutex_(&m) { mutex_->lock(); }
+
+    inline ~lock_guard() { if (mutex_) mutex_->unlock(); }
+
+    inline void release()
+    {
+        CV_DbgAssert(mutex_);
+        mutex_->unlock();
+        mutex_ = NULL;
+    }
+
+private:
+    Mutex* mutex_;
+
+private:
+    lock_guard(const lock_guard&); // disabled
+    lock_guard& operator=(const lock_guard&); // disabled
+};
+
+
+/** @brief A shared scoped lock (RAII-style locking for shared/reader access).
+ *
+ * Emulate boost::shared_lock_guard, subset of std::shared_lock (C++14),
+ */
+template <class _Mutex>
+class shared_lock_guard {
+public:
+    typedef _Mutex Mutex;
+
+    explicit inline shared_lock_guard(Mutex &m) : mutex_(&m) { mutex_->lock_shared(); }
+
+    inline ~shared_lock_guard() { if (mutex_) mutex_->unlock_shared(); }
+
+    inline void release()
+    {
+        CV_DbgAssert(mutex_);
+        mutex_->unlock_shared();
+        mutex_ = NULL;
+    }
+
+protected:
+    Mutex* mutex_;
+
+private:
+    shared_lock_guard(const shared_lock_guard&); // disabled
+    shared_lock_guard& operator=(const shared_lock_guard&); // disabled
+};
+
+
+/** @brief An optional simple scoped lock (RAII-style locking for exclusive/write access).
+ *
+ * Doesn't lock if mutex pointer is NULL.
+ *
+ * @sa lock_guard
+ */
+template <class _Mutex>
+class optional_lock_guard {
+public:
+    typedef _Mutex Mutex;
+
+    explicit inline optional_lock_guard(Mutex* m) : mutex_(m) { if (mutex_) mutex_->lock(); }
+
+    inline ~optional_lock_guard() { if (mutex_) mutex_->unlock(); }
+
+private:
+    Mutex* mutex_;
+
+private:
+    optional_lock_guard(const optional_lock_guard&); // disabled
+    optional_lock_guard& operator=(const optional_lock_guard&); // disabled
+};
+
+
+/** @brief An optional shared scoped lock (RAII-style locking for shared/reader access).
+ *
+ * Doesn't lock if mutex pointer is NULL.
+ *
+ * @sa shared_lock_guard
+ */
+template <class _Mutex>
+class optional_shared_lock_guard {
+public:
+    typedef _Mutex Mutex;
+
+    explicit inline optional_shared_lock_guard(Mutex* m) : mutex_(m) { if (mutex_) mutex_->lock_shared(); }
+
+    inline ~optional_shared_lock_guard() { if (mutex_) mutex_->unlock_shared(); }
+
+protected:
+    Mutex* mutex_;
+
+private:
+    optional_shared_lock_guard(const optional_shared_lock_guard&); // disabled
+    optional_shared_lock_guard& operator=(const optional_shared_lock_guard&); // disabled
+};
+
+
+}} // namespace
+
+#endif // OPENCV_UTILS_LOCK_HPP
index 60a220d..844e646 100644 (file)
@@ -42,6 +42,8 @@
 
 #include "precomp.hpp"
 
+#include "opencv2/core/utils/filesystem.hpp"
+
 #if defined _WIN32 || defined WINCE
 # include <windows.h>
 const char dir_separators[] = "/\\";
@@ -169,6 +171,12 @@ static bool isDir(const cv::String& path, DIR* dir)
 #endif
 }
 
+bool cv::utils::fs::isDirectory(const cv::String& path)
+{
+    CV_INSTRUMENT_REGION()
+    return isDir(path, NULL);
+}
+
 static bool wildcmp(const char *string, const char *wild)
 {
     // Based on wildcmp written by Jack Handy - <A href="mailto:jakkhandy@hotmail.com">jakkhandy@hotmail.com</A>
index 5d7981a..3d596ef 100644 (file)
 #include <string>
 #include <sstream>
 #include <iostream> // std::cerr
+#include <fstream>
 #if !(defined _MSC_VER) || (defined _MSC_VER && _MSC_VER > 1700)
 #include <inttypes.h>
 #endif
 
 #include <opencv2/core/utils/configuration.private.hpp>
 
+#include <opencv2/core/utils/logger.hpp>
+
 #include "opencv2/core/ocl_genbase.hpp"
 #include "opencl_kernels_core.hpp"
 
-#define CV_OPENCL_ALWAYS_SHOW_BUILD_LOG 0
+#include "opencv2/core/utils/lock.private.hpp"
+#include "opencv2/core/utils/filesystem.hpp"
+#include "opencv2/core/utils/filesystem.private.hpp"
+
+#define CV_OPENCL_ALWAYS_SHOW_BUILD_LOG          0
+
+#define CV_OPENCL_SHOW_RUN_KERNELS               0
+#define CV_OPENCL_TRACE_CHECK                    0
 
-#define CV_OPENCL_SHOW_RUN_KERNELS      0
-#define CV_OPENCL_TRACE_CHECK           0
+#define CV_OPENCL_VALIDATE_BINARY_PROGRAMS       1
 
-#define CV_OPENCL_SHOW_SVM_ERROR_LOG    1
-#define CV_OPENCL_SHOW_SVM_LOG          0
+#define CV_OPENCL_SHOW_SVM_ERROR_LOG             1
+#define CV_OPENCL_SHOW_SVM_LOG                   0
 
 #include "opencv2/core/bufferpool.hpp"
 #ifndef LOG_BUFFER_POOL
@@ -167,6 +176,16 @@ void traceOpenCLCheck(cl_int status, const char* message)
 #define CV_OCL_DBG_CHECK(expr) do { cl_int __cl_result = (expr); CV_OCL_DBG_CHECK_RESULT(__cl_result, #expr); } while (0)
 #endif
 
+
+static const bool CV_OPENCL_CACHE_ENABLE = utils::getConfigurationParameterBool("OPENCV_OPENCL_CACHE_ENABLE", true);
+static const bool CV_OPENCL_CACHE_WRITE = utils::getConfigurationParameterBool("OPENCV_OPENCL_CACHE_WRITE", true);
+static const bool CV_OPENCL_CACHE_LOCK_ENABLE = utils::getConfigurationParameterBool("OPENCV_OPENCL_CACHE_LOCK_ENABLE", true);
+
+#if CV_OPENCL_VALIDATE_BINARY_PROGRAMS
+static const bool CV_OPENCL_VALIDATE_BINARY_PROGRAMS_VALUE = utils::getConfigurationParameterBool("OPENCV_OPENCL_VALIDATE_BINARY_PROGRAMS", false);
+#endif
+
+
 struct UMat2D
 {
     UMat2D(const UMat& m)
@@ -226,6 +245,486 @@ static uint64 crc64( const uchar* data, size_t size, uint64 crc0=0 )
     return ~crc;
 }
 
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT
+struct OpenCLBinaryCacheConfigurator
+{
+    cv::String cache_path_;
+    cv::String cache_lock_filename_;
+    cv::Ptr<utils::fs::FileLock> cache_lock_;
+
+    typedef std::map<std::string, std::string> ContextCacheType;
+    ContextCacheType prepared_contexts_;
+
+    OpenCLBinaryCacheConfigurator()
+    {
+        CV_LOG_DEBUG(NULL, "Initializing OpenCL cache configuration...");
+        if (!CV_OPENCL_CACHE_ENABLE)
+        {
+            CV_LOG_INFO(NULL, "OpenCL cache is disabled");
+            return;
+        }
+        cache_path_ = utils::fs::getCacheDirectory("opencl_cache", "OPENCV_OPENCL_CACHE_DIR");
+        if (cache_path_.empty())
+        {
+            CV_LOG_INFO(NULL, "Specify OPENCV_OPENCL_CACHE_DIR configuration parameter to enable OpenCL cache");
+        }
+        do
+        {
+            try
+            {
+                if (cache_path_.empty())
+                    break;
+                if (cache_path_ == "disabled")
+                    break;
+                if (!utils::fs::createDirectories(cache_path_))
+                {
+                    CV_LOG_DEBUG(NULL, "Can't use OpenCL cache directory: " << cache_path_);
+                    clear();
+                    break;
+                }
+
+                if (CV_OPENCL_CACHE_LOCK_ENABLE)
+                {
+                    cache_lock_filename_ = cache_path_ + ".lock";
+                    if (!utils::fs::exists(cache_lock_filename_))
+                    {
+                        CV_LOG_DEBUG(NULL, "Creating lock file... (" << cache_lock_filename_ << ")");
+                        std::ofstream lock_filename(cache_lock_filename_.c_str(), std::ios::out);
+                        if (!lock_filename.is_open())
+                        {
+                            CV_LOG_WARNING(NULL, "Can't create lock file for OpenCL program cache: " << cache_lock_filename_);
+                            break;
+                        }
+                    }
+
+                    try
+                    {
+                        cache_lock_ = makePtr<utils::fs::FileLock>(cache_lock_filename_.c_str());
+                        CV_LOG_VERBOSE(NULL, 0, "Checking cache lock... (" << cache_lock_filename_ << ")");
+                        {
+                            utils::shared_lock_guard<utils::fs::FileLock> lock(*cache_lock_);
+                        }
+                        CV_LOG_VERBOSE(NULL, 0, "Checking cache lock... Done!");
+                    }
+                    catch (const cv::Exception& e)
+                    {
+                        CV_LOG_WARNING(NULL, "Can't create OpenCL program cache lock: " << cache_lock_filename_ << std::endl << e.what());
+                    }
+                    catch (...)
+                    {
+                        CV_LOG_WARNING(NULL, "Can't create OpenCL program cache lock: " << cache_lock_filename_);
+                    }
+                }
+                else
+                {
+                    if (CV_OPENCL_CACHE_WRITE)
+                    {
+                        CV_LOG_WARNING(NULL, "OpenCL cache lock is disabled while cache write is allowed "
+                                "(not safe for multiprocess environment)");
+                    }
+                    else
+                    {
+                        CV_LOG_INFO(NULL, "OpenCL cache lock is disabled");
+                    }
+                }
+            }
+            catch (const cv::Exception& e)
+            {
+                CV_LOG_WARNING(NULL, "Can't prepare OpenCL program cache: " << cache_path_ << std::endl << e.what());
+                clear();
+            }
+        } while (0);
+        if (!cache_path_.empty())
+        {
+            if (cache_lock_.empty() && CV_OPENCL_CACHE_LOCK_ENABLE)
+            {
+                CV_LOG_WARNING(NULL, "Initialized OpenCL cache directory, but interprocess synchronization lock is not available. "
+                        "Consider to disable OpenCL cache: OPENCV_OPENCL_CACHE_DIR=disabled");
+            }
+            else
+            {
+                CV_LOG_INFO(NULL, "Successfully initialized OpenCL cache directory: " << cache_path_);
+            }
+        }
+    }
+
+    void clear()
+    {
+        cache_path_.clear();
+        cache_lock_filename_.clear();
+        cache_lock_.release();
+    }
+
+    std::string prepareCacheDirectoryForContext(const std::string& ctx_prefix)
+    {
+        if (cache_path_.empty())
+            return std::string();
+
+        ContextCacheType::iterator i = prepared_contexts_.find(ctx_prefix);
+        if (i != prepared_contexts_.end())
+            return i->second;
+
+        CV_LOG_INFO(NULL, "Preparing OpenCL cache configuration for context: " << ctx_prefix);
+
+        std::string target_directory = cache_path_ + ctx_prefix + "/";
+        bool result = utils::fs::isDirectory(target_directory);
+        if (!result)
+        {
+            try
+            {
+                CV_LOG_VERBOSE(NULL, 0, "Creating directory: " << target_directory);
+                if (utils::fs::createDirectories(target_directory))
+                {
+                    result = true;
+                }
+                else
+                {
+                    CV_LOG_WARNING(NULL, "Can't create directory: " << target_directory);
+                }
+            }
+            catch (const cv::Exception& e)
+            {
+                CV_LOG_ERROR(NULL, "Can't create OpenCL program cache directory for context: " << target_directory << std::endl << e.what());
+            }
+        }
+        target_directory = result ? target_directory : std::string();
+        prepared_contexts_.insert(std::pair<std::string, std::string>(ctx_prefix, target_directory));
+
+        CV_LOG_VERBOSE(NULL, 1, "  Result: " << (target_directory.empty() ? std::string("Failed") : target_directory));
+
+        return target_directory;
+    }
+
+    static OpenCLBinaryCacheConfigurator& getSingletonInstance()
+    {
+        CV_SINGLETON_LAZY_INIT_REF(OpenCLBinaryCacheConfigurator, new OpenCLBinaryCacheConfigurator());
+    }
+};
+class BinaryProgramFile
+{
+    enum { MAX_ENTRIES = 64 };
+
+    typedef unsigned int uint32_t;
+
+    struct CV_DECL_ALIGNED(4) FileHeader
+    {
+        uint32_t sourceSignatureSize;
+        //char sourceSignature[];
+    };
+
+    struct CV_DECL_ALIGNED(4) FileTable
+    {
+        uint32_t numberOfEntries;
+        //uint32_t firstEntryOffset[];
+    };
+
+    struct CV_DECL_ALIGNED(4) FileEntry
+    {
+        uint32_t nextEntryFileOffset; // 0 for the last entry in chain
+        uint32_t keySize;
+        uint32_t dataSize;
+        //char key[];
+        //char data[];
+    };
+
+    const std::string fileName_;
+    const char* const sourceSignature_;
+    const size_t sourceSignatureSize_;
+
+    std::fstream f;
+
+    uint32_t entryOffsets[MAX_ENTRIES];
+
+    uint32_t getHash(const std::string& options)
+    {
+        uint64 hash = crc64((const uchar*)options.c_str(), options.size(), 0);
+        return hash & (MAX_ENTRIES - 1);
+    }
+
+    inline size_t getFileSize()
+    {
+        size_t pos = (size_t)f.tellg();
+        f.seekg(0, std::fstream::end);
+        size_t fileSize = (size_t)f.tellg();
+        f.seekg(pos, std::fstream::beg);
+        return fileSize;
+    }
+    inline uint32_t readUInt32()
+    {
+        uint32_t res = 0;
+        f.read((char*)&res, sizeof(uint32_t));
+        CV_Assert(!f.fail());
+        return res;
+    }
+    inline void writeUInt32(const uint32_t value)
+    {
+        uint32_t v = value;
+        f.write((char*)&v, sizeof(uint32_t));
+        CV_Assert(!f.fail());
+    }
+
+    inline void seekReadAbsolute(size_t pos)
+    {
+        f.seekg(pos, std::fstream::beg);
+        CV_Assert(!f.fail());
+    }
+    inline void seekReadRelative(size_t pos)
+    {
+        f.seekg(pos, std::fstream::cur);
+        CV_Assert(!f.fail());
+    }
+
+    inline void seekWriteAbsolute(size_t pos)
+    {
+        f.seekp(pos, std::fstream::beg);
+        CV_Assert(!f.fail());
+    }
+
+    void clearFile()
+    {
+        f.close();
+        if (0 != remove(fileName_.c_str()))
+            CV_LOG_ERROR(NULL, "Can't remove: " << fileName_);
+        return;
+    }
+
+public:
+    BinaryProgramFile(const std::string& fileName, const char* sourceSignature)
+        : fileName_(fileName), sourceSignature_(sourceSignature), sourceSignatureSize_(sourceSignature_ ? strlen(sourceSignature_) : 0)
+    {
+        CV_StaticAssert(sizeof(uint32_t) == 4, "");
+        CV_Assert(sourceSignature_ != NULL);
+        CV_Assert(sourceSignatureSize_ > 0);
+        memset(entryOffsets, 0, sizeof(entryOffsets));
+
+        f.rdbuf()->pubsetbuf(0, 0); // disable buffering
+        f.open(fileName_.c_str(), std::ios::in|std::ios::out|std::ios::binary);
+        if(f.is_open() && getFileSize() > 0)
+        {
+            bool isValid = false;
+            try
+            {
+                uint32_t fileSourceSignatureSize = readUInt32();
+                if (fileSourceSignatureSize == sourceSignatureSize_)
+                {
+                    cv::AutoBuffer<char> fileSourceSignature(fileSourceSignatureSize + 1);
+                    f.read((char*)fileSourceSignature, fileSourceSignatureSize);
+                    if (f.eof())
+                    {
+                        CV_LOG_ERROR(NULL, "Unexpected EOF");
+                    }
+                    else if (memcmp(sourceSignature, (const char*)fileSourceSignature, fileSourceSignatureSize) == 0)
+                    {
+                        isValid = true;
+                    }
+                }
+                if (!isValid)
+                {
+                    CV_LOG_ERROR(NULL, "Source code signature/hash mismatch (program source code has been changed/updated)");
+                }
+            }
+            catch (const cv::Exception& e)
+            {
+                CV_LOG_ERROR(NULL, "Can't open binary program file: " << fileName << " : " << e.what());
+            }
+            catch (...)
+            {
+                CV_LOG_ERROR(NULL, "Can't open binary program file: " << fileName << " : Unknown error");
+            }
+            if (!isValid)
+            {
+                clearFile();
+            }
+            else
+            {
+                seekReadAbsolute(0);
+            }
+        }
+    }
+
+    bool read(const std::string& key, std::vector<char>& buf)
+    {
+        if (!f.is_open())
+            return false;
+
+        size_t fileSize = getFileSize();
+        if (fileSize == 0)
+        {
+            CV_LOG_ERROR(NULL, "Invalid file (empty): " << fileName_);
+            clearFile();
+            return false;
+        }
+        seekReadAbsolute(0);
+
+        // bypass FileHeader
+        uint32_t fileSourceSignatureSize = readUInt32();
+        CV_Assert(fileSourceSignatureSize > 0);
+        seekReadRelative(fileSourceSignatureSize);
+
+        uint32_t numberOfEntries = readUInt32();
+        CV_Assert(numberOfEntries > 0);
+        if (numberOfEntries != MAX_ENTRIES)
+        {
+            CV_LOG_ERROR(NULL, "Invalid file: " << fileName_);
+            clearFile();
+            return false;
+        }
+        f.read((char*)&entryOffsets[0], sizeof(entryOffsets));
+        CV_Assert(!f.fail());
+
+        uint32_t entryNum = getHash(key);
+
+        uint32_t entryOffset = entryOffsets[entryNum];
+        FileEntry entry;
+        while (entryOffset > 0)
+        {
+            seekReadAbsolute(entryOffset);
+            //CV_StaticAssert(sizeof(entry) == sizeof(uint32_t) * 3, "");
+            f.read((char*)&entry, sizeof(entry));
+            CV_Assert(!f.fail());
+            cv::AutoBuffer<char> fileKey(entry.keySize + 1);
+            if (key.size() == entry.keySize)
+            {
+                if (entry.keySize > 0)
+                {
+                    f.read((char*)fileKey, entry.keySize);
+                    CV_Assert(!f.fail());
+                }
+                if (memcmp((const char*)fileKey, key.c_str(), entry.keySize) == 0)
+                {
+                    buf.resize(entry.dataSize);
+                    f.read(&buf[0], entry.dataSize);
+                    CV_Assert(!f.fail());
+                    seekReadAbsolute(0);
+                    CV_LOG_VERBOSE(NULL, 0, "Read...");
+                    return true;
+                }
+            }
+            if (entry.nextEntryFileOffset == 0)
+                break;
+            entryOffset = entry.nextEntryFileOffset;
+        }
+        return false;
+    }
+
+    bool write(const std::string& key, std::vector<char>& buf)
+    {
+        if (!f.is_open())
+        {
+            f.open(fileName_.c_str(), std::ios::in|std::ios::out|std::ios::binary);
+            if (!f.is_open())
+            {
+                f.open(fileName_.c_str(), std::ios::out|std::ios::binary);
+                if (!f.is_open())
+                {
+                    CV_LOG_ERROR(NULL, "Can't create file: " << fileName_);
+                    return false;
+                }
+            }
+        }
+
+        size_t fileSize = getFileSize();
+        if (fileSize == 0)
+        {
+            // Write header
+            seekWriteAbsolute(0);
+            writeUInt32((uint32_t)sourceSignatureSize_);
+            f.write(sourceSignature_, sourceSignatureSize_);
+            CV_Assert(!f.fail());
+
+            writeUInt32(MAX_ENTRIES);
+            memset(entryOffsets, 0, sizeof(entryOffsets));
+            f.write((char*)entryOffsets, sizeof(entryOffsets));
+            CV_Assert(!f.fail());
+            f.flush();
+            CV_Assert(!f.fail());
+            f.close();
+            f.open(fileName_.c_str(), std::ios::in|std::ios::out|std::ios::binary);
+            CV_Assert(f.is_open());
+            fileSize = getFileSize();
+        }
+        seekReadAbsolute(0);
+
+        // bypass FileHeader
+        uint32_t fileSourceSignatureSize = readUInt32();
+        CV_Assert(fileSourceSignatureSize == sourceSignatureSize_);
+        seekReadRelative(fileSourceSignatureSize);
+
+        uint32_t numberOfEntries = readUInt32();
+        CV_Assert(numberOfEntries > 0);
+        if (numberOfEntries != MAX_ENTRIES)
+        {
+            CV_LOG_ERROR(NULL, "Invalid file: " << fileName_);
+            clearFile();
+            return false;
+        }
+        size_t tableEntriesOffset = (size_t)f.tellg();
+        f.read((char*)&entryOffsets[0], sizeof(entryOffsets));
+        CV_Assert(!f.fail());
+
+        uint32_t entryNum = getHash(key);
+
+        uint32_t entryOffset = entryOffsets[entryNum];
+        FileEntry entry;
+        while (entryOffset > 0)
+        {
+            seekReadAbsolute(entryOffset);
+            //CV_StaticAssert(sizeof(entry) == sizeof(uint32_t) * 3, "");
+            f.read((char*)&entry, sizeof(entry));
+            CV_Assert(!f.fail());
+            cv::AutoBuffer<char> fileKey(entry.keySize + 1);
+            if (key.size() == entry.keySize)
+            {
+                if (entry.keySize > 0)
+                {
+                    f.read((char*)fileKey, entry.keySize);
+                    CV_Assert(!f.fail());
+                }
+                if (0 == memcmp((const char*)fileKey, key.c_str(), entry.keySize))
+                {
+                    // duplicate
+                    CV_LOG_VERBOSE(NULL, 0, "Duplicate key ignored: " << fileName_);
+                    return false;
+                }
+            }
+            if (entry.nextEntryFileOffset == 0)
+                break;
+            entryOffset = entry.nextEntryFileOffset;
+        }
+        seekReadAbsolute(0);
+        if (entryOffset > 0)
+        {
+            seekWriteAbsolute(entryOffset);
+            entry.nextEntryFileOffset = (uint32_t)fileSize;
+            f.write((char*)&entry, sizeof(entry));
+            CV_Assert(!f.fail());
+        }
+        else
+        {
+            entryOffsets[entryNum] = (uint32_t)fileSize;
+            seekWriteAbsolute(tableEntriesOffset);
+            f.write((char*)entryOffsets, sizeof(entryOffsets));
+            CV_Assert(!f.fail());
+        }
+        seekWriteAbsolute(fileSize);
+        entry.nextEntryFileOffset = 0;
+        entry.dataSize = (uint32_t)buf.size();
+        entry.keySize = (uint32_t)key.size();
+        f.write((char*)&entry, sizeof(entry));
+        CV_Assert(!f.fail());
+        f.write(key.c_str(), entry.keySize);
+        CV_Assert(!f.fail());
+        f.write(&buf[0], entry.dataSize);
+        CV_Assert(!f.fail());
+        f.flush();
+        CV_Assert(!f.fail());
+        CV_LOG_VERBOSE(NULL, 0, "Write... (" << buf.size() << " bytes)");
+        return true;
+    }
+};
+#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
+
+
 bool haveOpenCL()
 {
 #ifdef HAVE_OPENCL
@@ -1470,11 +1969,32 @@ struct Context::Impl
         }
     }
 
+    std::string getPrefixString()
+    {
+        if (prefix.empty())
+        {
+            const Device& d = devices[0];
+            prefix = d.vendorName() + "--" + d.name() + "--" + d.driverVersion();
+            // sanitize chars
+            for (size_t i = 0; i < prefix.size(); i++)
+            {
+                char c = prefix[i];
+                if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-'))
+                {
+                    prefix[i] = '_';
+                }
+            }
+        }
+        return prefix;
+    }
+
     IMPLEMENT_REFCOUNTABLE();
 
     cl_context handle;
     std::vector<Device> devices;
 
+    std::string prefix;
+
     cv::Mutex program_cache_mutex;
     typedef std::map<std::string, Program> phash_t;
     phash_t phash;
@@ -2700,13 +3220,141 @@ struct Program::Impl
          handle(NULL)
     {
         refcount = 1;
-        compile(Context::getDefault(), errmsg);
+        const Context ctx = Context::getDefault();
+        Device device = ctx.device(0);
+        if (device.isAMD())
+            buildflags += " -D AMD_DEVICE";
+        else if (device.isIntel())
+            buildflags += " -D INTEL_DEVICE";
+        compile(ctx, errmsg);
     }
 
     bool compile(const Context& ctx, String& errmsg)
     {
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT
+        OpenCLBinaryCacheConfigurator& config = OpenCLBinaryCacheConfigurator::getSingletonInstance();
+        const std::string base_dir = config.prepareCacheDirectoryForContext(ctx.getImpl()->getPrefixString());
+        const std::string fname = base_dir.empty() ? std::string() :
+                std::string(base_dir + src.getImpl()->module_.c_str() + "--" + src.getImpl()->name_ + "_" + src.getImpl()->codeHash_ + ".bin");
+        const cv::Ptr<utils::fs::FileLock> fileLock = config.cache_lock_; // can be empty
+        const String& hash_str = src.getImpl()->codeHash_;
+        if (!fname.empty() && CV_OPENCL_CACHE_ENABLE)
+        {
+            try
+            {
+                std::vector<char> binaryBuf;
+                bool res = false;
+                {
+                    cv::utils::optional_shared_lock_guard<cv::utils::fs::FileLock> lock_fs(fileLock.get());
+                    BinaryProgramFile file(fname, hash_str.c_str());
+                    res = file.read(buildflags, binaryBuf);
+                }
+                if (res)
+                {
+                    CV_Assert(!binaryBuf.empty());
+                    bool isLoaded = createFromBinary(ctx, binaryBuf, errmsg);
+                    if (isLoaded)
+                        return true;
+                }
+            }
+            catch (const cv::Exception& e)
+            {
+                CV_UNUSED(e);
+                CV_LOG_VERBOSE(NULL, 0, "Can't load OpenCL binary: " + fname << std::endl << e.what());
+            }
+            catch (...)
+            {
+                CV_LOG_VERBOSE(NULL, 0, "Can't load OpenCL binary: " + fname);
+            }
+        }
+#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
         CV_Assert(handle == NULL);
-        CV_INSTRUMENT_REGION_OPENCL_COMPILE(cv::format("Compile: %" PRIx64 " options: %s", src.hash(), buildflags.c_str()).c_str());
+        if (!buildFromSources(ctx, errmsg))
+        {
+            return true;
+        }
+        CV_Assert(handle != NULL);
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT
+        if (!fname.empty() && CV_OPENCL_CACHE_WRITE)
+        {
+            try
+            {
+                std::vector<char> binaryBuf;
+                getProgramBinary(binaryBuf);
+                {
+                    cv::utils::optional_lock_guard<cv::utils::fs::FileLock> lock_fs(fileLock.get());
+                    BinaryProgramFile file(fname, hash_str.c_str());
+                    file.write(buildflags, binaryBuf);
+                }
+            }
+            catch (const cv::Exception& e)
+            {
+                CV_LOG_WARNING(NULL, "Can't save OpenCL binary into cache: " + fname << std::endl << e.what());
+            }
+            catch (...)
+            {
+                CV_LOG_WARNING(NULL, "Can't save OpenCL binary into cache: " + fname);
+            }
+        }
+#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
+#if CV_OPENCL_VALIDATE_BINARY_PROGRAMS
+        if (CV_OPENCL_VALIDATE_BINARY_PROGRAMS_VALUE)
+        {
+            std::vector<char> binaryBuf;
+            getProgramBinary(binaryBuf);
+            if (!binaryBuf.empty())
+            {
+                CV_OCL_DBG_CHECK(clReleaseProgram(handle));
+                handle = NULL;
+                createFromBinary(ctx, binaryBuf, errmsg);
+            }
+        }
+#endif
+        return handle != NULL;
+    }
+
+    void dumpBuildLog_(cl_int result, const cl_device_id* deviceList, String& errmsg)
+    {
+        AutoBuffer<char, 4096> buffer; buffer[0] = 0;
+
+        size_t retsz = 0;
+        cl_int log_retval = clGetProgramBuildInfo(handle, deviceList[0],
+                                                  CL_PROGRAM_BUILD_LOG, 0, 0, &retsz);
+        if (log_retval == CL_SUCCESS && retsz > 1)
+        {
+            buffer.resize(retsz + 16);
+            log_retval = clGetProgramBuildInfo(handle, deviceList[0],
+                                               CL_PROGRAM_BUILD_LOG, retsz+1, (char*)buffer, &retsz);
+            if (log_retval == CL_SUCCESS)
+            {
+                if (retsz < buffer.size())
+                    buffer[retsz] = 0;
+                else
+                    buffer[buffer.size() - 1] = 0;
+            }
+            else
+            {
+                buffer[0] = 0;
+            }
+        }
+
+        errmsg = String(buffer);
+        printf("OpenCL program build log: %s/%s\nStatus %d: %s\n%s\n%s\n",
+                src.getImpl()->module_.c_str(), src.getImpl()->name_.c_str(),
+                result, getOpenCLErrorString(result),
+                buildflags.c_str(), errmsg.c_str());
+        fflush(stdout);
+    }
+
+    bool buildFromSources(const Context& ctx, String& errmsg)
+    {
+        CV_Assert(handle == NULL);
+        CV_INSTRUMENT_REGION_OPENCL_COMPILE(cv::format("Build OpenCL program: %s/%s %" PRIx64 " options: %s",
+                src.getImpl()->module_.c_str(), src.getImpl()->name_.c_str(),
+                src.hash(), buildflags.c_str()).c_str());
+
+        CV_LOG_VERBOSE(NULL, 0, "Compile... " << src.getImpl()->module_.c_str() << "/" << src.getImpl()->name_.c_str());
+
         const String& srcstr = src.source();
         const char* srcptr = srcstr.c_str();
         size_t srclen = srcstr.size();
@@ -2717,54 +3365,20 @@ struct Program::Impl
         CV_Assert(handle || retval != CL_SUCCESS);
         if (handle && retval == CL_SUCCESS)
         {
-            int i, n = (int)ctx.ndevices();
-            AutoBuffer<void*> deviceListBuf(n+1);
-            void** deviceList = deviceListBuf;
-            for( i = 0; i < n; i++ )
-                deviceList[i] = ctx.device(i).ptr();
-
-            Device device = Device::getDefault();
-            if (device.isAMD())
-                buildflags += " -D AMD_DEVICE";
-            else if (device.isIntel())
-                buildflags += " -D INTEL_DEVICE";
-
-            retval = clBuildProgram(handle, n,
-                                    (const cl_device_id*)deviceList,
-                                    buildflags.c_str(), 0, 0);
+            size_t n = ctx.ndevices();
+            AutoBuffer<cl_device_id, 4> deviceListBuf(n + 1);
+            cl_device_id* deviceList = deviceListBuf;
+            for (size_t i = 0; i < n; i++)
+            {
+                deviceList[i] = (cl_device_id)(ctx.device(i).ptr());
+            }
+
+            retval = clBuildProgram(handle, (cl_uint)n, deviceList, buildflags.c_str(), 0, 0);
 #if !CV_OPENCL_ALWAYS_SHOW_BUILD_LOG
             if (retval != CL_SUCCESS)
 #endif
             {
-                AutoBuffer<char, 4096> buffer; buffer[0] = 0;
-
-                size_t retsz = 0;
-                cl_int log_retval = clGetProgramBuildInfo(handle, (cl_device_id)deviceList[0],
-                                                          CL_PROGRAM_BUILD_LOG, 0, 0, &retsz);
-                if (log_retval == CL_SUCCESS && retsz > 1)
-                {
-                    buffer.resize(retsz + 16);
-                    log_retval = clGetProgramBuildInfo(handle, (cl_device_id)deviceList[0],
-                                                       CL_PROGRAM_BUILD_LOG, retsz+1, (char*)buffer, &retsz);
-                    if (log_retval == CL_SUCCESS)
-                    {
-                        if (retsz < buffer.size())
-                            buffer[retsz] = 0;
-                        else
-                            buffer[buffer.size() - 1] = 0;
-                    }
-                    else
-                    {
-                        buffer[0] = 0;
-                    }
-                }
-
-                errmsg = String(buffer);
-                printf("OpenCL program build log: %s (%s)\nStatus %d: %s\n%s\n%s\n",
-                        src.getImpl()->name_.c_str(), src.getImpl()->module_.c_str(),
-                        retval, getOpenCLErrorString(retval),
-                        buildflags.c_str(), errmsg.c_str());
-                fflush(stdout);
+                dumpBuildLog_(retval, deviceList, errmsg);
 
                 // don't remove "retval != CL_SUCCESS" condition here:
                 // it would break CV_OPENCL_ALWAYS_SHOW_BUILD_LOG mode
@@ -2774,6 +3388,7 @@ struct Program::Impl
                     handle = NULL;
                 }
             }
+
         }
         return handle != NULL;
     }
@@ -2830,6 +3445,135 @@ struct Program::Impl
         return String((const char*)(uchar*)bufbuf, prefixlen + progsz);
     }
 
+    void getProgramBinary(std::vector<char>& buf)
+    {
+        CV_Assert(handle);
+        size_t sz = 0;
+        CV_OCL_CHECK(clGetProgramInfo(handle, CL_PROGRAM_BINARY_SIZES, sizeof(sz), &sz, NULL));
+        buf.resize(sz);
+        uchar* ptr = (uchar*)&buf[0];
+        CV_OCL_CHECK(clGetProgramInfo(handle, CL_PROGRAM_BINARIES, sizeof(ptr), &ptr, NULL));
+#if CV_OPENCL_VALIDATE_BINARY_PROGRAMS
+        if (CV_OPENCL_VALIDATE_BINARY_PROGRAMS_VALUE)
+        {
+            CV_LOG_INFO(NULL, "OpenCL: query kernel names (compiled)...");
+            size_t retsz = 0;
+            char kernels_buffer[4096] = {0};
+            cl_int result = clGetProgramInfo(handle, CL_PROGRAM_KERNEL_NAMES, sizeof(kernels_buffer), &kernels_buffer[0], &retsz);
+            if (retsz < sizeof(kernels_buffer))
+                kernels_buffer[retsz] = 0;
+            else
+                kernels_buffer[0] = 0;
+            CV_LOG_INFO(NULL, result << ": Kernels='" << kernels_buffer << "'");
+        }
+#endif
+    }
+
+    bool createFromBinary(const Context& ctx, const std::vector<char>& buf, String& errmsg)
+    {
+        CV_Assert(handle == NULL);
+        CV_INSTRUMENT_REGION_OPENCL_COMPILE("Load OpenCL program");
+        CV_LOG_VERBOSE(NULL, 0, "Load from binary... " << src.getImpl()->module_.c_str() << "/" << src.getImpl()->name_.c_str());
+
+        const uchar* binaryPtr = (uchar*)&buf[0];
+        size_t binarySize = buf.size();
+        CV_Assert(binarySize > 0);
+
+        size_t ndevices = (int)ctx.ndevices();
+        AutoBuffer<cl_device_id> devices_(ndevices);
+        AutoBuffer<const uchar*> binaryPtrs_(ndevices);
+        AutoBuffer<size_t> binarySizes_(ndevices);
+
+        cl_device_id* devices = devices_;
+        const uchar** binaryPtrs = binaryPtrs_;
+        size_t* binarySizes = binarySizes_;
+        for (size_t i = 0; i < ndevices; i++)
+        {
+            devices[i] = (cl_device_id)ctx.device(i).ptr();
+            binaryPtrs[i] = binaryPtr;
+            binarySizes[i] = binarySize;
+        }
+
+        cl_int result = 0;
+        handle = clCreateProgramWithBinary((cl_context)ctx.ptr(), (cl_uint)ndevices, (cl_device_id*)devices_,
+                                           binarySizes, binaryPtrs, NULL, &result);
+        if (result != CL_SUCCESS)
+        {
+            CV_LOG_ERROR(NULL, CV_OCL_API_ERROR_MSG(result, "clCreateProgramWithBinary"));
+            if (handle)
+            {
+                CV_OCL_DBG_CHECK(clReleaseProgram(handle));
+                handle = NULL;
+            }
+        }
+        if (!handle)
+            return false;
+        cl_build_status build_status = CL_BUILD_NONE;
+        size_t retsz = 0;
+        CV_OCL_DBG_CHECK(result = clGetProgramBuildInfo(handle, devices[0], CL_PROGRAM_BUILD_STATUS,
+                sizeof(build_status), &build_status, &retsz));
+        if (result == CL_SUCCESS && build_status == CL_BUILD_SUCCESS)
+        {
+            CV_LOG_VERBOSE(NULL, 0, "clGetProgramBuildInfo() pre-check returns CL_BUILD_SUCCESS. Skip clBuildProgram() call");
+        }
+        else
+        {
+            result = clBuildProgram(handle, (cl_uint)ndevices, (cl_device_id*)devices_, buildflags.c_str(), 0, 0);
+            CV_OCL_DBG_CHECK_RESULT(result, cv::format("clBuildProgram(binary: %s/%s)", src.getImpl()->module_.c_str(), src.getImpl()->name_.c_str()).c_str());
+            if (result != CL_SUCCESS)
+            {
+                dumpBuildLog_(result, devices, errmsg);
+                if (handle)
+                {
+                    CV_OCL_DBG_CHECK(clReleaseProgram(handle));
+                    handle = NULL;
+                }
+                return false;
+            }
+        }
+        if (build_status != CL_BUILD_SUCCESS)
+        {
+            CV_OCL_DBG_CHECK(result = clGetProgramBuildInfo(handle, devices[0], CL_PROGRAM_BUILD_STATUS,
+                    sizeof(build_status), &build_status, &retsz));
+            if (result == CL_SUCCESS)
+            {
+                if (build_status == CL_BUILD_SUCCESS)
+                {
+                    return true;
+                }
+                else
+                {
+                    CV_LOG_WARNING(NULL, "clGetProgramBuildInfo() returns " << build_status);
+                    return false;
+                }
+            }
+            else
+            {
+                CV_LOG_ERROR(NULL, CV_OCL_API_ERROR_MSG(result, "clGetProgramBuildInfo()"));
+                if (handle)
+                {
+                    CV_OCL_DBG_CHECK(clReleaseProgram(handle));
+                    handle = NULL;
+                }
+            }
+        }
+#if CV_OPENCL_VALIDATE_BINARY_PROGRAMS
+        if (handle && CV_OPENCL_VALIDATE_BINARY_PROGRAMS_VALUE)
+        {
+            CV_LOG_INFO(NULL, "OpenCL: query kernel names (binary)...");
+            retsz = 0;
+            char kernels_buffer[4096] = {0};
+            result = clGetProgramInfo(handle, CL_PROGRAM_KERNEL_NAMES, sizeof(kernels_buffer), &kernels_buffer[0], &retsz);
+            if (retsz < sizeof(kernels_buffer))
+                kernels_buffer[retsz] = 0;
+            else
+                kernels_buffer[0] = 0;
+            CV_LOG_INFO(NULL, result << ": Kernels='" << kernels_buffer << "'");
+        }
+#endif
+        return handle != NULL;
+    }
+
     ~Impl()
     {
         if( handle )
diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp
new file mode 100644 (file)
index 0000000..aea7a95
--- /dev/null
@@ -0,0 +1,445 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#include "precomp.hpp"
+
+#include <opencv2/core/utils/configuration.private.hpp>
+
+#include <opencv2/core/utils/logger.hpp>
+
+#include "opencv2/core/utils/filesystem.private.hpp"
+#include "opencv2/core/utils/filesystem.hpp"
+
+//#define DEBUG_FS_UTILS
+
+#ifdef DEBUG_FS_UTILS
+#include <iostream>
+#define DBG(...) __VA_ARGS__
+#else
+#define DBG(...)
+#endif
+
+
+#if OPENCV_HAVE_FILESYSTEM_SUPPORT
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+#include <direct.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#elif defined __linux__ || defined __APPLE__
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+namespace cv { namespace utils { namespace fs {
+
+static inline
+bool isPathSeparator(char c)
+{
+    return c == '/' || c == '\\';
+}
+
+bool exists(const cv::String& path)
+{
+    CV_INSTRUMENT_REGION()
+
+#if defined _WIN32 || defined WINCE
+    BOOL status = TRUE;
+    {
+        WIN32_FILE_ATTRIBUTE_DATA all_attrs;
+#ifdef WINRT
+        wchar_t wpath[MAX_PATH];
+        size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
+        CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
+        status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
+#else
+        status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
+#endif
+    }
+
+    return !!status;
+#else
+    struct stat stat_buf;
+    return (0 == stat(path.c_str(), &stat_buf));
+#endif
+}
+
+cv::String getcwd()
+{
+    CV_INSTRUMENT_REGION()
+    cv::AutoBuffer<char, 4096> buf;
+#if defined WIN32 || defined _WIN32 || defined WINCE
+#ifdef WINRT
+    return cv::String();
+#else
+    DWORD sz = GetCurrentDirectoryA(0, NULL);
+    buf.allocate((size_t)sz);
+    sz = GetCurrentDirectoryA((DWORD)buf.size(), (char*)buf);
+    return cv::String((char*)buf, (size_t)sz);
+#endif
+#elif defined __linux__ || defined __APPLE__
+    for(;;)
+    {
+        char* p = ::getcwd((char*)buf, buf.size());
+        if (p == NULL)
+        {
+            if (errno == ERANGE)
+            {
+                buf.allocate(buf.size() * 2);
+                continue;
+            }
+            return cv::String();
+        }
+        break;
+    }
+    return cv::String((char*)buf, (size_t)strlen((char*)buf));
+#else
+    return cv::String();
+#endif
+}
+
+
+bool createDirectory(const cv::String& path)
+{
+    CV_INSTRUMENT_REGION()
+#if defined WIN32 || defined _WIN32 || defined WINCE
+#ifdef WINRT
+    wchar_t wpath[MAX_PATH];
+    size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
+    CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
+    int result = CreateDirectoryA(wpath, NULL) ? 0 : -1;
+#else
+    int result = _mkdir(path.c_str());
+#endif
+#elif defined __linux__ || defined __APPLE__
+    int result = mkdir(path.c_str(), 0777);
+#else
+    int result = -1;
+#endif
+
+    if (result == -1)
+    {
+        return isDirectory(path);
+    }
+    return true;
+}
+
+bool createDirectories(const cv::String& path_)
+{
+    cv::String path = path_;
+    for (;;)
+    {
+        char last_char = path.empty() ? 0 : path[path.length() - 1];
+        if (last_char == '/' || last_char == '\\')
+        {
+            path = path.substr(0, path.length() - 1);
+            continue;
+        }
+        break;
+    }
+
+    if (path.empty() || path == "./" || path == ".\\" || path == ".")
+        return true;
+    if (isDirectory(path))
+        return true;
+
+    size_t pos = path.rfind('/');
+    if (pos == cv::String::npos)
+        pos = path.rfind('\\');
+    if (pos != cv::String::npos)
+    {
+        cv::String parent_directory = path.substr(0, pos);
+        if (!parent_directory.empty())
+        {
+            if (!createDirectories(parent_directory))
+                return false;
+        }
+    }
+
+    return createDirectory(path);
+}
+
+#ifdef _WIN32
+
+struct FileLock::Impl
+{
+    Impl(const char* fname)
+    {
+        // http://support.microsoft.com/kb/316609
+        int numRetries = 5;
+        do
+        {
+            handle = ::CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+            if (INVALID_HANDLE_VALUE == handle)
+            {
+                if (ERROR_SHARING_VIOLATION == GetLastError())
+                {
+                    numRetries--;
+                    Sleep(250);
+                    continue;
+                }
+                else
+                {
+                    CV_ErrorNoReturn_(Error::StsAssert, ("Can't open lock file: %s", fname));
+                }
+            }
+            break;
+        } while (numRetries > 0);
+    }
+    ~Impl()
+    {
+        if (INVALID_HANDLE_VALUE != handle)
+        {
+            ::CloseHandle(handle);
+        }
+    }
+
+    bool lock()
+    {
+        OVERLAPPED overlapped;
+        std::memset(&overlapped, 0, sizeof(overlapped));
+        return !!::LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
+    }
+    bool unlock()
+    {
+        OVERLAPPED overlapped;
+        std::memset(&overlapped, 0, sizeof(overlapped));
+        return !!::UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped);
+    }
+
+    bool lock_shared()
+    {
+        OVERLAPPED overlapped;
+        std::memset(&overlapped, 0, sizeof(overlapped));
+        return !!::LockFileEx(handle, 0, 0, MAXDWORD, MAXDWORD, &overlapped);
+    }
+    bool unlock_shared()
+    {
+        return unlock();
+    }
+
+    HANDLE handle;
+
+private:
+    Impl(const Impl&); // disabled
+    Impl& operator=(const Impl&); // disabled
+};
+
+#elif defined __linux__ || defined __APPLE__
+
+struct FileLock::Impl
+{
+    Impl(const char* fname)
+    {
+        handle = ::open(fname, O_RDWR);
+        CV_Assert(handle != -1);
+    }
+    ~Impl()
+    {
+        if (handle >= 0)
+            ::close(handle);
+    }
+
+    bool lock()
+    {
+        struct ::flock l;
+        std::memset(&l, 0, sizeof(l));
+        l.l_type = F_WRLCK;
+        l.l_whence = SEEK_SET;
+        l.l_start = 0;
+        l.l_len = 0;
+        DBG(std::cout << "Lock..." << std::endl);
+        bool res = -1 != ::fcntl(handle, F_SETLKW, &l);
+        return res;
+    }
+    bool unlock()
+    {
+        struct ::flock l;
+        std::memset(&l, 0, sizeof(l));
+        l.l_type = F_UNLCK;
+        l.l_whence = SEEK_SET;
+        l.l_start = 0;
+        l.l_len = 0;
+        DBG(std::cout << "Unlock..." << std::endl);
+        bool res = -1 != ::fcntl(handle, F_SETLK, &l);
+        return res;
+    }
+
+    bool lock_shared()
+    {
+        struct ::flock l;
+        std::memset(&l, 0, sizeof(l));
+        l.l_type = F_RDLCK;
+        l.l_whence = SEEK_SET;
+        l.l_start = 0;
+        l.l_len = 0;
+        DBG(std::cout << "Lock read..." << std::endl);
+        bool res = -1 != ::fcntl(handle, F_SETLKW, &l);
+        return res;
+    }
+    bool unlock_shared()
+    {
+        return unlock();
+    }
+
+    int handle;
+
+private:
+    Impl(const Impl&); // disabled
+    Impl& operator=(const Impl&); // disabled
+};
+
+#endif
+
+FileLock::FileLock(const char* fname)
+    : pImpl(new Impl(fname))
+{
+    // nothing
+}
+FileLock::~FileLock()
+{
+    delete pImpl;
+    pImpl = NULL;
+}
+
+void FileLock::lock() { CV_Assert(pImpl->lock()); }
+void FileLock::unlock() { CV_Assert(pImpl->unlock()); }
+void FileLock::lock_shared() { CV_Assert(pImpl->lock_shared()); }
+void FileLock::unlock_shared() { CV_Assert(pImpl->unlock_shared()); }
+
+
+
+cv::String getCacheDirectory(const char* sub_directory_name, const char* configuration_name)
+{
+    String cache_path;
+    if (configuration_name)
+    {
+        cache_path = utils::getConfigurationParameterString(configuration_name, "");
+    }
+    if (cache_path.empty())
+    {
+        cv::String default_cache_path;
+#ifdef _WIN32
+        char tmp_path_buf[MAX_PATH+1] = {0};
+        DWORD res = GetTempPath(MAX_PATH, tmp_path_buf);
+        if (res > 0 && res <= MAX_PATH)
+        {
+            default_cache_path = tmp_path_buf;
+        }
+#elif defined __ANDROID__
+        // no defaults
+#elif defined __APPLE__
+        const char* tmpdir_env = getenv("TMPDIR");
+        if (tmpdir_env && utils::fs::isDirectory(tmpdir_env))
+        {
+            default_cache_path = tmpdir_env;
+        }
+        else
+        {
+            default_cache_path = "/tmp/";
+            CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
+        }
+#elif defined __linux__
+        // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+        if (default_cache_path.empty())
+        {
+            const char* xdg_cache_env = getenv("XDG_CACHE_HOME");
+            if (xdg_cache_env && xdg_cache_env[0] && utils::fs::isDirectory(xdg_cache_env))
+            {
+                default_cache_path = xdg_cache_env;
+            }
+        }
+        if (default_cache_path.empty())
+        {
+            const char* home_env = getenv("HOME");
+            if (home_env && home_env[0] && utils::fs::isDirectory(home_env))
+            {
+                cv::String home_path = home_env;
+                cv::String home_cache_path = home_path + "/.cache/";
+                if (utils::fs::isDirectory(home_cache_path))
+                {
+                    default_cache_path = home_cache_path;
+                }
+            }
+        }
+        if (default_cache_path.empty())
+        {
+            const char* temp_path = "/var/tmp/";
+            if (utils::fs::isDirectory(temp_path))
+            {
+                default_cache_path = temp_path;
+                CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
+            }
+        }
+        if (default_cache_path.empty())
+        {
+            default_cache_path = "/tmp/";
+            CV_LOG_WARNING(NULL, "Using world accessible cache directory. This may be not secure: " << default_cache_path);
+        }
+#else
+        // no defaults
+#endif
+        CV_LOG_VERBOSE(NULL, 0, "default_cache_path = " << default_cache_path);
+        if (!default_cache_path.empty())
+        {
+            if (utils::fs::isDirectory(default_cache_path))
+            {
+                default_cache_path += "/opencv/" CV_VERSION "/";
+                if (sub_directory_name && sub_directory_name[0] != '\0')
+                    default_cache_path += cv::String(sub_directory_name) + "/";
+                if (!utils::fs::createDirectories(default_cache_path))
+                {
+                    CV_LOG_DEBUG(NULL, "Can't create OpenCV cache sub-directory: " << default_cache_path);
+                }
+                else
+                {
+                    cache_path = default_cache_path;
+                }
+            }
+            else
+            {
+                CV_LOG_INFO(NULL, "Can't find default cache directory (does it exist?): " << default_cache_path);
+            }
+        }
+        else
+        {
+            CV_LOG_DEBUG(NULL, "OpenCV has no support to discover default cache directory on the current platform");
+        }
+    }
+    else
+    {
+        if (cache_path == "disabled")
+            return cache_path;
+        if (!isDirectory(cache_path))
+        {
+            CV_LOG_WARNING(NULL, "Specified non-existed directory, creating OpenCV sub-directory for caching purposes: " << cache_path);
+            if (!createDirectories(cache_path))
+            {
+                CV_LOG_ERROR(NULL, "Can't create OpenCV cache sub-directory: " << cache_path);
+                cache_path.clear();
+            }
+        }
+    }
+    CV_Assert(cache_path.empty() || utils::fs::isDirectory(cache_path));
+    if (!cache_path.empty())
+    {
+        if (!isPathSeparator(cache_path[cache_path.size() - 1]))
+        {
+            cache_path += '/';
+        }
+    }
+    return cache_path;
+}
+
+}}} // namespace
+
+#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT