mirror of
https://github.com/opencv/opencv.git
synced 2024-11-25 03:30:34 +08:00
Merge pull request #10018 from alalek:ocl_binary_cache
This commit is contained in:
commit
e5d1790b7b
@ -261,6 +261,8 @@ public:
|
||||
void setUseSVM(bool enabled);
|
||||
|
||||
struct Impl;
|
||||
inline Impl* getImpl() const { return (Impl*)p; }
|
||||
//protected:
|
||||
Impl* p;
|
||||
};
|
||||
|
||||
|
38
modules/core/include/opencv2/core/utils/filesystem.hpp
Normal file
38
modules/core/include/opencv2/core/utils/filesystem.hpp
Normal file
@ -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
|
@ -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
|
119
modules/core/include/opencv2/core/utils/lock.private.hpp
Normal file
119
modules/core/include/opencv2/core/utils/lock.private.hpp
Normal file
@ -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
|
@ -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>
|
||||
|
@ -47,22 +47,31 @@
|
||||
#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_SHOW_RUN_KERNELS 0
|
||||
#define CV_OPENCL_TRACE_CHECK 0
|
||||
#define CV_OPENCL_ALWAYS_SHOW_BUILD_LOG 0
|
||||
|
||||
#define CV_OPENCL_SHOW_SVM_ERROR_LOG 1
|
||||
#define CV_OPENCL_SHOW_SVM_LOG 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
|
||||
|
||||
#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();
|
||||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
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 )
|
||||
|
445
modules/core/src/utils/filesystem.cpp
Normal file
445
modules/core/src/utils/filesystem.cpp
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user