diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index d48ff79e6d..eb39889bd5 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -304,7 +304,9 @@ if(MSVC) endif() endif() -include(cmake/OpenCVCompilerOptimizations.cmake) +if(PROJECT_NAME STREQUAL "OpenCV") + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVCompilerOptimizations.cmake") +endif() if(COMMAND ocv_compiler_optimization_options) ocv_compiler_optimization_options() endif() diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index 5f72f5f3b2..fc564e981a 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -1,4 +1,4 @@ -# Search TBB library (4.1 - 4.4, 2017) +# Search TBB library: 4.1 - 4.4, 2017-2020, 2021+ (oneTBB) # # Own TBB (3rdparty/tbb): # - set cmake option BUILD_TBB to ON diff --git a/cmake/OpenCVPluginStandalone.cmake b/cmake/OpenCVPluginStandalone.cmake new file mode 100644 index 0000000000..bf4569db78 --- /dev/null +++ b/cmake/OpenCVPluginStandalone.cmake @@ -0,0 +1,127 @@ +# Standalone OpenCV plugins build scripts +# +# Useful OpenCV common build variables: +# - CMAKE_BUILD_TYPE=Release/Debug +# - BUILD_WITH_DEBUG_INFO=ON +# - ENABLE_BUILD_HARDENING=ON +# +# Plugin configuration variables: +# - OPENCV_PLUGIN_DEPS - set of extra dependencies (modules), used for include dirs, target_link_libraries +# - OPENCV_PLUGIN_SUFFIX +# - OPENCV_PLUGIN_NAME +# - OPENCV_PLUGIN_OUTPUT_NAME_FULL (overrides both OPENCV_PLUGIN_NAME / OPENCV_PLUGIN_SUFFIX) +# +#============================================= + +if(NOT OpenCV_SOURCE_DIR) + message(FATAL_ERROR "OpenCV_SOURCE_DIR must be set to build the plugin!") +endif() + +if(NOT DEFINED CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() +message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") + +set(BUILD_SHARED_LIBS ON CACHE BOOL "") +if(NOT BUILD_SHARED_LIBS) + message(FATAL_ERROR "Static plugin build does not make sense") +endif() + +# re-use OpenCV build scripts +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVUtils.cmake") +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCXXCompiler.cmake") +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVCompilerOptions.cmake") + +function(ocv_create_plugin module default_name dependency_target dependency_target_desc) + + set(OPENCV_PLUGIN_NAME ${default_name} CACHE STRING "") + set(OPENCV_PLUGIN_DESTINATION "" CACHE PATH "") + project(${OPENCV_PLUGIN_NAME} LANGUAGES CXX) + + if(NOT TARGET ${dependency_target}) + message(FATAL_ERROR "${dependency_target_desc} was not found! (missing target ${dependency_target})") + endif() + + set(modules_ROOT "${OpenCV_SOURCE_DIR}/modules") + set(module_ROOT "${modules_ROOT}/${module}") + + foreach(src ${ARGN}) + list(APPEND sources "${module_ROOT}/${src}") + endforeach() + + add_library(${OPENCV_PLUGIN_NAME} MODULE + "${sources}" + ${OPENCV_PLUGIN_EXTRA_SRC_FILES} + ) + + if(OPENCV_PLUGIN_DEPS) + foreach(d ${OPENCV_PLUGIN_DEPS}) + list(APPEND OPENCV_PLUGIN_EXTRA_INCLUDES "${modules_ROOT}/${d}/include") + endforeach() + endif() + + target_include_directories(${OPENCV_PLUGIN_NAME} PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" + "${module_ROOT}/src" + "${module_ROOT}/include" + ${OPENCV_PLUGIN_EXTRA_INCLUDES} + ) + target_compile_definitions(${OPENCV_PLUGIN_NAME} PRIVATE "BUILD_PLUGIN=1") + + target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${dependency_target}) + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_VISIBILITY_PRESET hidden + ) + + if(DEFINED OPENCV_PLUGIN_MODULE_PREFIX) + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES PREFIX "${OPENCV_PLUGIN_MODULE_PREFIX}") + endif() + + # Hack for Windows only, Linux/MacOS uses global symbol table (without exact .so binding) + if(WIN32) + find_package(OpenCV REQUIRED ${module} ${OPENCV_PLUGIN_DEPS}) + target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${OpenCV_LIBRARIES}) + endif() + + if(NOT OpenCV_FOUND) # build against sources (Linux) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/opencv2/opencv_modules.hpp" "#pragma once") + endif() + + if(WIN32) + ocv_update(OPENCV_DEBUG_POSTFIX d) + endif() + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}") + + if(DEFINED OPENCV_PLUGIN_SUFFIX) + # custom value + else() + if(WIN32) + ocv_update(OPENCV_PLUGIN_VERSION "${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}") + if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8) + ocv_update(OPENCV_PLUGIN_ARCH "_64") + else() + ocv_update(OPENCV_PLUGIN_ARCH "") + endif() + else() + # empty + endif() + ocv_update(OPENCV_PLUGIN_SUFFIX "${OPENCV_PLUGIN_VERSION}${OPENCV_PLUGIN_ARCH}") + endif() + + if(OPENCV_PLUGIN_DESTINATION) + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${OPENCV_PLUGIN_DESTINATION}") + message(STATUS "Output destination: ${OPENCV_PLUGIN_DESTINATION}") + endif() + + if(OPENCV_PLUGIN_OUTPUT_NAME_FULL) + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES OUTPUT_NAME "${OPENCV_PLUGIN_OUTPUT_NAME_FULL}") + elseif(OPENCV_PLUGIN_OUTPUT_NAME) + set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES OUTPUT_NAME "${OPENCV_PLUGIN_OUTPUT_NAME}${OPENCV_PLUGIN_SUFFIX}") + endif() + + install(TARGETS ${OPENCV_PLUGIN_NAME} LIBRARY DESTINATION . COMPONENT plugins) + + message(STATUS "Library name: ${OPENCV_PLUGIN_NAME}") + +endfunction() diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index ea31edec02..a6ea60365a 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -573,7 +573,9 @@ macro(ocv_check_flag_support lang flag varname base_options) string(REGEX REPLACE "^(/|-)" "HAVE_${_lang}_" ${varname} "${${varname}}") string(REGEX REPLACE " -|-|=| |\\.|," "_" ${varname} "${${varname}}") - ocv_check_compiler_flag("${_lang}" "${base_options} ${flag}" ${${varname}} ${ARGN}) + if(DEFINED CMAKE_${_lang}_COMPILER) + ocv_check_compiler_flag("${_lang}" "${base_options} ${flag}" ${${varname}} ${ARGN}) + endif() endmacro() macro(ocv_check_runtime_flag flag result) @@ -1571,6 +1573,30 @@ function(ocv_add_library target) endfunction() +function(ocv_add_external_target name inc link def) + if(BUILD_SHARED_LIBS) + set(imp IMPORTED) + endif() + add_library(ocv.3rdparty.${name} INTERFACE ${imp}) + set_target_properties(ocv.3rdparty.${name} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${inc}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${inc}" + INTERFACE_COMPILE_DEFINITIONS "${def}") + # When cmake version is greater than or equal to 3.11, INTERFACE_LINK_LIBRARIES no longer applies to interface library + # See https://github.com/opencv/opencv/pull/18658 + if (CMAKE_VERSION VERSION_LESS 3.11) + set_target_properties(ocv.3rdparty.${name} PROPERTIES + INTERFACE_LINK_LIBRARIES "${link}") + else() + target_link_libraries(ocv.3rdparty.${name} INTERFACE ${link}) + endif() + # + if(NOT BUILD_SHARED_LIBS) + install(TARGETS ocv.3rdparty.${name} EXPORT OpenCVModules) + endif() +endfunction() + + macro(ocv_get_libname var_name) get_filename_component(__libname "${ARGN}" NAME) # libopencv_core.so.3.3 -> opencv_core diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 274bc6abcd..7db716923a 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -17,6 +17,14 @@ ocv_add_dispatched_file_force_all(test_intrin128 TEST SSE2 SSE3 SSSE3 SSE4_1 SSE ocv_add_dispatched_file_force_all(test_intrin256 TEST AVX2 AVX512_SKX) ocv_add_dispatched_file_force_all(test_intrin512 TEST AVX512_SKX) + +# parallel backends configuration +set(PARALLEL_ENABLE_PLUGINS "ON" CACHE BOOL "Allow building parallel plugin support") +# TODO building plugins with OpenCV is not supported yet +#set(PARALLEL_PLUGIN_LIST "" CACHE STRING "List of parallel backends to be compiled as plugins (tbb, openmp or special value 'all')") +#string(REPLACE "," ";" PARALLEL_PLUGIN_LIST "${PARALLEL_PLUGIN_LIST}") # support comma-separated list (,) too + + ocv_add_module(core OPTIONAL opencv_cudev WRAP java objc python js) @@ -117,6 +125,14 @@ elseif(HAVE_CXX11 OR DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE) endif() +if(PARALLEL_ENABLE_PLUGINS) + ocv_append_source_file_compile_definitions(${CMAKE_CURRENT_SOURCE_DIR}/src/parallel/parallel.cpp "PARALLEL_ENABLE_PLUGINS=1") + if(OPENCV_DEBUG_POSTFIX) + ocv_append_source_file_compile_definitions("${CMAKE_CURRENT_LIST_DIR}/src/parallel/parallel.cpp" "DEBUG_POSTFIX=${OPENCV_DEBUG_POSTFIX}") + endif() +endif() + + ocv_create_module(${extra_libs}) ocv_target_link_libraries(${the_module} PRIVATE diff --git a/modules/core/cmake/parallel/detect_openmp.cmake b/modules/core/cmake/parallel/detect_openmp.cmake new file mode 100644 index 0000000000..39c050c78d --- /dev/null +++ b/modules/core/cmake/parallel/detect_openmp.cmake @@ -0,0 +1,13 @@ +if(CMAKE_VERSION VERSION_LESS "3.9") + message(STATUS "OpenMP detection requires CMake 3.9+") # OpenMP::OpenMP_CXX target +endif() + +find_package(OpenMP) +if(OpenMP_FOUND) + if(TARGET OpenMP::OpenMP_CXX) + set(HAVE_OPENMP 1) + ocv_add_external_target(openmp "" "OpenMP::OpenMP_CXX" "HAVE_OPENMP=1") + else() + message(WARNING "OpenMP: missing OpenMP::OpenMP_CXX target") + endif() +endif() diff --git a/modules/core/cmake/parallel/detect_tbb.cmake b/modules/core/cmake/parallel/detect_tbb.cmake new file mode 100644 index 0000000000..93059f8f67 --- /dev/null +++ b/modules/core/cmake/parallel/detect_tbb.cmake @@ -0,0 +1,5 @@ +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectTBB.cmake") + +if(HAVE_TBB) + ocv_add_external_target(tbb "" "tbb" "HAVE_TBB=1") +endif() diff --git a/modules/core/cmake/parallel/init.cmake b/modules/core/cmake/parallel/init.cmake new file mode 100644 index 0000000000..c1bbe99f18 --- /dev/null +++ b/modules/core/cmake/parallel/init.cmake @@ -0,0 +1,8 @@ +macro(ocv_add_core_parallel_backend backend_id cond_var) + if(${cond_var}) + include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake") + endif() +endmacro() + +ocv_add_core_parallel_backend("tbb" WITH_TBB) +ocv_add_core_parallel_backend("openmp" WITH_OPENMP) diff --git a/modules/core/include/opencv2/core/llapi/llapi.h b/modules/core/include/opencv2/core/llapi/llapi.h index 805d9ed262..ce322aecf8 100644 --- a/modules/core/include/opencv2/core/llapi/llapi.h +++ b/modules/core/include/opencv2/core/llapi/llapi.h @@ -27,6 +27,14 @@ Using this approach OpenCV provides some basic low level functionality for exter #define CV_API_CALL #endif +#ifndef CV_PLUGIN_EXPORTS +#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) +# define CV_PLUGIN_EXPORTS __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) +#endif +#endif + typedef enum cvResult { CV_ERROR_FAIL = -1, //!< Some error occurred (TODO Require to fill exception information) diff --git a/modules/core/include/opencv2/core/parallel/parallel_backend.hpp b/modules/core/include/opencv2/core/parallel/parallel_backend.hpp index c519c3b27b..965e7d5640 100644 --- a/modules/core/include/opencv2/core/parallel/parallel_backend.hpp +++ b/modules/core/include/opencv2/core/parallel/parallel_backend.hpp @@ -19,9 +19,10 @@ namespace cv { namespace parallel { * * Applications can replace OpenCV `parallel_for()` backend with own implementation (to reuse Application's thread pool). * - * @note This call is not thread-safe. Consider calling this function from the `main()` before any other OpenCV processing functions (and without any other created threads). * - * #### Intel TBB usage example: + * ### Backend API usage examples + * + * #### Intel TBB * * - include header with simple implementation of TBB backend: * @snippet parallel_backend/example-tbb.cpp tbb_include @@ -29,13 +30,22 @@ namespace cv { namespace parallel { * @snippet parallel_backend/example-tbb.cpp tbb_backend * - configuration of compiler/linker options is responsibility of Application's scripts * - * #### OpenMP usage example: + * #### OpenMP * * - include header with simple implementation of OpenMP backend: * @snippet parallel_backend/example-openmp.cpp openmp_include * - execute backend replacement code: * @snippet parallel_backend/example-openmp.cpp openmp_backend * - Configuration of compiler/linker options is responsibility of Application's scripts + * + * + * ### Plugins support + * + * Runtime configuration options: + * - change backend priority: `OPENCV_PARALLEL_PRIORITY_=9999` + * - disable backend: `OPENCV_PARALLEL_PRIORITY_=0` + * - specify list of backends with high priority (>100000): `OPENCV_PARALLEL_PRIORITY_LIST=TBB,OPENMP`. Unknown backends are registered as new plugins. + * */ /** Interface for parallel_for backends implementations @@ -68,6 +78,12 @@ public: */ CV_EXPORTS void setParallelForBackend(const std::shared_ptr& api, bool propagateNumThreads = true); +/** @brief Change OpenCV parallel_for backend + * + * @note This call is not thread-safe. Consider calling this function from the `main()` before any other OpenCV processing functions (and without any other created threads). + */ +CV_EXPORTS_W bool setParallelForBackend(const std::string& backendName, bool propagateNumThreads = true); + //! @} }} // namespace #endif // OPENCV_CORE_PARALLEL_BACKEND_HPP diff --git a/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp b/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp new file mode 100644 index 0000000000..000e5f6967 --- /dev/null +++ b/modules/core/include/opencv2/core/utils/plugin_loader.private.hpp @@ -0,0 +1,163 @@ +// 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_PLUGIN_LOADER_HPP +#define OPENCV_UTILS_PLUGIN_LOADER_HPP + +#include "opencv2/core/utils/filesystem.hpp" +#include "opencv2/core/utils/filesystem.private.hpp" + +#if OPENCV_HAVE_FILESYSTEM_SUPPORT + +#if defined(_WIN32) +#include +#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) +#include +#endif + +namespace cv { namespace plugin { namespace impl { + +#if defined(_WIN32) +typedef HMODULE LibHandle_t; +typedef wchar_t FileSystemChar_t; +typedef std::wstring FileSystemPath_t; + +// TODO wchar_t <=> UTF-8 +static inline +FileSystemPath_t toFileSystemPath(const std::string& p) +{ + FileSystemPath_t result; + result.resize(p.size()); + for (size_t i = 0; i < p.size(); i++) + result[i] = (wchar_t)p[i]; + return result; +} + +// TODO wchar_t <=> UTF-8 +static inline +std::string toPrintablePath(const FileSystemPath_t& p) +{ + std::string result; + result.resize(p.size()); + for (size_t i = 0; i < p.size(); i++) + { + wchar_t ch = p[i]; + if ((int)ch >= ' ' && (int)ch < 128) + result[i] = (char)ch; + else + result[i] = '?'; + } + return result; +} +#else // !_WIN32 +typedef void* LibHandle_t; +typedef char FileSystemChar_t; +typedef std::string FileSystemPath_t; + +static inline FileSystemPath_t toFileSystemPath(const std::string& p) { return p; } +static inline std::string toPrintablePath(const FileSystemPath_t& p) { return p; } +#endif + + +static inline +void* getSymbol_(LibHandle_t h, const char* symbolName) +{ +#if defined(_WIN32) + return (void*)GetProcAddress(h, symbolName); +#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) + return dlsym(h, symbolName); +#endif +} + +static inline +LibHandle_t libraryLoad_(const FileSystemPath_t& filename) +{ +#if defined(_WIN32) +# ifdef WINRT + return LoadPackagedLibrary(filename.c_str(), 0); +# else + return LoadLibraryW(filename.c_str()); +#endif +#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) + return dlopen(filename.c_str(), RTLD_LAZY); +#endif +} + +static inline +void libraryRelease_(LibHandle_t h) +{ +#if defined(_WIN32) + FreeLibrary(h); +#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) + dlclose(h); +#endif +} + +static inline +std::string libraryPrefix() +{ +#if defined(_WIN32) + return ""; +#else + return "lib"; +#endif +} +static inline +std::string librarySuffix() +{ +#if defined(_WIN32) + const char* suffix = "" + CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION) + #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) + "_64" + #endif + #if defined(_DEBUG) && defined(DEBUG_POSTFIX) + CVAUX_STR(DEBUG_POSTFIX) + #endif + ".dll"; + return suffix; +#else + return ".so"; +#endif +} + + +//============================ + +class CV_EXPORTS DynamicLib +{ +private: + LibHandle_t handle; + const FileSystemPath_t fname; + bool disableAutoUnloading_; + +public: + DynamicLib(const FileSystemPath_t& filename); + ~DynamicLib(); + /** Do not automatically unload library in destructor */ + inline void disableAutomaticLibraryUnloading() + { + disableAutoUnloading_ = true; + } + inline bool isLoaded() const + { + return handle != NULL; + } + void* getSymbol(const char* symbolName) const; + const std::string getName() const; +private: + void libraryLoad(const FileSystemPath_t& filename); + void libraryRelease(); + +private: + DynamicLib(const DynamicLib &) = delete; + DynamicLib &operator=(const DynamicLib &) = delete; +}; + + +}}} // namespace + +#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT + +#endif // OPENCV_UTILS_PLUGIN_LOADER_HPP diff --git a/modules/core/misc/plugins/parallel_openmp/CMakeLists.txt b/modules/core/misc/plugins/parallel_openmp/CMakeLists.txt new file mode 100644 index 0000000000..49d4f39751 --- /dev/null +++ b/modules/core/misc/plugins/parallel_openmp/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.5) +project(opencv_core_parallel_openmp CXX) + +get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../.." ABSOLUTE) +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake") + +# scan dependencies +set(WITH_OPENMP ON) +include("${OpenCV_SOURCE_DIR}/modules/core/cmake/parallel/init.cmake") + +message(STATUS "OpenMP: ${OPENMP_VERSION}") +ocv_create_plugin(core "opencv_core_parallel_openmp" "ocv.3rdparty.openmp" "OPENMP" "src/parallel/parallel_openmp.cpp") diff --git a/modules/core/misc/plugins/parallel_tbb/CMakeLists.txt b/modules/core/misc/plugins/parallel_tbb/CMakeLists.txt new file mode 100644 index 0000000000..c2129c7c2c --- /dev/null +++ b/modules/core/misc/plugins/parallel_tbb/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.5) +project(opencv_core_parallel_tbb CXX) + +get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../.." ABSOLUTE) +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake") + +# scan dependencies +set(WITH_TBB ON) +include("${OpenCV_SOURCE_DIR}/modules/core/cmake/parallel/init.cmake") + +message(STATUS "TBB: ver ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR} interface ${TBB_INTERFACE_VERSION}") +ocv_create_plugin(core "opencv_core_parallel_tbb" "ocv.3rdparty.tbb" "TBB" "src/parallel/parallel_tbb.cpp") diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 3bd0addbc4..7bb7e4633d 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -46,6 +46,7 @@ #include #include "opencv2/core/parallel/parallel_backend.hpp" +#include "parallel/parallel.hpp" #if defined _WIN32 || defined WINCE #include @@ -158,32 +159,6 @@ namespace cv { ParallelLoopBody::~ParallelLoopBody() {} -namespace parallel { - -static int numThreads = -1; - -static -std::shared_ptr& getCurrentParallelForAPI() -{ - static std::shared_ptr g_currentParallelForAPI; - return g_currentParallelForAPI; -} - -ParallelForAPI::~ParallelForAPI() -{ - // nothing -} - -void setParallelForBackend(const std::shared_ptr& api, bool propagateNumThreads) -{ - getCurrentParallelForAPI() = api; - if (propagateNumThreads && api) - { - setNumThreads(numThreads); - } -} - -} // namespace using namespace cv::parallel; namespace { diff --git a/modules/core/src/parallel/factory_parallel.hpp b/modules/core/src/parallel/factory_parallel.hpp new file mode 100644 index 0000000000..693fe30ecf --- /dev/null +++ b/modules/core/src/parallel/factory_parallel.hpp @@ -0,0 +1,48 @@ +// 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_CORE_PARALLEL_FACTORY_HPP +#define OPENCV_CORE_PARALLEL_FACTORY_HPP + +#include "opencv2/core/parallel/parallel_backend.hpp" + +namespace cv { namespace parallel { + +class IParallelBackendFactory +{ +public: + virtual ~IParallelBackendFactory() {} + virtual std::shared_ptr create() const = 0; +}; + + +class StaticBackendFactory CV_FINAL: public IParallelBackendFactory +{ +protected: + std::function(void)> create_fn_; + +public: + StaticBackendFactory(std::function(void)>&& create_fn) + : create_fn_(create_fn) + { + // nothing + } + + ~StaticBackendFactory() CV_OVERRIDE {} + + std::shared_ptr create() const CV_OVERRIDE + { + return create_fn_(); + } +}; + +// +// PluginBackendFactory is implemented in plugin_wrapper.cpp +// + +std::shared_ptr createPluginParallelBackendFactory(const std::string& baseName); + +}} // namespace + +#endif // OPENCV_CORE_PARALLEL_FACTORY_HPP diff --git a/modules/core/src/parallel/parallel.cpp b/modules/core/src/parallel/parallel.cpp new file mode 100644 index 0000000000..67fea67cf2 --- /dev/null +++ b/modules/core/src/parallel/parallel.cpp @@ -0,0 +1,173 @@ +// 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 "parallel.hpp" + +#include +#include +#ifdef NDEBUG +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1 +#else +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 +#endif +#include + + +#include "registry_parallel.hpp" +#include "registry_parallel.impl.hpp" + +#include "plugin_parallel_api.hpp" +#include "plugin_parallel_wrapper.impl.hpp" + + +namespace cv { namespace parallel { + +int numThreads = -1; + +ParallelForAPI::~ParallelForAPI() +{ + // nothing +} + +static +std::string& getParallelBackendName() +{ + static std::string g_backendName = toUpperCase(cv::utils::getConfigurationParameterString("OPENCV_PARALLEL_BACKEND", "")); + return g_backendName; +} + +static bool g_initializedParallelForAPI = false; + +static +std::shared_ptr createParallelForAPI() +{ + const std::string& name = getParallelBackendName(); + bool isKnown = false; + const auto& backends = getParallelBackendsInfo(); + if (!name.empty()) + { + CV_LOG_INFO(NULL, "core(parallel): requested backend name: " << name); + } + for (size_t i = 0; i < backends.size(); i++) + { + const auto& info = backends[i]; + if (!name.empty()) + { + if (name != info.name) + { + continue; + } + isKnown = true; + } + try + { + CV_LOG_DEBUG(NULL, "core(parallel): trying backend: " << info.name << " (priority=" << info.priority << ")"); + CV_Assert(info.backendFactory); + std::shared_ptr backend = info.backendFactory->create(); + if (!backend) + { + CV_LOG_VERBOSE(NULL, 0, "core(parallel): not available: " << info.name); + continue; + } + CV_LOG_INFO(NULL, "core(parallel): using backend: " << info.name << " (priority=" << info.priority << ")"); + g_initializedParallelForAPI = true; + getParallelBackendName() = info.name; + return backend; + } + catch (const std::exception& e) + { + CV_LOG_WARNING(NULL, "core(parallel): can't initialize " << info.name << " backend: " << e.what()); + } + catch (...) + { + CV_LOG_WARNING(NULL, "core(parallel): can't initialize " << info.name << " backend: Unknown C++ exception"); + } + } + if (name.empty()) + { + CV_LOG_DEBUG(NULL, "core(parallel): fallback on builtin code"); + } + else + { + if (!isKnown) + CV_LOG_INFO(NULL, "core(parallel): unknown backend: " << name); + } + g_initializedParallelForAPI = true; + return std::shared_ptr(); +} + +static inline +std::shared_ptr createDefaultParallelForAPI() +{ + CV_LOG_DEBUG(NULL, "core(parallel): Initializing parallel backend..."); + return createParallelForAPI(); +} + +std::shared_ptr& getCurrentParallelForAPI() +{ + static std::shared_ptr g_currentParallelForAPI = createDefaultParallelForAPI(); + return g_currentParallelForAPI; +} + +void setParallelForBackend(const std::shared_ptr& api, bool propagateNumThreads) +{ + getCurrentParallelForAPI() = api; + if (propagateNumThreads && api) + { + setNumThreads(numThreads); + } +} + +bool setParallelForBackend(const std::string& backendName, bool propagateNumThreads) +{ + CV_TRACE_FUNCTION(); + + std::string backendName_u = toUpperCase(backendName); + if (g_initializedParallelForAPI) + { + // ... already initialized + if (getParallelBackendName() == backendName_u) + { + CV_LOG_INFO(NULL, "core(parallel): backend is already activated: " << (backendName.empty() ? "builtin(legacy)" : backendName)); + return true; + } + else + { + // ... re-create new + CV_LOG_DEBUG(NULL, "core(parallel): replacing parallel backend..."); + getParallelBackendName() = backendName_u; + getCurrentParallelForAPI() = createParallelForAPI(); + } + } + else + { + // ... no backend exists, just specify the name (initialization is triggered by getCurrentParallelForAPI() call) + getParallelBackendName() = backendName_u; + } + std::shared_ptr api = getCurrentParallelForAPI(); + if (!api) + { + if (!backendName.empty()) + { + CV_LOG_WARNING(NULL, "core(parallel): backend is not available: " << backendName << " (using builtin legacy code)"); + return false; + } + else + { + CV_LOG_WARNING(NULL, "core(parallel): switched to builtin code (legacy)"); + } + } + if (!backendName_u.empty()) + { + CV_Assert(backendName_u == getParallelBackendName()); // data race? + } + + if (propagateNumThreads) + { + setNumThreads(numThreads); + } + return true; +} + +}} // namespace diff --git a/modules/core/src/parallel/parallel.hpp b/modules/core/src/parallel/parallel.hpp new file mode 100644 index 0000000000..b6a54b14e7 --- /dev/null +++ b/modules/core/src/parallel/parallel.hpp @@ -0,0 +1,29 @@ +// 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_CORE_SRC_PARALLEL_PARALLEL_HPP +#define OPENCV_CORE_SRC_PARALLEL_PARALLEL_HPP + +#include "opencv2/core/parallel/parallel_backend.hpp" + +namespace cv { namespace parallel { + +extern int numThreads; + +std::shared_ptr& getCurrentParallelForAPI(); + +#ifndef BUILD_PLUGIN + +#ifdef HAVE_TBB +std::shared_ptr createParallelBackendTBB(); +#endif + +#ifdef HAVE_OPENMP +std::shared_ptr createParallelBackendOpenMP(); +#endif + +#endif // BUILD_PLUGIN + +}} // namespace + +#endif // OPENCV_CORE_SRC_PARALLEL_PARALLEL_HPP diff --git a/modules/core/src/parallel/parallel_openmp.cpp b/modules/core/src/parallel/parallel_openmp.cpp new file mode 100644 index 0000000000..c0010dd845 --- /dev/null +++ b/modules/core/src/parallel/parallel_openmp.cpp @@ -0,0 +1,72 @@ +// 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" + +#ifdef HAVE_OPENMP + +#include "parallel.hpp" +#include "opencv2/core/parallel/backend/parallel_for.openmp.hpp" + +namespace cv { namespace parallel { + +static +std::shared_ptr& getInstance() +{ + static std::shared_ptr g_instance = std::make_shared(); + return g_instance; +} + +#ifndef BUILD_PLUGIN +std::shared_ptr createParallelBackendOpenMP() +{ + return getInstance(); +} +#endif + +}} // namespace + +#ifdef BUILD_PLUGIN + +#define ABI_VERSION 0 +#define API_VERSION 0 +#include "plugin_parallel_api.hpp" + +static +CvResult cv_getInstance(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT +{ + try + { + if (!handle) + return CV_ERROR_FAIL; + *handle = cv::parallel::getInstance().get(); + return CV_ERROR_OK; + } + catch (...) + { + return CV_ERROR_FAIL; + } +} + +static const OpenCV_Core_Parallel_Plugin_API plugin_api = +{ + { + sizeof(OpenCV_Core_Parallel_Plugin_API), ABI_VERSION, API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "OpenMP (" CVAUX_STR(_OPENMP) ") OpenCV parallel plugin" + }, + { + /* 1*/cv_getInstance + } +}; + +const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) + return &plugin_api; + return NULL; +} + +#endif // BUILD_PLUGIN + +#endif // HAVE_TBB diff --git a/modules/core/src/parallel/parallel_tbb.cpp b/modules/core/src/parallel/parallel_tbb.cpp new file mode 100644 index 0000000000..d430e858e6 --- /dev/null +++ b/modules/core/src/parallel/parallel_tbb.cpp @@ -0,0 +1,74 @@ +// 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 "factory_parallel.hpp" + +#ifdef HAVE_TBB + +#include "parallel.hpp" +#include "opencv2/core/parallel/backend/parallel_for.tbb.hpp" + +namespace cv { namespace parallel { + +static +std::shared_ptr& getInstance() +{ + static std::shared_ptr g_instance = std::make_shared(); + return g_instance; +} + +#ifndef BUILD_PLUGIN +std::shared_ptr createParallelBackendTBB() +{ + return getInstance(); +} +#endif + +}} // namespace + +#ifdef BUILD_PLUGIN + +#define ABI_VERSION 0 +#define API_VERSION 0 +#include "plugin_parallel_api.hpp" + +static +CvResult cv_getInstance(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT +{ + try + { + if (!handle) + return CV_ERROR_FAIL; + *handle = cv::parallel::getInstance().get(); + return CV_ERROR_OK; + } + catch (...) + { + return CV_ERROR_FAIL; + } +} + +static const OpenCV_Core_Parallel_Plugin_API plugin_api = +{ + { + sizeof(OpenCV_Core_Parallel_Plugin_API), ABI_VERSION, API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "TBB (interface " CVAUX_STR(TBB_INTERFACE_VERSION) ") OpenCV parallel plugin" + }, + { + /* 1*/cv_getInstance + } +}; + +const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) + return &plugin_api; + return NULL; +} + +#endif // BUILD_PLUGIN + +#endif // HAVE_TBB diff --git a/modules/core/src/parallel/plugin_parallel_api.hpp b/modules/core/src/parallel/plugin_parallel_api.hpp new file mode 100644 index 0000000000..bdc28d6de0 --- /dev/null +++ b/modules/core/src/parallel/plugin_parallel_api.hpp @@ -0,0 +1,72 @@ +// 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 PARALLEL_PLUGIN_API_HPP +#define PARALLEL_PLUGIN_API_HPP + +#include +#include + +#include "opencv2/core/parallel/parallel_backend.hpp" + +#if !defined(BUILD_PLUGIN) + +/// increased for backward-compatible changes, e.g. add new function +/// Caller API <= Plugin API -> plugin is fully compatible +/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API +#define API_VERSION 0 // preview + +/// increased for incompatible changes, e.g. remove function argument +/// Caller ABI == Plugin ABI -> plugin is compatible +/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible) +/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that) +#define ABI_VERSION 0 // preview + +#else // !defined(BUILD_PLUGIN) + +#if !defined(ABI_VERSION) || !defined(API_VERSION) +#error "Plugin must define ABI_VERSION and API_VERSION before including parallel_plugin_api.hpp" +#endif + +#endif // !defined(BUILD_PLUGIN) + +typedef cv::parallel::ParallelForAPI* CvPluginParallelBackendAPI; + +struct OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries +{ + /** @brief Get parallel backend API instance + + @param[out] handle pointer on backend API handle + + @note API-CALL 1, API-Version == 0 + */ + CvResult (CV_API_CALL *getInstance)(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT; +}; // OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries + +typedef struct OpenCV_Core_Parallel_Plugin_API_v0 +{ + OpenCV_API_Header api_header; + struct OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries v0; +} OpenCV_Core_Parallel_Plugin_API_v0; + +#if ABI_VERSION == 0 && API_VERSION == 0 +typedef OpenCV_Core_Parallel_Plugin_API_v0 OpenCV_Core_Parallel_Plugin_API; +#else +#error "Not supported configuration: check ABI_VERSION/API_VERSION" +#endif + +#ifdef BUILD_PLUGIN +extern "C" { + +CV_PLUGIN_EXPORTS +const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0 + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; + +} // extern "C" +#else // BUILD_PLUGIN +typedef const OpenCV_Core_Parallel_Plugin_API* (CV_API_CALL *FN_opencv_core_parallel_plugin_init_t) + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/); +#endif // BUILD_PLUGIN + +#endif // PARALLEL_PLUGIN_API_HPP diff --git a/modules/core/src/parallel/plugin_parallel_wrapper.impl.hpp b/modules/core/src/parallel/plugin_parallel_wrapper.impl.hpp new file mode 100644 index 0000000000..b986cfc174 --- /dev/null +++ b/modules/core/src/parallel/plugin_parallel_wrapper.impl.hpp @@ -0,0 +1,286 @@ +// 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. + +// +// Not a standalone header, part of parallel.cpp +// + +//================================================================================================== +// Dynamic backend implementation + +#include "opencv2/core/utils/plugin_loader.private.hpp" + +namespace cv { namespace impl { + +using namespace cv::parallel; + +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS) + +using namespace cv::plugin::impl; // plugin_loader.hpp + +class PluginParallelBackend CV_FINAL: public std::enable_shared_from_this +{ +protected: + void initPluginAPI() + { + const char* init_name = "opencv_core_parallel_plugin_init_v0"; + FN_opencv_core_parallel_plugin_init_t fn_init = reinterpret_cast(lib_->getSymbol(init_name)); + if (fn_init) + { + CV_LOG_DEBUG(NULL, "Found entry: '" << init_name << "'"); + for (int supported_api_version = API_VERSION; supported_api_version >= 0; supported_api_version--) + { + plugin_api_ = fn_init(ABI_VERSION, supported_api_version, NULL); + if (plugin_api_) + break; + } + if (!plugin_api_) + { + CV_LOG_INFO(NULL, "core(parallel): plugin is incompatible (can't be initialized): " << lib_->getName()); + return; + } + if (!checkCompatibility(plugin_api_->api_header, ABI_VERSION, API_VERSION, false)) + { + plugin_api_ = NULL; + return; + } + CV_LOG_INFO(NULL, "core(parallel): plugin is ready to use '" << plugin_api_->api_header.api_description << "'"); + } + else + { + CV_LOG_INFO(NULL, "core(parallel): plugin is incompatible, missing init function: '" << init_name << "', file: " << lib_->getName()); + } + } + + + bool checkCompatibility(const OpenCV_API_Header& api_header, unsigned int abi_version, unsigned int api_version, bool checkMinorOpenCVVersion) + { + if (api_header.opencv_version_major != CV_VERSION_MAJOR) + { + CV_LOG_ERROR(NULL, "core(parallel): wrong OpenCV major version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + if (!checkMinorOpenCVVersion) + { + // no checks for OpenCV minor version + } + else if (api_header.opencv_version_minor != CV_VERSION_MINOR) + { + CV_LOG_ERROR(NULL, "core(parallel): wrong OpenCV minor version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + CV_LOG_DEBUG(NULL, "core(parallel): initialized '" << api_header.api_description << "': built with " + << cv::format("OpenCV %d.%d (ABI/API = %d/%d)", + api_header.opencv_version_major, api_header.opencv_version_minor, + api_header.min_api_version, api_header.api_version) + << ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << abi_version << "/" << api_version << ")" + ); + if (api_header.min_api_version != abi_version) // future: range can be here + { + // actually this should never happen due to checks in plugin's init() function + CV_LOG_ERROR(NULL, "core(parallel): plugin is not supported due to incompatible ABI = " << api_header.min_api_version); + return false; + } + if (api_header.api_version != api_version) + { + CV_LOG_INFO(NULL, "core(parallel): NOTE: plugin is supported, but there is API version mismath: " + << cv::format("plugin API level (%d) != OpenCV API level (%d)", api_header.api_version, api_version)); + if (api_header.api_version < api_version) + { + CV_LOG_INFO(NULL, "core(parallel): NOTE: some functionality may be unavailable due to lack of support by plugin implementation"); + } + } + return true; + } + +public: + std::shared_ptr lib_; + const OpenCV_Core_Parallel_Plugin_API* plugin_api_; + + PluginParallelBackend(const std::shared_ptr& lib) + : lib_(lib) + , plugin_api_(NULL) + { + initPluginAPI(); + } + + std::shared_ptr create() const + { + CV_Assert(plugin_api_); + + CvPluginParallelBackendAPI instancePtr = NULL; + + if (plugin_api_->v0.getInstance) + { + if (CV_ERROR_OK == plugin_api_->v0.getInstance(&instancePtr)) + { + CV_Assert(instancePtr); + // TODO C++20 "aliasing constructor" + return std::shared_ptr(instancePtr, [](cv::parallel::ParallelForAPI*){}); // empty deleter + } + } + return std::shared_ptr(); + } +}; + + +class PluginParallelBackendFactory CV_FINAL: public IParallelBackendFactory +{ +public: + std::string baseName_; + std::shared_ptr backend; + bool initialized; +public: + PluginParallelBackendFactory(const std::string& baseName) + : baseName_(baseName) + , initialized(false) + { + // nothing, plugins are loaded on demand + } + + std::shared_ptr create() const CV_OVERRIDE + { + if (!initialized) + { + const_cast(this)->initBackend(); + } + if (backend) + return backend->create(); + return std::shared_ptr(); + } +protected: + void initBackend() + { + AutoLock lock(getInitializationMutex()); + try + { + if (!initialized) + loadPlugin(); + } + catch (...) + { + CV_LOG_INFO(NULL, "core(parallel): exception during plugin loading: " << baseName_ << ". SKIP"); + } + initialized = true; + } + void loadPlugin(); +}; + +static +std::vector getPluginCandidates(const std::string& baseName) +{ + using namespace cv::utils; + using namespace cv::utils::fs; + const std::string baseName_l = toLowerCase(baseName); + const std::string baseName_u = toUpperCase(baseName); + const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l); + std::vector paths; + // TODO OPENCV_PLUGIN_PATH + const std::vector paths_ = getConfigurationParameterPaths("OPENCV_CORE_PLUGIN_PATH", std::vector()); + if (paths_.size() != 0) + { + for (size_t i = 0; i < paths_.size(); i++) + { + paths.push_back(toFileSystemPath(paths_[i])); + } + } + else + { + FileSystemPath_t binaryLocation; + if (getBinLocation(binaryLocation)) + { + binaryLocation = getParent(binaryLocation); +#ifndef CV_CORE_PARALLEL_PLUGIN_SUBDIRECTORY + paths.push_back(binaryLocation); +#else + paths.push_back(binaryLocation + toFileSystemPath("/") + toFileSystemPath(CV_CORE_PARALLEL_PLUGIN_SUBDIRECTORY_STR)); +#endif + } + } + const std::string default_expr = libraryPrefix() + "opencv_core_parallel_" + baseName_l + "*" + librarySuffix(); + const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_CORE_PARALLEL_PLUGIN_") + baseName_u).c_str(), default_expr.c_str()); + std::vector results; +#ifdef _WIN32 + FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_core_parallel_" + baseName_l + librarySuffix()); + if (plugin_expr != default_expr) + { + moduleName = toFileSystemPath(plugin_expr); + results.push_back(moduleName); + } + for (const FileSystemPath_t& path : paths) + { + results.push_back(path + L"\\" + moduleName); + } + results.push_back(moduleName); +#else + CV_LOG_DEBUG(NULL, "core(parallel): " << baseName << " plugin's glob is '" << plugin_expr << "', " << paths.size() << " location(s)"); + for (const std::string& path : paths) + { + if (path.empty()) + continue; + std::vector candidates; + cv::glob(utils::fs::join(path, plugin_expr), candidates); + CV_LOG_DEBUG(NULL, " - " << path << ": " << candidates.size()); + copy(candidates.begin(), candidates.end(), back_inserter(results)); + } +#endif + CV_LOG_DEBUG(NULL, "Found " << results.size() << " plugin(s) for " << baseName); + return results; +} + +void PluginParallelBackendFactory::loadPlugin() +{ + for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_)) + { + auto lib = std::make_shared(plugin); + if (!lib->isLoaded()) + { + continue; + } + try + { + auto pluginBackend = std::make_shared(lib); + if (!pluginBackend) + { + continue; + } + if (pluginBackend->plugin_api_ == NULL) + { + CV_LOG_ERROR(NULL, "core(parallel): no compatible plugin API for backend: " << baseName_ << " in " << toPrintablePath(plugin)); + continue; + } +#if !defined(_WIN32) + // NB: we are going to use parallel backend, so prevent automatic library unloading + // (avoid uncontrolled crashes in worker threads of underlying libraries: libgomp, libtbb) + // details: https://github.com/opencv/opencv/pull/19470#pullrequestreview-589834777 + lib->disableAutomaticLibraryUnloading(); +#endif + backend = pluginBackend; + return; + } + catch (...) + { + CV_LOG_WARNING(NULL, "core(parallel): exception during plugin initialization: " << toPrintablePath(plugin) << ". SKIP"); + } + } +} + +#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS) + +} // namespace + +namespace parallel { + +std::shared_ptr createPluginParallelBackendFactory(const std::string& baseName) +{ +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS) + return std::make_shared(baseName); +#else + return std::shared_ptr(); +#endif +} + +}} // namespace diff --git a/modules/core/src/parallel/registry_parallel.hpp b/modules/core/src/parallel/registry_parallel.hpp new file mode 100644 index 0000000000..97464f278f --- /dev/null +++ b/modules/core/src/parallel/registry_parallel.hpp @@ -0,0 +1,25 @@ +// 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_CORE_PARALLEL_REGISTRY_HPP +#define OPENCV_CORE_PARALLEL_REGISTRY_HPP + +#include "factory_parallel.hpp" + +namespace cv { namespace parallel { + +struct ParallelBackendInfo +{ + int priority; // 1000- - default builtin priority + // 0 - disabled (OPENCV_PARALLEL_PRIORITY_ = 0) + // >10000 - prioritized list (OPENCV_PARALLEL_PRIORITY_LIST) + std::string name; + std::shared_ptr backendFactory; +}; + +const std::vector& getParallelBackendsInfo(); + +}} // namespace + +#endif // OPENCV_CORE_PARALLEL_REGISTRY_HPP diff --git a/modules/core/src/parallel/registry_parallel.impl.hpp b/modules/core/src/parallel/registry_parallel.impl.hpp new file mode 100644 index 0000000000..ef51437632 --- /dev/null +++ b/modules/core/src/parallel/registry_parallel.impl.hpp @@ -0,0 +1,167 @@ +// 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. + +// +// Not a standalone header, part of parallel.cpp +// + +namespace cv { namespace parallel { + +#define DECLARE_DYNAMIC_BACKEND(name) \ +ParallelBackendInfo { \ + 1000, name, createPluginParallelBackendFactory(name) \ +} + +#define DECLARE_STATIC_BACKEND(name, createBackendAPI) \ +ParallelBackendInfo { \ + 1000, name, std::make_shared([=] () -> std::shared_ptr { return createBackendAPI(); }) \ +} + +static +std::vector& getBuiltinParallelBackendsInfo() +{ + static std::vector g_backends + { +#ifdef HAVE_TBB + DECLARE_STATIC_BACKEND("TBB", createParallelBackendTBB), +#elif defined(PARALLEL_ENABLE_PLUGINS) + DECLARE_DYNAMIC_BACKEND("ONETBB"), // dedicated oneTBB plugin (interface >= 12000, binary incompatibe with TBB 2017-2020) + DECLARE_DYNAMIC_BACKEND("TBB"), // generic TBB plugins +#endif + +#ifdef HAVE_OPENMP + DECLARE_STATIC_BACKEND("OPENMP", createParallelBackendOpenMP), +#elif defined(PARALLEL_ENABLE_PLUGINS) + DECLARE_DYNAMIC_BACKEND("OPENMP") // TODO Intel OpenMP? +#endif + }; + return g_backends; +}; + +static +bool sortByPriority(const ParallelBackendInfo &lhs, const ParallelBackendInfo &rhs) +{ + return lhs.priority > rhs.priority; +} + +/** @brief Manages list of enabled backends + */ +class ParallelBackendRegistry +{ +protected: + std::vector enabledBackends; + ParallelBackendRegistry() + { + enabledBackends = getBuiltinParallelBackendsInfo(); + int N = (int)enabledBackends.size(); + for (int i = 0; i < N; i++) + { + ParallelBackendInfo& info = enabledBackends[i]; + info.priority = 1000 - i * 10; + } + CV_LOG_DEBUG(NULL, "core(parallel): Builtin backends(" << N << "): " << dumpBackends()); + if (readPrioritySettings()) + { + CV_LOG_INFO(NULL, "core(parallel): Updated backends priorities: " << dumpBackends()); + N = (int)enabledBackends.size(); + } + int enabled = 0; + for (int i = 0; i < N; i++) + { + ParallelBackendInfo& info = enabledBackends[enabled]; + if (enabled != i) + info = enabledBackends[i]; + size_t param_priority = utils::getConfigurationParameterSizeT(cv::format("OPENCV_PARALLEL_PRIORITY_%s", info.name.c_str()).c_str(), (size_t)info.priority); + CV_Assert(param_priority == (size_t)(int)param_priority); // overflow check + if (param_priority > 0) + { + info.priority = (int)param_priority; + enabled++; + } + else + { + CV_LOG_INFO(NULL, "core(parallel): Disable backend: " << info.name); + } + } + enabledBackends.resize(enabled); + CV_LOG_DEBUG(NULL, "core(parallel): Available backends(" << enabled << "): " << dumpBackends()); + std::sort(enabledBackends.begin(), enabledBackends.end(), sortByPriority); + CV_LOG_INFO(NULL, "core(parallel): Enabled backends(" << enabled << ", sorted by priority): " << (enabledBackends.empty() ? std::string("N/A") : dumpBackends())); + } + + static std::vector tokenize_string(const std::string& input, char token) + { + std::vector result; + std::string::size_type prev_pos = 0, pos = 0; + while((pos = input.find(token, pos)) != std::string::npos) + { + result.push_back(input.substr(prev_pos, pos-prev_pos)); + prev_pos = ++pos; + } + result.push_back(input.substr(prev_pos)); + return result; + } + bool readPrioritySettings() + { + bool hasChanges = false; + cv::String prioritized_backends = utils::getConfigurationParameterString("OPENCV_PARALLEL_PRIORITY_LIST", NULL); + if (prioritized_backends.empty()) + return hasChanges; + CV_LOG_INFO(NULL, "core(parallel): Configured priority list (OPENCV_PARALLEL_PRIORITY_LIST): " << prioritized_backends); + const std::vector names = tokenize_string(prioritized_backends, ','); + for (size_t i = 0; i < names.size(); i++) + { + const std::string& name = names[i]; + int priority = (int)(100000 + (names.size() - i) * 1000); + bool found = false; + for (size_t k = 0; k < enabledBackends.size(); k++) + { + ParallelBackendInfo& info = enabledBackends[k]; + if (name == info.name) + { + info.priority = priority; + CV_LOG_DEBUG(NULL, "core(parallel): New backend priority: '" << name << "' => " << info.priority); + found = true; + hasChanges = true; + break; + } + } + if (!found) + { + CV_LOG_INFO(NULL, "core(parallel): Adding parallel backend (plugin): '" << name << "'"); + enabledBackends.push_back(ParallelBackendInfo{priority, name, createPluginParallelBackendFactory(name)}); + hasChanges = true; + } + } + return hasChanges; + } +public: + std::string dumpBackends() const + { + std::ostringstream os; + for (size_t i = 0; i < enabledBackends.size(); i++) + { + if (i > 0) os << "; "; + const ParallelBackendInfo& info = enabledBackends[i]; + os << info.name << '(' << info.priority << ')'; + } + return os.str(); + } + + static ParallelBackendRegistry& getInstance() + { + static ParallelBackendRegistry g_instance; + return g_instance; + } + + inline const std::vector& getEnabledBackends() const { return enabledBackends; } +}; + + +const std::vector& getParallelBackendsInfo() +{ + return cv::parallel::ParallelBackendRegistry::getInstance().getEnabledBackends(); +} + +}} // namespace diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index 21e281c007..5a0a7637c2 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -43,6 +43,10 @@ #ifndef __OPENCV_PRECOMP_H__ #define __OPENCV_PRECOMP_H__ +#ifdef BUILD_PLUGIN +#include "opencv2/core/utility.hpp" +#else // BUILD_PLUGIN + #include "opencv2/opencv_modules.hpp" #include "cvconfig.h" @@ -375,4 +379,5 @@ int cv_snprintf(char* buf, int len, const char* fmt, ...); int cv_vsnprintf(char* buf, int len, const char* fmt, va_list args); } -#endif /*_CXCORE_INTERNAL_H_*/ +#endif // BUILD_PLUGIN +#endif // __OPENCV_PRECOMP_H__ diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp index 9e606dcdbe..663ec311e4 100644 --- a/modules/core/src/utils/filesystem.cpp +++ b/modules/core/src/utils/filesystem.cpp @@ -587,3 +587,8 @@ cv::String getCacheDirectory(const char* /*sub_directory_name*/, const char* /*c #endif // OPENCV_HAVE_FILESYSTEM_SUPPORT }}} // namespace + + +#if OPENCV_HAVE_FILESYSTEM_SUPPORT +#include "plugin_loader.impl.hpp" +#endif diff --git a/modules/core/src/utils/plugin_loader.impl.hpp b/modules/core/src/utils/plugin_loader.impl.hpp new file mode 100644 index 0000000000..4173c9d802 --- /dev/null +++ b/modules/core/src/utils/plugin_loader.impl.hpp @@ -0,0 +1,80 @@ +// 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. + +// +// Not a standalone header, part of filesystem.cpp +// + +#include "opencv2/core/utils/plugin_loader.private.hpp" + +#if !OPENCV_HAVE_FILESYSTEM_SUPPORT +#error "Invalid build configuration" +#endif + +#if 0 // TODO +#ifdef NDEBUG +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1 +#else +#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 +#endif +#include +#endif + +namespace cv { namespace plugin { namespace impl { + +DynamicLib::DynamicLib(const FileSystemPath_t& filename) + : handle(0), fname(filename), disableAutoUnloading_(false) +{ + libraryLoad(filename); +} + +DynamicLib::~DynamicLib() +{ + if (!disableAutoUnloading_) + { + libraryRelease(); + } + else if (handle) + { + CV_LOG_INFO(NULL, "skip auto unloading (disabled): " << toPrintablePath(fname)); + handle = 0; + } +} + +void* DynamicLib::getSymbol(const char* symbolName) const +{ + if (!handle) + { + return 0; + } + void* res = getSymbol_(handle, symbolName); + if (!res) + { + CV_LOG_DEBUG(NULL, "No symbol '" << symbolName << "' in " << toPrintablePath(fname)); + } + return res; +} + +const std::string DynamicLib::getName() const +{ + return toPrintablePath(fname); +} + +void DynamicLib::libraryLoad(const FileSystemPath_t& filename) +{ + handle = libraryLoad_(filename); + CV_LOG_INFO(NULL, "load " << toPrintablePath(filename) << " => " << (handle ? "OK" : "FAILED")); +} + +void DynamicLib::libraryRelease() +{ + if (handle) + { + CV_LOG_INFO(NULL, "unload "<< toPrintablePath(fname)); + libraryRelease_(handle); + handle = 0; + } +} + +}}} // namespace diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index a31d969ab8..7cb3eab598 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -1,7 +1,6 @@ set(VIDEOIO_PLUGIN_LIST "" CACHE STRING "List of videoio backends to be compiled as plugins (ffmpeg, gstreamer, mfx, msmf or special value 'all')") set(VIDEOIO_ENABLE_PLUGINS "ON" CACHE BOOL "Allow building videoio plugin support") -set(VIDEOIO_ENABLE_STRICT_PLUGIN_CHECK "ON" CACHE BOOL "Make sure OpenCV version is the same in plugin and host code") -mark_as_advanced(VIDEOIO_PLUGIN_LIST VIDEOIO_ENABLE_PLUGINS VIDEOIO_ENABLE_STRICT_PLUGIN_CHECK) +mark_as_advanced(VIDEOIO_PLUGIN_LIST VIDEOIO_ENABLE_PLUGINS) string(REPLACE "," ";" VIDEOIO_PLUGIN_LIST "${VIDEOIO_PLUGIN_LIST}") # support comma-separated list (,) too @@ -212,10 +211,6 @@ if(VIDEOIO_ENABLE_PLUGINS) ocv_target_compile_definitions(${the_module} PRIVATE ENABLE_PLUGINS) endif() -if(VIDEOIO_ENABLE_STRICT_PLUGIN_CHECK) - ocv_target_compile_definitions(${the_module} PRIVATE STRICT_PLUGIN_CHECK) -endif() - ocv_target_link_libraries(${the_module} LINK_PRIVATE ${tgts}) # copy FFmpeg dll to the output folder diff --git a/modules/videoio/cmake/init.cmake b/modules/videoio/cmake/init.cmake index 500b9386ff..e4de1134cf 100644 --- a/modules/videoio/cmake/init.cmake +++ b/modules/videoio/cmake/init.cmake @@ -1,31 +1,19 @@ -macro(add_backend backend_id cond_var) - if(${cond_var}) - include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake") - endif() -endmacro() +include(FindPkgConfig) -function(ocv_add_external_target name inc link def) - if(BUILD_SHARED_LIBS) - set(imp IMPORTED) - endif() - add_library(ocv.3rdparty.${name} INTERFACE ${imp}) - set_target_properties(ocv.3rdparty.${name} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${inc}" - INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${inc}" - INTERFACE_COMPILE_DEFINITIONS "${def}") - # When cmake version is greater than or equal to 3.11, INTERFACE_LINK_LIBRARIES no longer applies to interface library - # See https://github.com/opencv/opencv/pull/18658 - if (CMAKE_VERSION VERSION_LESS 3.11) - set_target_properties(ocv.3rdparty.${name} PROPERTIES - INTERFACE_LINK_LIBRARIES "${link}") - else() - target_link_libraries(ocv.3rdparty.${name} INTERFACE ${link}) - endif() - # - if(NOT BUILD_SHARED_LIBS) - install(TARGETS ocv.3rdparty.${name} EXPORT OpenCVModules) - endif() -endfunction() +# FIXIT: stop using PARENT_SCOPE in dependencies +if(PROJECT_NAME STREQUAL "OpenCV") + macro(add_backend backend_id cond_var) + if(${cond_var}) + include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake") + endif() + endmacro() +else() + function(add_backend backend_id cond_var) + if(${cond_var}) + include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake") + endif() + endfunction() +endif() add_backend("ffmpeg" WITH_FFMPEG) add_backend("gstreamer" WITH_GSTREAMER) diff --git a/modules/videoio/cmake/plugin_standalone.cmake b/modules/videoio/cmake/plugin_standalone.cmake deleted file mode 100644 index 190cb82808..0000000000 --- a/modules/videoio/cmake/plugin_standalone.cmake +++ /dev/null @@ -1,80 +0,0 @@ -#============================================= -# standalone build - -include(FindPkgConfig) - -#============================================= -# build with OpenCV -include("${OpenCV_SOURCE_DIR}/cmake/OpenCVUtils.cmake") - -function(ocv_create_videoio_plugin default_name target target_desc videoio_src_file) - - set(OPENCV_PLUGIN_NAME ${default_name} CACHE STRING "") - set(OPENCV_PLUGIN_DESTINATION "" CACHE PATH "") - project(${OPENCV_PLUGIN_NAME} LANGUAGES CXX) - - set(BUILD_SHARED_LIBS ON CACHE BOOL "") - if(NOT BUILD_SHARED_LIBS) - message(FATAL_ERROR "Static plugin build does not make sense") - endif() - - if(NOT OpenCV_SOURCE_DIR) - message(FATAL_ERROR "OpenCV_SOURCE_DIR must be set to build the plugin!") - endif() - - include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/init.cmake") - - if(NOT TARGET ${target}) - message(FATAL_ERROR "${target_desc} was not found!") - endif() - - get_filename_component(modules_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) - set(videoio_ROOT "${modules_ROOT}/videoio") - set(core_ROOT "${modules_ROOT}/core") - set(imgproc_ROOT "${modules_ROOT}/imgproc") - set(imgcodecs_ROOT "${modules_ROOT}/imgcodecs") - - add_library(${OPENCV_PLUGIN_NAME} MODULE - "${videoio_ROOT}/src/${videoio_src_file}" - ${OPENCV_PLUGIN_EXTRA_SRC_FILES} - ) - target_include_directories(${OPENCV_PLUGIN_NAME} PRIVATE - "${CMAKE_CURRENT_BINARY_DIR}" - "${videoio_ROOT}/src" - "${videoio_ROOT}/include" - "${core_ROOT}/include" - "${imgproc_ROOT}/include" - "${imgcodecs_ROOT}/include" - ) - target_compile_definitions(${OPENCV_PLUGIN_NAME} PRIVATE BUILD_PLUGIN) - - target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${target}) - set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES - CXX_STANDARD 11 - CXX_VISIBILITY_PRESET hidden - ) - - if(DEFINED OPENCV_PLUGIN_MODULE_PREFIX) - set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES PREFIX "${OPENCV_PLUGIN_MODULE_PREFIX}") - endif() - - # Hack for Windows - if(WIN32) - find_package(OpenCV REQUIRED core imgproc videoio) - target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${OpenCV_LIBS}) - endif() - - if(NOT OpenCV_FOUND) # build against sources (Linux) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/opencv2/opencv_modules.hpp" "#pragma once") - endif() - - if(OPENCV_PLUGIN_DESTINATION) - set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${OPENCV_PLUGIN_DESTINATION}") - message(STATUS "Output destination: ${OPENCV_PLUGIN_DESTINATION}") - endif() - - install(TARGETS ${OPENCV_PLUGIN_NAME} LIBRARY DESTINATION . COMPONENT plugins) - - message(STATUS "Library name: ${OPENCV_PLUGIN_NAME}") - -endfunction() diff --git a/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt b/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt index 55f3c2945f..ebe388a886 100644 --- a/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt +++ b/modules/videoio/misc/plugin_ffmpeg/CMakeLists.txt @@ -1,10 +1,15 @@ cmake_minimum_required(VERSION 3.5) -set(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../..") +get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../.." ABSOLUTE) +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake") + +# scan dependencies set(WITH_FFMPEG ON) set(OPENCV_FFMPEG_SKIP_BUILD_CHECK ON) -include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/plugin_standalone.cmake") -ocv_create_videoio_plugin("opencv_videoio_ffmpeg" "ocv.3rdparty.ffmpeg" "FFmpeg" "cap_ffmpeg.cpp") +include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/init.cmake") + +set(OPENCV_PLUGIN_DEPS core imgproc imgcodecs) +ocv_create_plugin(videoio "opencv_videoio_ffmpeg" "ocv.3rdparty.ffmpeg" "FFmpeg" "src/cap_ffmpeg.cpp") message(STATUS "FFMPEG_libavcodec_VERSION=${FFMPEG_libavcodec_VERSION}") message(STATUS "FFMPEG_libavformat_VERSION=${FFMPEG_libavformat_VERSION}") diff --git a/modules/videoio/misc/plugin_gstreamer/CMakeLists.txt b/modules/videoio/misc/plugin_gstreamer/CMakeLists.txt index 1361487e9b..b8e7aa5b55 100644 --- a/modules/videoio/misc/plugin_gstreamer/CMakeLists.txt +++ b/modules/videoio/misc/plugin_gstreamer/CMakeLists.txt @@ -1,8 +1,13 @@ cmake_minimum_required(VERSION 3.5) -set(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../..") +get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../.." ABSOLUTE) +include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake") + +# scan dependencies set(WITH_GSTREAMER ON) -include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/plugin_standalone.cmake") -ocv_create_videoio_plugin("opencv_videoio_gstreamer" "ocv.3rdparty.gstreamer" "GStreamer" "cap_gstreamer.cpp") +include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/init.cmake") + +set(OPENCV_PLUGIN_DEPS core imgproc imgcodecs) +ocv_create_plugin(videoio "opencv_videoio_gstreamer" "ocv.3rdparty.gstreamer" "GStreamer" "src/cap_gstreamer.cpp") message(STATUS "Using GStreamer: ${GSTREAMER_VERSION}") diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index 6d79b47296..2f6389c5fb 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -9,66 +9,26 @@ #include "plugin_capture_api.hpp" #include "plugin_writer_api.hpp" -#include "opencv2/core/utils/filesystem.hpp" #include "opencv2/core/utils/configuration.private.hpp" +#include "opencv2/core/utils/logger.hpp" + #include "opencv2/core/private.hpp" #include "videoio_registry.hpp" //================================================================================================== // Dynamic backend implementation -#include "opencv2/core/utils/logger.hpp" -#include -using namespace std; +#include "opencv2/core/utils/plugin_loader.private.hpp" -#if defined(_WIN32) -#include -#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) -#include -#endif #include "backend_plugin_legacy.impl.hpp" + namespace cv { namespace impl { -#if defined(_WIN32) -typedef HMODULE LibHandle_t; -typedef wchar_t FileSystemChar_t; -typedef std::wstring FileSystemPath_t; - -static -FileSystemPath_t toFileSystemPath(const std::string& p) -{ - FileSystemPath_t result; - result.resize(p.size()); - for (size_t i = 0; i < p.size(); i++) - result[i] = (wchar_t)p[i]; - return result; -} -static -std::string toPrintablePath(const FileSystemPath_t& p) -{ - std::string result; - result.resize(p.size()); - for (size_t i = 0; i < p.size(); i++) - { - wchar_t ch = p[i]; - if ((int)ch >= ' ' && (int)ch < 128) - result[i] = (char)ch; - else - result[i] = '?'; - } - return result; -} -#else // !_WIN32 -typedef void* LibHandle_t; -typedef char FileSystemChar_t; -typedef std::string FileSystemPath_t; - -static inline FileSystemPath_t toFileSystemPath(const std::string& p) { return p; } -static inline std::string toPrintablePath(const FileSystemPath_t& p) { return p; } -#endif +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) +using namespace cv::plugin::impl; // plugin_loader.hpp static Mutex& getInitializationMutex() { @@ -76,125 +36,6 @@ static Mutex& getInitializationMutex() return initializationMutex; } -static inline -void* getSymbol_(LibHandle_t h, const char* symbolName) -{ -#if defined(_WIN32) - return (void*)GetProcAddress(h, symbolName); -#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) - return dlsym(h, symbolName); -#endif -} - -static inline -LibHandle_t libraryLoad_(const FileSystemPath_t& filename) -{ -#if defined(_WIN32) -# ifdef WINRT - return LoadPackagedLibrary(filename.c_str(), 0); -# else - return LoadLibraryW(filename.c_str()); -#endif -#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) - return dlopen(filename.c_str(), RTLD_LAZY); -#endif -} - -static inline -void libraryRelease_(LibHandle_t h) -{ -#if defined(_WIN32) - FreeLibrary(h); -#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) - dlclose(h); -#endif -} - -static inline -std::string libraryPrefix() -{ -#if defined(_WIN32) - return ""; -#else - return "lib"; -#endif -} -static inline -std::string librarySuffix() -{ -#if defined(_WIN32) - const char* suffix = "" - CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION) - #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) - "_64" - #endif - #if defined(_DEBUG) && defined(DEBUG_POSTFIX) - CVAUX_STR(DEBUG_POSTFIX) - #endif - ".dll"; - return suffix; -#else - return ".so"; -#endif -} - -//============================ - -class DynamicLib -{ -private: - LibHandle_t handle; - const FileSystemPath_t fname; - -public: - DynamicLib(const FileSystemPath_t& filename) - : handle(0), fname(filename) - { - libraryLoad(filename); - } - ~DynamicLib() - { - libraryRelease(); - } - bool isLoaded() const - { - return handle != NULL; - } - void* getSymbol(const char* symbolName) const - { - if (!handle) - { - return 0; - } - void * res = getSymbol_(handle, symbolName); - if (!res) - CV_LOG_DEBUG(NULL, "No symbol '" << symbolName << "' in " << toPrintablePath(fname)); - return res; - } - const std::string getName() const { return toPrintablePath(fname); } -private: - void libraryLoad(const FileSystemPath_t& filename) - { - handle = libraryLoad_(filename); - CV_LOG_INFO(NULL, "load " << toPrintablePath(filename) << " => " << (handle ? "OK" : "FAILED")); - } - void libraryRelease() - { - if (handle) - { - CV_LOG_INFO(NULL, "unload "<< toPrintablePath(fname)); - libraryRelease_(handle); - handle = 0; - } - } - -private: - DynamicLib(const DynamicLib &); - DynamicLib &operator=(const DynamicLib &); -}; - - -//============================ class PluginBackend: public IBackend { @@ -345,12 +186,12 @@ protected: } public: - Ptr lib_; + Ptr lib_; const OpenCV_VideoIO_Capture_Plugin_API* capture_api_; const OpenCV_VideoIO_Writer_Plugin_API* writer_api_; const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; //!< deprecated - PluginBackend(const Ptr& lib) + PluginBackend(const Ptr& lib) : lib_(lib) , capture_api_(NULL), writer_api_(NULL) , plugin_api_(NULL) @@ -416,11 +257,11 @@ std::vector getPluginCandidates(const std::string& baseName) { using namespace cv::utils; using namespace cv::utils::fs; - const string baseName_l = toLowerCase(baseName); - const string baseName_u = toUpperCase(baseName); + const std::string baseName_l = toLowerCase(baseName); + const std::string baseName_u = toUpperCase(baseName); const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l); - vector paths; - const vector paths_ = getConfigurationParameterPaths("OPENCV_VIDEOIO_PLUGIN_PATH", vector()); + std::vector paths; + const std::vector paths_ = getConfigurationParameterPaths("OPENCV_VIDEOIO_PLUGIN_PATH", std::vector()); if (paths_.size() != 0) { for (size_t i = 0; i < paths_.size(); i++) @@ -441,9 +282,9 @@ std::vector getPluginCandidates(const std::string& baseName) #endif } } - const string default_expr = libraryPrefix() + "opencv_videoio_" + baseName_l + "*" + librarySuffix(); - const string plugin_expr = getConfigurationParameterString((std::string("OPENCV_VIDEOIO_PLUGIN_") + baseName_u).c_str(), default_expr.c_str()); - vector results; + const std::string default_expr = libraryPrefix() + "opencv_videoio_" + baseName_l + "*" + librarySuffix(); + const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_VIDEOIO_PLUGIN_") + baseName_u).c_str(), default_expr.c_str()); + std::vector results; #ifdef _WIN32 FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_videoio_" + baseName_l + librarySuffix()); #ifndef WINRT @@ -480,12 +321,12 @@ std::vector getPluginCandidates(const std::string& baseName) } #endif // _DEBUG && DEBUG_POSTFIX #else - CV_LOG_INFO(NULL, "VideoIO pluigin (" << baseName << "): glob is '" << plugin_expr << "', " << paths.size() << " location(s)"); - for (const string & path : paths) + CV_LOG_INFO(NULL, "VideoIO plugin (" << baseName << "): glob is '" << plugin_expr << "', " << paths.size() << " location(s)"); + for (const std::string& path : paths) { if (path.empty()) continue; - vector candidates; + std::vector candidates; cv::glob(utils::fs::join(path, plugin_expr), candidates); CV_LOG_INFO(NULL, " - " << path << ": " << candidates.size()); copy(candidates.begin(), candidates.end(), back_inserter(results)); @@ -499,7 +340,7 @@ void PluginBackendFactory::loadPlugin() { for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_)) { - Ptr lib = makePtr(plugin); + auto lib = makePtr(plugin); if (!lib->isLoaded()) continue; try @@ -834,11 +675,17 @@ Ptr PluginBackend::createWriter(const std::string& filename, int f return Ptr(); } +#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) + } // namespace Ptr createPluginBackendFactory(VideoCaptureAPIs id, const char* baseName) { +#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS) return makePtr(id, baseName); //.staticCast(); +#else + return Ptr(); +#endif } } // namespace diff --git a/modules/videoio/src/plugin_api.hpp b/modules/videoio/src/plugin_api.hpp index 011dff5ff6..b46009a4aa 100644 --- a/modules/videoio/src/plugin_api.hpp +++ b/modules/videoio/src/plugin_api.hpp @@ -211,14 +211,6 @@ typedef struct OpenCV_VideoIO_Plugin_API_preview_v0 OpenCV_VideoIO_Plugin_API_pr #ifdef BUILD_PLUGIN -#ifndef CV_PLUGIN_EXPORTS -#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) -# define CV_PLUGIN_EXPORTS __declspec(dllexport) -#elif defined __GNUC__ && __GNUC__ >= 4 -# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) -#endif -#endif - CV_PLUGIN_EXPORTS const OpenCV_VideoIO_Plugin_API_preview* CV_API_CALL opencv_videoio_plugin_init_v0 (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; diff --git a/modules/videoio/src/plugin_capture_api.hpp b/modules/videoio/src/plugin_capture_api.hpp index 2cb3f3163a..8f33d40219 100644 --- a/modules/videoio/src/plugin_capture_api.hpp +++ b/modules/videoio/src/plugin_capture_api.hpp @@ -144,14 +144,6 @@ typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_0 OpenCV_VideoIO_Capture_Plu #ifdef BUILD_PLUGIN -#ifndef CV_PLUGIN_EXPORTS -#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) -# define CV_PLUGIN_EXPORTS __declspec(dllexport) -#elif defined __GNUC__ && __GNUC__ >= 4 -# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) -#endif -#endif - CV_PLUGIN_EXPORTS const OpenCV_VideoIO_Capture_Plugin_API* CV_API_CALL opencv_videoio_capture_plugin_init_v1 (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; diff --git a/modules/videoio/src/plugin_writer_api.hpp b/modules/videoio/src/plugin_writer_api.hpp index e1ce014e98..50896eba51 100644 --- a/modules/videoio/src/plugin_writer_api.hpp +++ b/modules/videoio/src/plugin_writer_api.hpp @@ -146,14 +146,6 @@ typedef struct OpenCV_VideoIO_Writer_Plugin_API_v1_0 OpenCV_VideoIO_Writer_Plugi #ifdef BUILD_PLUGIN -#ifndef CV_PLUGIN_EXPORTS -#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) -# define CV_PLUGIN_EXPORTS __declspec(dllexport) -#elif defined __GNUC__ && __GNUC__ >= 4 -# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) -#endif -#endif - CV_PLUGIN_EXPORTS const OpenCV_VideoIO_Writer_Plugin_API* CV_API_CALL opencv_videoio_writer_plugin_init_v1 (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT;