2010-05-12 01:44:00 +08:00
|
|
|
/*M///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
|
|
//
|
|
|
|
// By downloading, copying, installing or using the software you agree to this license.
|
|
|
|
// If you do not agree to this license, do not download, install,
|
|
|
|
// copy or use the software.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// License Agreement
|
|
|
|
// For Open Source Computer Vision Library
|
|
|
|
//
|
|
|
|
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
|
|
|
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
2015-01-12 15:59:30 +08:00
|
|
|
// Copyright (C) 2015, Itseez Inc., all rights reserved.
|
2010-05-12 01:44:00 +08:00
|
|
|
// Third party copyrights are property of their respective owners.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
// are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistribution's of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
//
|
|
|
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
//
|
|
|
|
// * The name of the copyright holders may not be used to endorse or promote products
|
|
|
|
// derived from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// This software is provided by the copyright holders and contributors "as is" and
|
|
|
|
// any express or implied warranties, including, but not limited to, the implied
|
|
|
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
|
|
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
|
|
|
// indirect, incidental, special, exemplary, or consequential damages
|
|
|
|
// (including, but not limited to, procurement of substitute goods or services;
|
|
|
|
// loss of use, data, or profits; or business interruption) however caused
|
|
|
|
// and on any theory of liability, whether in contract, strict liability,
|
|
|
|
// or tort (including negligence or otherwise) arising in any way out of
|
|
|
|
// the use of this software, even if advised of the possibility of such damage.
|
|
|
|
//
|
|
|
|
//M*/
|
|
|
|
|
|
|
|
#include "precomp.hpp"
|
2015-09-25 23:00:53 +08:00
|
|
|
#include <iostream>
|
2018-09-27 20:52:42 +08:00
|
|
|
#include <ostream>
|
2010-05-12 01:44:00 +08:00
|
|
|
|
Merge pull request #9114 from pengli:dnn_rebase
add libdnn acceleration to dnn module (#9114)
* import libdnn code
Signed-off-by: Li Peng <peng.li@intel.com>
* add convolution layer ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* add pooling layer ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* add softmax layer ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* add lrn layer ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* add innerproduct layer ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* add HAVE_OPENCL macro
Signed-off-by: Li Peng <peng.li@intel.com>
* fix for convolution ocl
Signed-off-by: Li Peng <peng.li@intel.com>
* enable getUMat() for multi-dimension Mat
Signed-off-by: Li Peng <peng.li@intel.com>
* use getUMat for ocl acceleration
Signed-off-by: Li Peng <peng.li@intel.com>
* use CV_OCL_RUN macro
Signed-off-by: Li Peng <peng.li@intel.com>
* set OPENCL target when it is available
and disable fuseLayer for OCL target for the time being
Signed-off-by: Li Peng <peng.li@intel.com>
* fix innerproduct accuracy test
Signed-off-by: Li Peng <peng.li@intel.com>
* remove trailing space
Signed-off-by: Li Peng <peng.li@intel.com>
* Fixed tensorflow demo bug.
Root cause is that tensorflow has different algorithm with libdnn
to calculate convolution output dimension.
libdnn don't calculate output dimension anymore and just use one
passed in by config.
* split gemm ocl file
split it into gemm_buffer.cl and gemm_image.cl
Signed-off-by: Li Peng <peng.li@intel.com>
* Fix compile failure
Signed-off-by: Li Peng <peng.li@intel.com>
* check env flag for auto tuning
Signed-off-by: Li Peng <peng.li@intel.com>
* switch to new ocl kernels for softmax layer
Signed-off-by: Li Peng <peng.li@intel.com>
* update softmax layer
on some platform subgroup extension may not work well,
fallback to non subgroup ocl acceleration.
Signed-off-by: Li Peng <peng.li@intel.com>
* fallback to cpu path for fc layer with multi output
Signed-off-by: Li Peng <peng.li@intel.com>
* update output message
Signed-off-by: Li Peng <peng.li@intel.com>
* update fully connected layer
fallback to gemm API if libdnn return false
Signed-off-by: Li Peng <peng.li@intel.com>
* Add ReLU OCL implementation
* disable layer fusion for now
Signed-off-by: Li Peng <peng.li@intel.com>
* Add OCL implementation for concat layer
Signed-off-by: Wu Zhiwen <zhiwen.wu@intel.com>
* libdnn: update license and copyrights
Also refine libdnn coding style
Signed-off-by: Wu Zhiwen <zhiwen.wu@intel.com>
Signed-off-by: Li Peng <peng.li@intel.com>
* DNN: Don't link OpenCL library explicitly
* DNN: Make default preferableTarget to DNN_TARGET_CPU
User should set it to DNN_TARGET_OPENCL explicitly if want to
use OpenCL acceleration.
Also don't fusion when using DNN_TARGET_OPENCL
* DNN: refine coding style
* Add getOpenCLErrorString
* DNN: Use int32_t/uint32_t instread of alias
* Use namespace ocl4dnn to include libdnn things
* remove extra copyTo in softmax ocl path
Signed-off-by: Li Peng <peng.li@intel.com>
* update ReLU layer ocl path
Signed-off-by: Li Peng <peng.li@intel.com>
* Add prefer target property for layer class
It is used to indicate the target for layer forwarding,
either the default CPU target or OCL target.
Signed-off-by: Li Peng <peng.li@intel.com>
* Add cl_event based timer for cv::ocl
* Rename libdnn to ocl4dnn
Signed-off-by: Li Peng <peng.li@intel.com>
Signed-off-by: wzw <zhiwen.wu@intel.com>
* use UMat for ocl4dnn internal buffer
Remove allocateMemory which use clCreateBuffer directly
Signed-off-by: Li Peng <peng.li@intel.com>
Signed-off-by: wzw <zhiwen.wu@intel.com>
* enable buffer gemm in ocl4dnn innerproduct
Signed-off-by: Li Peng <peng.li@intel.com>
* replace int_tp globally for ocl4dnn kernels.
Signed-off-by: wzw <zhiwen.wu@intel.com>
Signed-off-by: Li Peng <peng.li@intel.com>
* create UMat for layer params
Signed-off-by: Li Peng <peng.li@intel.com>
* update sign ocl kernel
Signed-off-by: Li Peng <peng.li@intel.com>
* update image based gemm of inner product layer
Signed-off-by: Li Peng <peng.li@intel.com>
* remove buffer gemm of inner product layer
call cv::gemm API instead
Signed-off-by: Li Peng <peng.li@intel.com>
* change ocl4dnn forward parameter to UMat
Signed-off-by: Li Peng <peng.li@intel.com>
* Refine auto-tuning mechanism.
- Use OPENCV_OCL4DNN_KERNEL_CONFIG_PATH to set cache directory
for fine-tuned kernel configuration.
e.g. export OPENCV_OCL4DNN_KERNEL_CONFIG_PATH=/home/tmp,
the cache directory will be /home/tmp/spatialkernels/ on Linux.
- Define environment OPENCV_OCL4DNN_ENABLE_AUTO_TUNING to enable
auto-tuning.
- OPENCV_OPENCL_ENABLE_PROFILING is only used to enable profiling
for OpenCL command queue. This fix basic kernel get wrong running
time, i.e. 0ms.
- If creating cache directory failed, disable auto-tuning.
* Detect and create cache dir on windows
Signed-off-by: Li Peng <peng.li@intel.com>
* Refine gemm like convolution kernel.
Signed-off-by: Li Peng <peng.li@intel.com>
* Fix redundant swizzleWeights calling when use cached kernel config.
* Fix "out of resource" bug when auto-tuning too many kernels.
* replace cl_mem with UMat in ocl4dnnConvSpatial class
* OCL4DNN: reduce the tuning kernel candidate.
This patch could reduce 75% of the tuning candidates with less
than 2% performance impact for the final result.
Signed-off-by: Zhigang Gong <zhigang.gong@intel.com>
* replace cl_mem with umat in ocl4dnn convolution
Signed-off-by: Li Peng <peng.li@intel.com>
* remove weight_image_ of ocl4dnn inner product
Actually it is unused in the computation
Signed-off-by: Li Peng <peng.li@intel.com>
* Various fixes for ocl4dnn
1. OCL_PERFORMANCE_CHECK(ocl::Device::getDefault().isIntel())
2. Ptr<OCL4DNNInnerProduct<float> > innerProductOp
3. Code comments cleanup
4. ignore check on OCL cpu device
Signed-off-by: Li Peng <peng.li@intel.com>
* add build option for log softmax
Signed-off-by: Li Peng <peng.li@intel.com>
* remove unused ocl kernels in ocl4dnn
Signed-off-by: Li Peng <peng.li@intel.com>
* replace ocl4dnnSet with opencv setTo
Signed-off-by: Li Peng <peng.li@intel.com>
* replace ALIGN with cv::alignSize
Signed-off-by: Li Peng <peng.li@intel.com>
* check kernel build options
Signed-off-by: Li Peng <peng.li@intel.com>
* Handle program compilation fail properly.
* Use std::numeric_limits<float>::infinity() for large float number
* check ocl4dnn kernel compilation result
Signed-off-by: Li Peng <peng.li@intel.com>
* remove unused ctx_id
Signed-off-by: Li Peng <peng.li@intel.com>
* change clEnqueueNDRangeKernel to kernel.run()
Signed-off-by: Li Peng <peng.li@intel.com>
* change cl_mem to UMat in image based gemm
Signed-off-by: Li Peng <peng.li@intel.com>
* check intel subgroup support for lrn and pooling layer
Signed-off-by: Li Peng <peng.li@intel.com>
* Fix convolution bug if group is greater than 1
Signed-off-by: Li Peng <peng.li@intel.com>
* Set default layer preferableTarget to be DNN_TARGET_CPU
Signed-off-by: Li Peng <peng.li@intel.com>
* Add ocl perf test for convolution
Signed-off-by: Li Peng <peng.li@intel.com>
* Add more ocl accuracy test
Signed-off-by: Li Peng <peng.li@intel.com>
* replace cl_image with ocl::Image2D
Signed-off-by: Li Peng <peng.li@intel.com>
* Fix build failure in elementwise layer
Signed-off-by: Li Peng <peng.li@intel.com>
* use getUMat() to get blob data
Signed-off-by: Li Peng <peng.li@intel.com>
* replace cl_mem handle with ocl::KernelArg
Signed-off-by: Li Peng <peng.li@intel.com>
* dnn(build): don't use C++11, OPENCL_LIBRARIES fix
* dnn(ocl4dnn): remove unused OpenCL kernels
* dnn(ocl4dnn): extract OpenCL code into .cl files
* dnn(ocl4dnn): refine auto-tuning
Defaultly disable auto-tuning, set OPENCV_OCL4DNN_ENABLE_AUTO_TUNING
environment variable to enable it.
Use a set of pre-tuned configs as default config if auto-tuning is disabled.
These configs are tuned for Intel GPU with 48/72 EUs, and for googlenet,
AlexNet, ResNet-50
If default config is not suitable, use the first available kernel config
from the candidates. Candidate priority from high to low is gemm like kernel,
IDLF kernel, basick kernel.
* dnn(ocl4dnn): pooling doesn't use OpenCL subgroups
* dnn(ocl4dnn): fix perf test
OpenCV has default 3sec time limit for each performance test.
Warmup OpenCL backend outside of perf measurement loop.
* use ocl::KernelArg as much as possible
Signed-off-by: Li Peng <peng.li@intel.com>
* dnn(ocl4dnn): fix bias bug for gemm like kernel
* dnn(ocl4dnn): wrap cl_mem into UMat
Signed-off-by: Li Peng <peng.li@intel.com>
* dnn(ocl4dnn): Refine signature of kernel config
- Use more readable string as signture of kernel config
- Don't count device name and vendor in signature string
- Default kernel configurations are tuned for Intel GPU with
24/48/72 EUs, and for googlenet, AlexNet, ResNet-50 net model.
* dnn(ocl4dnn): swap width/height in configuration
* dnn(ocl4dnn): enable configs for Intel OpenCL runtime only
* core: make configuration helper functions accessible from non-core modules
* dnn(ocl4dnn): update kernel auto-tuning behavior
Avoid unwanted creation of directories
* dnn(ocl4dnn): simplify kernel to workaround OpenCL compiler crash
* dnn(ocl4dnn): remove redundant code
* dnn(ocl4dnn): Add more clear message for simd size dismatch.
* dnn(ocl4dnn): add const to const argument
Signed-off-by: Li Peng <peng.li@intel.com>
* dnn(ocl4dnn): force compiler use a specific SIMD size for IDLF kernel
* dnn(ocl4dnn): drop unused tuneLocalSize()
* dnn(ocl4dnn): specify OpenCL queue for Timer and convolve() method
* dnn(ocl4dnn): sanitize file names used for cache
* dnn(perf): enable Network tests with OpenCL
* dnn(ocl4dnn/conv): drop computeGlobalSize()
* dnn(ocl4dnn/conv): drop unused fields
* dnn(ocl4dnn/conv): simplify ctor
* dnn(ocl4dnn/conv): refactor kernelConfig localSize=NULL
* dnn(ocl4dnn/conv): drop unsupported double / untested half types
* dnn(ocl4dnn/conv): drop unused variable
* dnn(ocl4dnn/conv): alignSize/divUp
* dnn(ocl4dnn/conv): use enum values
* dnn(ocl4dnn): drop unused innerproduct variable
Signed-off-by: Li Peng <peng.li@intel.com>
* dnn(ocl4dnn): add an generic function to check cl option support
* dnn(ocl4dnn): run softmax subgroup version kernel first
Signed-off-by: Li Peng <peng.li@intel.com>
2017-10-02 20:38:00 +08:00
|
|
|
#include <opencv2/core/utils/configuration.private.hpp>
|
2017-05-25 23:59:01 +08:00
|
|
|
#include <opencv2/core/utils/trace.private.hpp>
|
|
|
|
|
2018-03-14 20:33:02 +08:00
|
|
|
#include <opencv2/core/utils/logger.hpp>
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
#include <opencv2/core/utils/tls.hpp>
|
|
|
|
#include <opencv2/core/utils/instrumentation.hpp>
|
|
|
|
|
2022-01-11 11:06:43 +08:00
|
|
|
#include <opencv2/core/utils/fp_control_utils.hpp>
|
|
|
|
#include <opencv2/core/utils/fp_control.private.hpp>
|
|
|
|
|
2021-10-08 09:36:58 +08:00
|
|
|
#ifndef OPENCV_WITH_THREAD_SANITIZER
|
|
|
|
#if defined(__clang__) && defined(__has_feature)
|
|
|
|
#if __has_feature(thread_sanitizer)
|
|
|
|
#define OPENCV_WITH_THREAD_SANITIZER 1
|
|
|
|
#include <atomic> // assume C++11
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifndef OPENCV_WITH_THREAD_SANITIZER
|
|
|
|
#define OPENCV_WITH_THREAD_SANITIZER 0
|
|
|
|
#endif
|
|
|
|
|
2015-06-23 19:31:01 +08:00
|
|
|
namespace cv {
|
|
|
|
|
2019-11-22 23:42:25 +08:00
|
|
|
static void _initSystem()
|
|
|
|
{
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
// https://github.com/opencv/opencv/issues/14906
|
|
|
|
// "ios_base::Init" object is not a part of Android's "iostream" header (in case of clang toolchain, NDK 20).
|
|
|
|
// Ref1: https://en.cppreference.com/w/cpp/io/ios_base/Init
|
|
|
|
// The header <iostream> behaves as if it defines (directly or indirectly) an instance of std::ios_base::Init with static storage duration
|
|
|
|
// Ref2: https://github.com/gcc-mirror/gcc/blob/gcc-8-branch/libstdc%2B%2B-v3/include/std/iostream#L73-L74
|
|
|
|
static std::ios_base::Init s_iostream_initializer;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-23 19:31:01 +08:00
|
|
|
static Mutex* __initialization_mutex = NULL;
|
|
|
|
Mutex& getInitializationMutex()
|
|
|
|
{
|
|
|
|
if (__initialization_mutex == NULL)
|
2019-11-22 23:42:25 +08:00
|
|
|
{
|
|
|
|
(void)_initSystem();
|
2015-06-23 19:31:01 +08:00
|
|
|
__initialization_mutex = new Mutex();
|
2019-11-22 23:42:25 +08:00
|
|
|
}
|
2015-06-23 19:31:01 +08:00
|
|
|
return *__initialization_mutex;
|
|
|
|
}
|
|
|
|
// force initialization (single-threaded environment)
|
|
|
|
Mutex* __initialization_mutex_initializer = &getInitializationMutex();
|
|
|
|
|
2018-04-20 17:29:12 +08:00
|
|
|
static bool param_dumpErrors = utils::getConfigurationParameterBool("OPENCV_DUMP_ERRORS",
|
|
|
|
#if defined(_DEBUG) || defined(__ANDROID__) || (defined(__GNUC__) && !defined(__EXCEPTIONS))
|
|
|
|
true
|
|
|
|
#else
|
|
|
|
false
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
|
2018-10-05 23:23:05 +08:00
|
|
|
void* allocSingletonBuffer(size_t size) { return fastMalloc(size); }
|
2018-10-01 21:28:17 +08:00
|
|
|
void* allocSingletonNewBuffer(size_t size) { return malloc(size); }
|
|
|
|
|
2018-10-05 23:23:05 +08:00
|
|
|
|
2015-06-23 19:31:01 +08:00
|
|
|
} // namespace cv
|
|
|
|
|
2018-04-23 20:06:43 +08:00
|
|
|
#ifndef CV_ERROR_SET_TERMINATE_HANDLER // build config option
|
|
|
|
# if defined(_WIN32)
|
|
|
|
# define CV_ERROR_SET_TERMINATE_HANDLER 1
|
|
|
|
# endif
|
|
|
|
#endif
|
2018-04-24 00:02:39 +08:00
|
|
|
#if defined(CV_ERROR_SET_TERMINATE_HANDLER) && !CV_ERROR_SET_TERMINATE_HANDLER
|
2018-04-23 20:06:43 +08:00
|
|
|
# undef CV_ERROR_SET_TERMINATE_HANDLER
|
|
|
|
#endif
|
|
|
|
|
2013-08-21 18:38:20 +08:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
# if _MSC_VER >= 1700
|
|
|
|
# pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
2018-04-23 20:06:43 +08:00
|
|
|
#ifdef CV_ERROR_SET_TERMINATE_HANDLER
|
|
|
|
#include <exception> // std::set_terminate
|
|
|
|
#include <cstdlib> // std::abort
|
|
|
|
#endif
|
|
|
|
|
2021-07-14 03:40:15 +08:00
|
|
|
#if defined __ANDROID__ || defined __unix__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __HAIKU__
|
2014-12-30 21:53:19 +08:00
|
|
|
# include <unistd.h>
|
|
|
|
# include <fcntl.h>
|
2021-10-19 21:30:27 +08:00
|
|
|
#if defined __QNX__
|
2021-07-14 03:40:15 +08:00
|
|
|
# include <sys/elf.h>
|
|
|
|
#else
|
2014-12-30 21:53:19 +08:00
|
|
|
# include <elf.h>
|
2021-07-14 03:40:15 +08:00
|
|
|
#endif
|
2017-07-10 17:43:59 +08:00
|
|
|
#if defined __ANDROID__ || defined __linux__
|
2014-12-30 21:53:19 +08:00
|
|
|
# include <linux/auxvec.h>
|
|
|
|
#endif
|
2015-11-13 16:03:34 +08:00
|
|
|
#endif
|
2014-12-30 21:53:19 +08:00
|
|
|
|
2017-07-10 17:43:59 +08:00
|
|
|
#if defined __ANDROID__ && defined HAVE_CPUFEATURES
|
2017-05-19 21:14:01 +08:00
|
|
|
# include <cpu-features.h>
|
|
|
|
#endif
|
|
|
|
|
2018-11-20 20:05:20 +08:00
|
|
|
|
2021-07-14 03:40:15 +08:00
|
|
|
#if (defined __ppc64__ || defined __PPC64__) && defined __unix__
|
2018-11-20 20:05:20 +08:00
|
|
|
# include "sys/auxv.h"
|
|
|
|
# ifndef AT_HWCAP2
|
|
|
|
# define AT_HWCAP2 26
|
|
|
|
# endif
|
2021-03-12 10:02:31 +08:00
|
|
|
# ifndef PPC_FEATURE2_ARCH_2_07
|
|
|
|
# define PPC_FEATURE2_ARCH_2_07 0x80000000
|
|
|
|
# endif
|
2018-11-20 20:05:20 +08:00
|
|
|
# ifndef PPC_FEATURE2_ARCH_3_00
|
|
|
|
# define PPC_FEATURE2_ARCH_3_00 0x00800000
|
2017-10-03 06:54:31 +08:00
|
|
|
# endif
|
2021-10-14 00:19:57 +08:00
|
|
|
# ifndef PPC_FEATURE_HAS_VSX
|
|
|
|
# define PPC_FEATURE_HAS_VSX 0x00000080
|
|
|
|
# endif
|
2017-10-03 06:54:31 +08:00
|
|
|
#endif
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined _WIN32 || defined WINCE
|
2012-11-01 16:44:10 +08:00
|
|
|
#ifndef _WIN32_WINNT // This is needed for the declaration of TryEnterCriticalSection in winbase.h with Visual Studio 2005 (and older?)
|
|
|
|
#define _WIN32_WINNT 0x0400 // http://msdn.microsoft.com/en-us/library/ms686857(VS.85).aspx
|
|
|
|
#endif
|
|
|
|
#include <windows.h>
|
2013-07-19 17:43:05 +08:00
|
|
|
#if (_WIN32_WINNT >= 0x0602)
|
2013-07-23 21:44:57 +08:00
|
|
|
#include <synchapi.h>
|
2013-07-19 17:43:05 +08:00
|
|
|
#endif
|
2019-11-02 03:33:12 +08:00
|
|
|
#if ((_WIN32_WINNT >= 0x0600) && !defined(CV_DISABLE_FLS)) || defined(CV_FORCE_FLS)
|
|
|
|
#include <fibersapi.h>
|
|
|
|
#define CV_USE_FLS
|
|
|
|
#endif
|
2012-11-01 16:44:10 +08:00
|
|
|
#undef small
|
|
|
|
#undef min
|
|
|
|
#undef max
|
|
|
|
#undef abs
|
2010-05-12 01:44:00 +08:00
|
|
|
#include <tchar.h>
|
2013-07-19 17:43:05 +08:00
|
|
|
|
2015-02-21 00:47:45 +08:00
|
|
|
#ifdef WINRT
|
2013-07-19 17:43:05 +08:00
|
|
|
#include <wrl/client.h>
|
2013-11-18 20:25:50 +08:00
|
|
|
#ifndef __cplusplus_winrt
|
|
|
|
#include <windows.storage.h>
|
|
|
|
#pragma comment(lib, "runtimeobject.lib")
|
2019-11-02 03:33:12 +08:00
|
|
|
#endif // WINRT
|
2013-07-19 17:43:05 +08:00
|
|
|
|
|
|
|
std::wstring GetTempPathWinRT()
|
|
|
|
{
|
2013-11-18 20:25:50 +08:00
|
|
|
#ifdef __cplusplus_winrt
|
2013-07-19 17:43:05 +08:00
|
|
|
return std::wstring(Windows::Storage::ApplicationData::Current->TemporaryFolder->Path->Data());
|
2013-11-18 20:25:50 +08:00
|
|
|
#else
|
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IApplicationDataStatics> appdataFactory;
|
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IApplicationData> appdataRef;
|
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFolder> storagefolderRef;
|
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageItem> storageitemRef;
|
|
|
|
HSTRING str;
|
|
|
|
HSTRING_HEADER hstrHead;
|
|
|
|
std::wstring wstr;
|
|
|
|
if (FAILED(WindowsCreateStringReference(RuntimeClass_Windows_Storage_ApplicationData,
|
|
|
|
(UINT32)wcslen(RuntimeClass_Windows_Storage_ApplicationData), &hstrHead, &str)))
|
|
|
|
return wstr;
|
2013-12-23 00:21:51 +08:00
|
|
|
if (FAILED(RoGetActivationFactory(str, IID_PPV_ARGS(appdataFactory.ReleaseAndGetAddressOf()))))
|
2013-11-18 20:25:50 +08:00
|
|
|
return wstr;
|
|
|
|
if (FAILED(appdataFactory->get_Current(appdataRef.ReleaseAndGetAddressOf())))
|
|
|
|
return wstr;
|
|
|
|
if (FAILED(appdataRef->get_TemporaryFolder(storagefolderRef.ReleaseAndGetAddressOf())))
|
|
|
|
return wstr;
|
|
|
|
if (FAILED(storagefolderRef.As(&storageitemRef)))
|
|
|
|
return wstr;
|
|
|
|
str = NULL;
|
|
|
|
if (FAILED(storageitemRef->get_Path(&str)))
|
|
|
|
return wstr;
|
|
|
|
wstr = WindowsGetStringRawBuffer(str, NULL);
|
|
|
|
WindowsDeleteString(str);
|
|
|
|
return wstr;
|
|
|
|
#endif
|
2013-07-19 17:43:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring GetTempFileNameWinRT(std::wstring prefix)
|
|
|
|
{
|
2013-07-29 19:38:18 +08:00
|
|
|
wchar_t guidStr[40];
|
|
|
|
GUID g;
|
|
|
|
CoCreateGuid(&g);
|
2013-07-19 17:43:05 +08:00
|
|
|
wchar_t* mask = L"%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x";
|
2013-07-29 19:38:18 +08:00
|
|
|
swprintf(&guidStr[0], sizeof(guidStr)/sizeof(wchar_t), mask,
|
|
|
|
g.Data1, g.Data2, g.Data3, UINT(g.Data4[0]), UINT(g.Data4[1]),
|
|
|
|
UINT(g.Data4[2]), UINT(g.Data4[3]), UINT(g.Data4[4]),
|
|
|
|
UINT(g.Data4[5]), UINT(g.Data4[6]), UINT(g.Data4[7]));
|
2013-07-19 17:43:05 +08:00
|
|
|
|
2015-02-21 00:47:45 +08:00
|
|
|
return prefix.append(std::wstring(guidStr));
|
2013-07-19 17:43:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2010-05-12 01:44:00 +08:00
|
|
|
#else
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
2011-06-13 04:29:50 +08:00
|
|
|
#if defined __MACH__ && defined __APPLE__
|
2010-05-12 01:44:00 +08:00
|
|
|
#include <mach/mach.h>
|
|
|
|
#include <mach/mach_time.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _OPENMP
|
|
|
|
#include "omp.h"
|
|
|
|
#endif
|
|
|
|
|
2021-07-14 03:40:15 +08:00
|
|
|
#if defined __unix__ || defined __APPLE__ || defined __EMSCRIPTEN__ || defined __FreeBSD__ || defined __GLIBC__ || defined __HAIKU__
|
2011-08-02 20:42:58 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
2012-06-08 01:21:29 +08:00
|
|
|
#include <sys/types.h>
|
2017-07-10 17:43:59 +08:00
|
|
|
#if defined __ANDROID__
|
2011-08-02 22:56:51 +08:00
|
|
|
#include <sys/sysconf.h>
|
2011-08-02 20:42:58 +08:00
|
|
|
#endif
|
2011-08-02 22:56:51 +08:00
|
|
|
#endif
|
2011-08-02 20:42:58 +08:00
|
|
|
|
2017-07-10 17:43:59 +08:00
|
|
|
#ifdef __ANDROID__
|
2012-08-08 20:39:24 +08:00
|
|
|
# include <android/log.h>
|
|
|
|
#endif
|
|
|
|
|
2018-02-13 03:34:18 +08:00
|
|
|
#ifdef DECLARE_CV_CPUID_X86
|
|
|
|
DECLARE_CV_CPUID_X86
|
|
|
|
#endif
|
|
|
|
#ifndef CV_CPUID_X86
|
|
|
|
#if defined _MSC_VER && (defined _M_IX86 || defined _M_X64)
|
|
|
|
#if _MSC_VER >= 1400 // MSVS 2005
|
|
|
|
#include <intrin.h> // __cpuidex()
|
|
|
|
#define CV_CPUID_X86 __cpuidex
|
|
|
|
#else
|
|
|
|
#error "Required MSVS 2005+"
|
|
|
|
#endif
|
|
|
|
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
|
|
|
static void cv_cpuid(int* cpuid_data, int reg_eax, int reg_ecx)
|
|
|
|
{
|
|
|
|
int __eax = reg_eax, __ebx = 0, __ecx = reg_ecx, __edx = 0;
|
|
|
|
// tested with available compilers (-fPIC -O2 -m32/-m64): https://godbolt.org/
|
|
|
|
#if !defined(__PIC__) \
|
|
|
|
|| defined(__x86_64__) || __GNUC__ >= 5 \
|
|
|
|
|| defined(__clang__) || defined(__INTEL_COMPILER)
|
|
|
|
__asm__("cpuid\n\t"
|
|
|
|
: "+a" (__eax), "=b" (__ebx), "+c" (__ecx), "=d" (__edx)
|
|
|
|
);
|
|
|
|
#elif defined(__i386__) // ebx may be reserved as the PIC register
|
|
|
|
__asm__("xchg{l}\t{%%}ebx, %1\n\t"
|
|
|
|
"cpuid\n\t"
|
|
|
|
"xchg{l}\t{%%}ebx, %1\n\t"
|
|
|
|
: "+a" (__eax), "=&r" (__ebx), "+c" (__ecx), "=d" (__edx)
|
|
|
|
);
|
|
|
|
#else
|
|
|
|
#error "Configuration error"
|
|
|
|
#endif
|
|
|
|
cpuid_data[0] = __eax; cpuid_data[1] = __ebx; cpuid_data[2] = __ecx; cpuid_data[3] = __edx;
|
|
|
|
}
|
|
|
|
#define CV_CPUID_X86 cv_cpuid
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
namespace cv
|
|
|
|
{
|
|
|
|
|
2011-06-09 05:35:19 +08:00
|
|
|
Exception::Exception() { code = 0; line = 0; }
|
|
|
|
|
2013-03-23 00:37:49 +08:00
|
|
|
Exception::Exception(int _code, const String& _err, const String& _func, const String& _file, int _line)
|
2011-06-09 05:35:19 +08:00
|
|
|
: code(_code), err(_err), func(_func), file(_file), line(_line)
|
|
|
|
{
|
|
|
|
formatMessage();
|
|
|
|
}
|
|
|
|
|
|
|
|
Exception::~Exception() throw() {}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\return the error description and the context as a text string.
|
2012-06-08 01:21:29 +08:00
|
|
|
*/
|
2011-06-09 05:35:19 +08:00
|
|
|
const char* Exception::what() const throw() { return msg.c_str(); }
|
|
|
|
|
|
|
|
void Exception::formatMessage()
|
|
|
|
{
|
2018-02-23 17:24:44 +08:00
|
|
|
size_t pos = err.find('\n');
|
|
|
|
bool multiline = pos != cv::String::npos;
|
|
|
|
if (multiline)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
size_t prev_pos = 0;
|
|
|
|
while (pos != cv::String::npos)
|
|
|
|
{
|
|
|
|
ss << "> " << err.substr(prev_pos, pos - prev_pos) << std::endl;
|
|
|
|
prev_pos = pos + 1;
|
|
|
|
pos = err.find('\n', prev_pos);
|
|
|
|
}
|
|
|
|
ss << "> " << err.substr(prev_pos);
|
|
|
|
if (err[err.size() - 1] != '\n')
|
|
|
|
ss << std::endl;
|
|
|
|
err = ss.str();
|
|
|
|
}
|
|
|
|
if (func.size() > 0)
|
|
|
|
{
|
|
|
|
if (multiline)
|
|
|
|
msg = format("OpenCV(%s) %s:%d: error: (%d:%s) in function '%s'\n%s", CV_VERSION, file.c_str(), line, code, cvErrorStr(code), func.c_str(), err.c_str());
|
|
|
|
else
|
|
|
|
msg = format("OpenCV(%s) %s:%d: error: (%d:%s) %s in function '%s'\n", CV_VERSION, file.c_str(), line, code, cvErrorStr(code), err.c_str(), func.c_str());
|
|
|
|
}
|
2011-06-09 05:35:19 +08:00
|
|
|
else
|
2018-02-23 17:24:44 +08:00
|
|
|
{
|
|
|
|
msg = format("OpenCV(%s) %s:%d: error: (%d:%s) %s%s", CV_VERSION, file.c_str(), line, code, cvErrorStr(code), err.c_str(), multiline ? "" : "\n");
|
|
|
|
}
|
2011-06-09 05:35:19 +08:00
|
|
|
}
|
2012-06-08 01:21:29 +08:00
|
|
|
|
2016-09-07 23:02:36 +08:00
|
|
|
static const char* g_hwFeatureNames[CV_HARDWARE_MAX_FEATURE] = { NULL };
|
|
|
|
|
|
|
|
static const char* getHWFeatureName(int id)
|
|
|
|
{
|
|
|
|
return (id < CV_HARDWARE_MAX_FEATURE) ? g_hwFeatureNames[id] : NULL;
|
|
|
|
}
|
|
|
|
static const char* getHWFeatureNameSafe(int id)
|
|
|
|
{
|
|
|
|
const char* name = getHWFeatureName(id);
|
|
|
|
return name ? name : "Unknown feature";
|
|
|
|
}
|
|
|
|
|
2015-12-15 20:55:43 +08:00
|
|
|
struct HWFeatures
|
|
|
|
{
|
|
|
|
enum { MAX_FEATURE = CV_HARDWARE_MAX_FEATURE };
|
|
|
|
|
2016-09-07 23:02:36 +08:00
|
|
|
HWFeatures(bool run_initialize = false)
|
2015-12-15 20:55:43 +08:00
|
|
|
{
|
2016-09-07 23:02:36 +08:00
|
|
|
memset( have, 0, sizeof(have[0]) * MAX_FEATURE );
|
|
|
|
if (run_initialize)
|
|
|
|
initialize();
|
2015-12-15 20:55:43 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 23:02:36 +08:00
|
|
|
static void initializeNames()
|
2015-12-15 20:55:43 +08:00
|
|
|
{
|
2016-09-07 23:02:36 +08:00
|
|
|
for (int i = 0; i < CV_HARDWARE_MAX_FEATURE; i++)
|
|
|
|
{
|
|
|
|
g_hwFeatureNames[i] = 0;
|
|
|
|
}
|
|
|
|
g_hwFeatureNames[CPU_MMX] = "MMX";
|
|
|
|
g_hwFeatureNames[CPU_SSE] = "SSE";
|
|
|
|
g_hwFeatureNames[CPU_SSE2] = "SSE2";
|
|
|
|
g_hwFeatureNames[CPU_SSE3] = "SSE3";
|
|
|
|
g_hwFeatureNames[CPU_SSSE3] = "SSSE3";
|
|
|
|
g_hwFeatureNames[CPU_SSE4_1] = "SSE4.1";
|
|
|
|
g_hwFeatureNames[CPU_SSE4_2] = "SSE4.2";
|
|
|
|
g_hwFeatureNames[CPU_POPCNT] = "POPCNT";
|
|
|
|
g_hwFeatureNames[CPU_FP16] = "FP16";
|
|
|
|
g_hwFeatureNames[CPU_AVX] = "AVX";
|
|
|
|
g_hwFeatureNames[CPU_AVX2] = "AVX2";
|
|
|
|
g_hwFeatureNames[CPU_FMA3] = "FMA3";
|
|
|
|
|
|
|
|
g_hwFeatureNames[CPU_AVX_512F] = "AVX512F";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512BW] = "AVX512BW";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512CD] = "AVX512CD";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512DQ] = "AVX512DQ";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512ER] = "AVX512ER";
|
2017-12-29 13:06:52 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX_512IFMA] = "AVX512IFMA";
|
2016-09-07 23:02:36 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX_512PF] = "AVX512PF";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512VBMI] = "AVX512VBMI";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512VL] = "AVX512VL";
|
2019-05-05 19:19:49 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX_512VBMI2] = "AVX512VBMI2";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512VNNI] = "AVX512VNNI";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512BITALG] = "AVX512BITALG";
|
|
|
|
g_hwFeatureNames[CPU_AVX_512VPOPCNTDQ] = "AVX512VPOPCNTDQ";
|
|
|
|
g_hwFeatureNames[CPU_AVX_5124VNNIW] = "AVX5124VNNIW";
|
|
|
|
g_hwFeatureNames[CPU_AVX_5124FMAPS] = "AVX5124FMAPS";
|
2016-09-07 23:02:36 +08:00
|
|
|
|
|
|
|
g_hwFeatureNames[CPU_NEON] = "NEON";
|
2017-10-03 06:54:31 +08:00
|
|
|
|
|
|
|
g_hwFeatureNames[CPU_VSX] = "VSX";
|
2018-11-20 20:05:20 +08:00
|
|
|
g_hwFeatureNames[CPU_VSX3] = "VSX3";
|
2017-12-29 13:06:52 +08:00
|
|
|
|
2019-09-21 00:52:48 +08:00
|
|
|
g_hwFeatureNames[CPU_MSA] = "CPU_MSA";
|
|
|
|
|
2019-10-05 18:39:35 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX512_COMMON] = "AVX512-COMMON";
|
2017-12-29 13:06:52 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX512_SKX] = "AVX512-SKX";
|
2019-05-05 19:19:49 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX512_KNL] = "AVX512-KNL";
|
|
|
|
g_hwFeatureNames[CPU_AVX512_KNM] = "AVX512-KNM";
|
|
|
|
g_hwFeatureNames[CPU_AVX512_CNL] = "AVX512-CNL";
|
2019-10-05 18:39:35 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX512_CLX] = "AVX512-CLX";
|
2019-05-05 19:19:49 +08:00
|
|
|
g_hwFeatureNames[CPU_AVX512_ICL] = "AVX512-ICL";
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void initialize(void)
|
|
|
|
{
|
2019-07-25 04:12:09 +08:00
|
|
|
#ifndef NO_GETENV
|
2016-09-07 23:02:36 +08:00
|
|
|
if (getenv("OPENCV_DUMP_CONFIG"))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "\nOpenCV build configuration is:\n%s\n",
|
|
|
|
cv::getBuildInformation().c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
initializeNames();
|
|
|
|
|
2018-02-13 03:34:18 +08:00
|
|
|
#ifdef CV_CPUID_X86
|
2015-12-15 20:55:43 +08:00
|
|
|
int cpuid_data[4] = { 0, 0, 0, 0 };
|
2016-09-07 23:02:36 +08:00
|
|
|
int cpuid_data_ex[4] = { 0, 0, 0, 0 };
|
2015-12-15 20:55:43 +08:00
|
|
|
|
2018-02-13 03:34:18 +08:00
|
|
|
CV_CPUID_X86(cpuid_data, 1, 0/*unused*/);
|
2015-12-15 20:55:43 +08:00
|
|
|
|
2016-09-07 23:02:36 +08:00
|
|
|
int x86_family = (cpuid_data[0] >> 8) & 15;
|
|
|
|
if( x86_family >= 6 )
|
2015-12-15 20:55:43 +08:00
|
|
|
{
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_MMX] = (cpuid_data[3] & (1<<23)) != 0;
|
|
|
|
have[CV_CPU_SSE] = (cpuid_data[3] & (1<<25)) != 0;
|
|
|
|
have[CV_CPU_SSE2] = (cpuid_data[3] & (1<<26)) != 0;
|
|
|
|
have[CV_CPU_SSE3] = (cpuid_data[2] & (1<<0)) != 0;
|
|
|
|
have[CV_CPU_SSSE3] = (cpuid_data[2] & (1<<9)) != 0;
|
|
|
|
have[CV_CPU_FMA3] = (cpuid_data[2] & (1<<12)) != 0;
|
|
|
|
have[CV_CPU_SSE4_1] = (cpuid_data[2] & (1<<19)) != 0;
|
|
|
|
have[CV_CPU_SSE4_2] = (cpuid_data[2] & (1<<20)) != 0;
|
|
|
|
have[CV_CPU_POPCNT] = (cpuid_data[2] & (1<<23)) != 0;
|
|
|
|
have[CV_CPU_AVX] = (cpuid_data[2] & (1<<28)) != 0;
|
|
|
|
have[CV_CPU_FP16] = (cpuid_data[2] & (1<<29)) != 0;
|
2015-12-15 20:55:43 +08:00
|
|
|
|
|
|
|
// make the second call to the cpuid command in order to get
|
|
|
|
// information about extended features like AVX2
|
2018-02-13 03:34:18 +08:00
|
|
|
CV_CPUID_X86(cpuid_data_ex, 7, 0);
|
2016-09-07 23:02:36 +08:00
|
|
|
|
|
|
|
have[CV_CPU_AVX2] = (cpuid_data_ex[1] & (1<<5)) != 0;
|
|
|
|
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX_512F] = (cpuid_data_ex[1] & (1<<16)) != 0;
|
|
|
|
have[CV_CPU_AVX_512DQ] = (cpuid_data_ex[1] & (1<<17)) != 0;
|
|
|
|
have[CV_CPU_AVX_512IFMA] = (cpuid_data_ex[1] & (1<<21)) != 0;
|
|
|
|
have[CV_CPU_AVX_512PF] = (cpuid_data_ex[1] & (1<<26)) != 0;
|
|
|
|
have[CV_CPU_AVX_512ER] = (cpuid_data_ex[1] & (1<<27)) != 0;
|
|
|
|
have[CV_CPU_AVX_512CD] = (cpuid_data_ex[1] & (1<<28)) != 0;
|
|
|
|
have[CV_CPU_AVX_512BW] = (cpuid_data_ex[1] & (1<<30)) != 0;
|
|
|
|
have[CV_CPU_AVX_512VL] = (cpuid_data_ex[1] & (1<<31)) != 0;
|
|
|
|
have[CV_CPU_AVX_512VBMI] = (cpuid_data_ex[2] & (1<<1)) != 0;
|
|
|
|
have[CV_CPU_AVX_512VBMI2] = (cpuid_data_ex[2] & (1<<6)) != 0;
|
|
|
|
have[CV_CPU_AVX_512VNNI] = (cpuid_data_ex[2] & (1<<11)) != 0;
|
|
|
|
have[CV_CPU_AVX_512BITALG] = (cpuid_data_ex[2] & (1<<12)) != 0;
|
|
|
|
have[CV_CPU_AVX_512VPOPCNTDQ] = (cpuid_data_ex[2] & (1<<14)) != 0;
|
|
|
|
have[CV_CPU_AVX_5124VNNIW] = (cpuid_data_ex[3] & (1<<2)) != 0;
|
|
|
|
have[CV_CPU_AVX_5124FMAPS] = (cpuid_data_ex[3] & (1<<3)) != 0;
|
2016-09-07 23:02:36 +08:00
|
|
|
|
|
|
|
bool have_AVX_OS_support = true;
|
|
|
|
bool have_AVX512_OS_support = true;
|
|
|
|
if (!(cpuid_data[2] & (1<<27)))
|
|
|
|
have_AVX_OS_support = false; // OS uses XSAVE_XRSTORE and CPU support AVX
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int xcr0 = 0;
|
|
|
|
#ifdef _XCR_XFEATURE_ENABLED_MASK // requires immintrin.h
|
|
|
|
xcr0 = (int)_xgetbv(_XCR_XFEATURE_ENABLED_MASK);
|
|
|
|
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
2018-02-13 03:34:18 +08:00
|
|
|
__asm__ ("xgetbv\n\t" : "=a" (xcr0) : "c" (0) : "%edx" );
|
2016-09-07 23:02:36 +08:00
|
|
|
#endif
|
|
|
|
if ((xcr0 & 0x6) != 0x6)
|
|
|
|
have_AVX_OS_support = false; // YMM registers
|
|
|
|
if ((xcr0 & 0xe6) != 0xe6)
|
|
|
|
have_AVX512_OS_support = false; // ZMM registers
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_AVX_OS_support)
|
|
|
|
{
|
|
|
|
have[CV_CPU_AVX] = false;
|
|
|
|
have[CV_CPU_FP16] = false;
|
|
|
|
have[CV_CPU_AVX2] = false;
|
|
|
|
have[CV_CPU_FMA3] = false;
|
|
|
|
}
|
|
|
|
if (!have_AVX_OS_support || !have_AVX512_OS_support)
|
|
|
|
{
|
|
|
|
have[CV_CPU_AVX_512F] = false;
|
|
|
|
have[CV_CPU_AVX_512BW] = false;
|
|
|
|
have[CV_CPU_AVX_512CD] = false;
|
|
|
|
have[CV_CPU_AVX_512DQ] = false;
|
|
|
|
have[CV_CPU_AVX_512ER] = false;
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX_512IFMA] = false;
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_AVX_512PF] = false;
|
|
|
|
have[CV_CPU_AVX_512VBMI] = false;
|
|
|
|
have[CV_CPU_AVX_512VL] = false;
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX_512VBMI2] = false;
|
|
|
|
have[CV_CPU_AVX_512VNNI] = false;
|
|
|
|
have[CV_CPU_AVX_512BITALG] = false;
|
|
|
|
have[CV_CPU_AVX_512VPOPCNTDQ] = false;
|
|
|
|
have[CV_CPU_AVX_5124VNNIW] = false;
|
|
|
|
have[CV_CPU_AVX_5124FMAPS] = false;
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
2017-12-29 13:06:52 +08:00
|
|
|
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX512_COMMON] = have[CV_CPU_AVX_512F] && have[CV_CPU_AVX_512CD];
|
|
|
|
if (have[CV_CPU_AVX512_COMMON])
|
2017-12-29 13:06:52 +08:00
|
|
|
{
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX512_KNL] = have[CV_CPU_AVX_512ER] && have[CV_CPU_AVX_512PF];
|
|
|
|
have[CV_CPU_AVX512_KNM] = have[CV_CPU_AVX512_KNL] && have[CV_CPU_AVX_5124FMAPS] &&
|
|
|
|
have[CV_CPU_AVX_5124VNNIW] && have[CV_CPU_AVX_512VPOPCNTDQ];
|
|
|
|
have[CV_CPU_AVX512_SKX] = have[CV_CPU_AVX_512BW] && have[CV_CPU_AVX_512DQ] && have[CV_CPU_AVX_512VL];
|
|
|
|
have[CV_CPU_AVX512_CNL] = have[CV_CPU_AVX512_SKX] && have[CV_CPU_AVX_512IFMA] && have[CV_CPU_AVX_512VBMI];
|
2019-10-05 18:39:35 +08:00
|
|
|
have[CV_CPU_AVX512_CLX] = have[CV_CPU_AVX512_SKX] && have[CV_CPU_AVX_512VNNI];
|
|
|
|
have[CV_CPU_AVX512_ICL] = have[CV_CPU_AVX512_SKX] &&
|
|
|
|
have[CV_CPU_AVX_512IFMA] && have[CV_CPU_AVX_512VBMI] &&
|
|
|
|
have[CV_CPU_AVX_512VNNI] &&
|
|
|
|
have[CV_CPU_AVX_512VBMI2] && have[CV_CPU_AVX_512BITALG] && have[CV_CPU_AVX_512VPOPCNTDQ];
|
2019-05-05 19:19:49 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
have[CV_CPU_AVX512_KNL] = false;
|
|
|
|
have[CV_CPU_AVX512_KNM] = false;
|
|
|
|
have[CV_CPU_AVX512_SKX] = false;
|
|
|
|
have[CV_CPU_AVX512_CNL] = false;
|
2019-10-05 18:39:35 +08:00
|
|
|
have[CV_CPU_AVX512_CLX] = false;
|
2019-05-05 19:19:49 +08:00
|
|
|
have[CV_CPU_AVX512_ICL] = false;
|
2017-12-29 13:06:52 +08:00
|
|
|
}
|
2015-12-15 20:55:43 +08:00
|
|
|
}
|
2018-02-13 03:34:18 +08:00
|
|
|
#endif // CV_CPUID_X86
|
2015-12-15 20:55:43 +08:00
|
|
|
|
2021-10-19 21:30:27 +08:00
|
|
|
#if defined __ANDROID__ || defined __linux__ || defined __FreeBSD__ || defined __QNX__
|
2015-12-15 20:55:43 +08:00
|
|
|
#ifdef __aarch64__
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_NEON] = true;
|
|
|
|
have[CV_CPU_FP16] = true;
|
2017-06-09 23:24:14 +08:00
|
|
|
#elif defined __arm__ && defined __ANDROID__
|
2017-06-13 00:10:13 +08:00
|
|
|
#if defined HAVE_CPUFEATURES
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "calling android_getCpuFeatures() ...");
|
2017-06-09 23:24:14 +08:00
|
|
|
uint64_t features = android_getCpuFeatures();
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, cv::format("calling android_getCpuFeatures() ... Done (%llx)", (long long)features));
|
2017-06-09 23:24:14 +08:00
|
|
|
have[CV_CPU_NEON] = (features & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
|
|
|
|
have[CV_CPU_FP16] = (features & ANDROID_CPU_ARM_FEATURE_VFP_FP16) != 0;
|
2017-06-13 00:10:13 +08:00
|
|
|
#else
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "cpufeatures library is not available for CPU detection");
|
2017-06-13 00:10:13 +08:00
|
|
|
#if CV_NEON
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "- NEON instructions is enabled via build flags");
|
2017-06-13 00:10:13 +08:00
|
|
|
have[CV_CPU_NEON] = true;
|
|
|
|
#else
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "- NEON instructions is NOT enabled via build flags");
|
2017-06-13 00:10:13 +08:00
|
|
|
#endif
|
|
|
|
#if CV_FP16
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "- FP16 instructions is enabled via build flags");
|
2017-06-13 00:10:13 +08:00
|
|
|
have[CV_CPU_FP16] = true;
|
|
|
|
#else
|
2018-03-14 20:33:02 +08:00
|
|
|
CV_LOG_INFO(NULL, "- FP16 instructions is NOT enabled via build flags");
|
2017-06-13 00:10:13 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
2021-09-22 01:46:33 +08:00
|
|
|
#elif defined __arm__ && !defined __FreeBSD__
|
2015-12-15 20:55:43 +08:00
|
|
|
int cpufile = open("/proc/self/auxv", O_RDONLY);
|
|
|
|
|
|
|
|
if (cpufile >= 0)
|
|
|
|
{
|
|
|
|
Elf32_auxv_t auxv;
|
|
|
|
const size_t size_auxv_t = sizeof(auxv);
|
|
|
|
|
|
|
|
while ((size_t)read(cpufile, &auxv, size_auxv_t) == size_auxv_t)
|
|
|
|
{
|
|
|
|
if (auxv.a_type == AT_HWCAP)
|
|
|
|
{
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_NEON] = (auxv.a_un.a_val & 4096) != 0;
|
|
|
|
have[CV_CPU_FP16] = (auxv.a_un.a_val & 2) != 0;
|
2015-12-15 20:55:43 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close(cpufile);
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-21 20:31:33 +08:00
|
|
|
#elif (defined __clang__ || defined __APPLE__)
|
|
|
|
#if (defined __ARM_NEON__ || (defined __ARM_NEON && defined __aarch64__))
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_NEON] = true;
|
2016-05-21 20:31:33 +08:00
|
|
|
#endif
|
|
|
|
#if (defined __ARM_FP && (((__ARM_FP & 0x2) != 0) && defined __ARM_NEON__))
|
2016-09-07 23:02:36 +08:00
|
|
|
have[CV_CPU_FP16] = true;
|
2016-05-21 20:31:33 +08:00
|
|
|
#endif
|
2015-12-15 20:55:43 +08:00
|
|
|
#endif
|
2019-08-09 23:01:37 +08:00
|
|
|
#if defined _ARM_ && (defined(_WIN32_WCE) && _WIN32_WCE >= 0x800)
|
|
|
|
have[CV_CPU_NEON] = true;
|
|
|
|
#endif
|
2019-09-21 00:52:48 +08:00
|
|
|
#ifdef __mips_msa
|
|
|
|
have[CV_CPU_MSA] = true;
|
|
|
|
#endif
|
2021-03-12 10:02:31 +08:00
|
|
|
|
2021-10-14 00:19:57 +08:00
|
|
|
#if (defined __ppc64__ || defined __PPC64__) && defined __linux__
|
2021-03-12 10:02:31 +08:00
|
|
|
unsigned int hwcap = getauxval(AT_HWCAP);
|
|
|
|
if (hwcap & PPC_FEATURE_HAS_VSX) {
|
|
|
|
hwcap = getauxval(AT_HWCAP2);
|
|
|
|
if (hwcap & PPC_FEATURE2_ARCH_3_00) {
|
|
|
|
have[CV_CPU_VSX] = have[CV_CPU_VSX3] = true;
|
|
|
|
} else {
|
|
|
|
have[CV_CPU_VSX] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0;
|
|
|
|
}
|
|
|
|
}
|
2021-10-14 00:19:57 +08:00
|
|
|
#elif (defined __ppc64__ || defined __PPC64__) && defined __FreeBSD__
|
2022-01-25 21:35:22 +08:00
|
|
|
unsigned long hwcap = 0;
|
2021-10-14 00:19:57 +08:00
|
|
|
elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap));
|
|
|
|
if (hwcap & PPC_FEATURE_HAS_VSX) {
|
|
|
|
elf_aux_info(AT_HWCAP2, &hwcap, sizeof(hwcap));
|
|
|
|
if (hwcap & PPC_FEATURE2_ARCH_3_00) {
|
|
|
|
have[CV_CPU_VSX] = have[CV_CPU_VSX3] = true;
|
|
|
|
} else {
|
|
|
|
have[CV_CPU_VSX] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0;
|
|
|
|
}
|
|
|
|
}
|
2017-10-03 06:54:31 +08:00
|
|
|
#else
|
2021-10-14 00:19:57 +08:00
|
|
|
// TODO: AIX, OpenBSD
|
2021-03-12 10:02:31 +08:00
|
|
|
#if CV_VSX || defined _ARCH_PWR8 || defined __POWER9_VECTOR__
|
|
|
|
have[CV_CPU_VSX] = true;
|
|
|
|
#endif
|
|
|
|
#if CV_VSX3 || defined __POWER9_VECTOR__
|
|
|
|
have[CV_CPU_VSX3] = true;
|
|
|
|
#endif
|
2017-10-03 06:54:31 +08:00
|
|
|
#endif
|
|
|
|
|
2019-10-05 18:39:35 +08:00
|
|
|
bool skip_baseline_check = false;
|
|
|
|
#ifndef NO_GETENV
|
|
|
|
if (getenv("OPENCV_SKIP_CPU_BASELINE_CHECK"))
|
|
|
|
{
|
|
|
|
skip_baseline_check = true;
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-07 23:02:36 +08:00
|
|
|
int baseline_features[] = { CV_CPU_BASELINE_FEATURES };
|
2019-10-05 18:39:35 +08:00
|
|
|
if (!checkFeatures(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]))
|
|
|
|
&& !skip_baseline_check)
|
2016-09-07 23:02:36 +08:00
|
|
|
{
|
|
|
|
fprintf(stderr, "\n"
|
|
|
|
"******************************************************************\n"
|
|
|
|
"* FATAL ERROR: *\n"
|
|
|
|
"* This OpenCV build doesn't support current CPU/HW configuration *\n"
|
|
|
|
"* *\n"
|
|
|
|
"* Use OPENCV_DUMP_CONFIG=1 environment variable for details *\n"
|
|
|
|
"******************************************************************\n");
|
|
|
|
fprintf(stderr, "\nRequired baseline features:\n");
|
|
|
|
checkFeatures(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]), true);
|
2018-04-24 00:02:39 +08:00
|
|
|
CV_Error(cv::Error::StsAssert, "Missing support for required CPU baseline features. Check OpenCV build configuration and required CPU/HW setup.");
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
readSettings(baseline_features, sizeof(baseline_features) / sizeof(baseline_features[0]));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkFeatures(const int* features, int count, bool dump = false)
|
|
|
|
{
|
|
|
|
bool result = true;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
int feature = features[i];
|
|
|
|
if (feature)
|
|
|
|
{
|
|
|
|
if (have[feature])
|
|
|
|
{
|
2019-10-05 18:39:35 +08:00
|
|
|
if (dump) fprintf(stderr, " ID=%3d (%s) - OK\n", feature, getHWFeatureNameSafe(feature));
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = false;
|
2019-10-05 18:39:35 +08:00
|
|
|
if (dump) fprintf(stderr, " ID=%3d (%s) - NOT AVAILABLE\n", feature, getHWFeatureNameSafe(feature));
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool isSymbolSeparator(char c)
|
|
|
|
{
|
2018-02-18 08:03:04 +08:00
|
|
|
return c == ',' || c == ';';
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void readSettings(const int* baseline_features, int baseline_count)
|
|
|
|
{
|
|
|
|
bool dump = true;
|
|
|
|
const char* disabled_features =
|
2019-07-25 04:12:09 +08:00
|
|
|
#ifdef NO_GETENV
|
2016-09-07 23:02:36 +08:00
|
|
|
NULL;
|
2019-07-25 04:12:09 +08:00
|
|
|
#else
|
|
|
|
getenv("OPENCV_CPU_DISABLE");
|
2016-09-07 23:02:36 +08:00
|
|
|
#endif
|
|
|
|
if (disabled_features && disabled_features[0] != 0)
|
|
|
|
{
|
|
|
|
const char* start = disabled_features;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
while (start[0] != 0 && isSymbolSeparator(start[0]))
|
|
|
|
{
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
if (start[0] == 0)
|
|
|
|
break;
|
|
|
|
const char* end = start;
|
|
|
|
while (end[0] != 0 && !isSymbolSeparator(end[0]))
|
|
|
|
{
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
if (end == start)
|
|
|
|
continue;
|
|
|
|
cv::String feature(start, end);
|
|
|
|
start = end;
|
|
|
|
|
|
|
|
CV_Assert(feature.size() > 0);
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < CV_HARDWARE_MAX_FEATURE; i++)
|
|
|
|
{
|
|
|
|
if (!g_hwFeatureNames[i]) continue;
|
|
|
|
size_t len = strlen(g_hwFeatureNames[i]);
|
|
|
|
if (len != feature.size()) continue;
|
|
|
|
if (feature.compare(g_hwFeatureNames[i]) == 0)
|
|
|
|
{
|
|
|
|
bool isBaseline = false;
|
|
|
|
for (int k = 0; k < baseline_count; k++)
|
|
|
|
{
|
|
|
|
if (baseline_features[k] == i)
|
|
|
|
{
|
|
|
|
isBaseline = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isBaseline)
|
|
|
|
{
|
2019-05-05 19:19:49 +08:00
|
|
|
if (dump) fprintf(stderr, "OPENCV: Trying to disable baseline CPU feature: '%s'."
|
|
|
|
"This has very limited effect, because code optimizations for this feature are executed unconditionally "
|
|
|
|
"in the most cases.\n", getHWFeatureNameSafe(i));
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
if (!have[i])
|
|
|
|
{
|
2019-05-05 19:19:49 +08:00
|
|
|
if (dump) fprintf(stderr, "OPENCV: Trying to disable unavailable CPU feature on the current platform: '%s'.\n",
|
|
|
|
getHWFeatureNameSafe(i));
|
2016-09-07 23:02:36 +08:00
|
|
|
}
|
|
|
|
have[i] = false;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
if (dump) fprintf(stderr, "OPENCV: Trying to disable unknown CPU feature: '%s'.\n", feature.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-15 20:55:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool have[MAX_FEATURE+1];
|
|
|
|
};
|
|
|
|
|
2016-09-07 23:02:36 +08:00
|
|
|
static HWFeatures featuresEnabled(true), featuresDisabled = HWFeatures(false);
|
2015-12-15 20:55:43 +08:00
|
|
|
static HWFeatures* currentFeatures = &featuresEnabled;
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
bool checkHardwareSupport(int feature)
|
|
|
|
{
|
|
|
|
CV_DbgAssert( 0 <= feature && feature <= CV_HARDWARE_MAX_FEATURE );
|
2015-12-15 20:55:43 +08:00
|
|
|
return currentFeatures->have[feature];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2018-01-27 21:37:38 +08:00
|
|
|
String getHardwareFeatureName(int feature)
|
|
|
|
{
|
|
|
|
const char* name = getHWFeatureName(feature);
|
|
|
|
return name ? String(name) : String();
|
|
|
|
}
|
2015-12-15 20:55:43 +08:00
|
|
|
|
2018-07-26 21:27:47 +08:00
|
|
|
std::string getCPUFeaturesLine()
|
|
|
|
{
|
|
|
|
const int features[] = { CV_CPU_BASELINE_FEATURES, CV_CPU_DISPATCH_FEATURES };
|
|
|
|
const int sz = sizeof(features) / sizeof(features[0]);
|
|
|
|
std::string result;
|
|
|
|
std::string prefix;
|
|
|
|
for (int i = 1; i < sz; ++i)
|
|
|
|
{
|
|
|
|
if (features[i] == 0)
|
|
|
|
{
|
|
|
|
prefix = "*";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (i != 1) result.append(" ");
|
|
|
|
result.append(prefix);
|
|
|
|
result.append(getHWFeatureNameSafe(features[i]));
|
|
|
|
if (!checkHardwareSupport(features[i])) result.append("?");
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-12-15 20:55:43 +08:00
|
|
|
volatile bool useOptimizedFlag = true;
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
void setUseOptimized( bool flag )
|
|
|
|
{
|
2015-12-15 20:55:43 +08:00
|
|
|
useOptimizedFlag = flag;
|
|
|
|
currentFeatures = flag ? &featuresEnabled : &featuresDisabled;
|
2015-02-27 19:24:51 +08:00
|
|
|
|
|
|
|
ipp::setUseIPP(flag);
|
2015-06-20 01:52:14 +08:00
|
|
|
#ifdef HAVE_OPENCL
|
2015-02-27 19:24:51 +08:00
|
|
|
ocl::setUseOpenCL(flag);
|
2015-06-20 01:52:14 +08:00
|
|
|
#endif
|
2015-02-27 19:24:51 +08:00
|
|
|
#ifdef HAVE_TEGRA_OPTIMIZATION
|
|
|
|
::tegra::setUseTegra(flag);
|
|
|
|
#endif
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2010-12-24 07:00:04 +08:00
|
|
|
bool useOptimized(void)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2015-12-15 20:55:43 +08:00
|
|
|
return useOptimizedFlag;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
|
|
|
|
int64 getTickCount(void)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined _WIN32 || defined WINCE
|
2010-05-12 01:44:00 +08:00
|
|
|
LARGE_INTEGER counter;
|
|
|
|
QueryPerformanceCounter( &counter );
|
|
|
|
return (int64)counter.QuadPart;
|
2021-07-14 03:40:15 +08:00
|
|
|
#elif defined __MACH__ && defined __APPLE__
|
|
|
|
return (int64)mach_absolute_time();
|
|
|
|
#elif defined __unix__
|
2010-05-12 01:44:00 +08:00
|
|
|
struct timespec tp;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
|
|
return (int64)tp.tv_sec*1000000000 + tp.tv_nsec;
|
2010-12-24 07:00:04 +08:00
|
|
|
#else
|
2010-05-12 01:44:00 +08:00
|
|
|
struct timeval tv;
|
2019-06-05 00:45:21 +08:00
|
|
|
gettimeofday(&tv, NULL);
|
2010-05-12 01:44:00 +08:00
|
|
|
return (int64)tv.tv_sec*1000000 + tv.tv_usec;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-12-24 07:00:04 +08:00
|
|
|
double getTickFrequency(void)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined _WIN32 || defined WINCE
|
2010-05-12 01:44:00 +08:00
|
|
|
LARGE_INTEGER freq;
|
|
|
|
QueryPerformanceFrequency(&freq);
|
|
|
|
return (double)freq.QuadPart;
|
2011-05-31 23:22:22 +08:00
|
|
|
#elif defined __MACH__ && defined __APPLE__
|
2010-05-12 01:44:00 +08:00
|
|
|
static double freq = 0;
|
|
|
|
if( freq == 0 )
|
|
|
|
{
|
|
|
|
mach_timebase_info_data_t sTimebaseInfo;
|
|
|
|
mach_timebase_info(&sTimebaseInfo);
|
|
|
|
freq = sTimebaseInfo.denom*1e9/sTimebaseInfo.numer;
|
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
return freq;
|
2021-07-14 03:40:15 +08:00
|
|
|
#elif defined __unix__
|
|
|
|
return 1e9;
|
2010-05-12 01:44:00 +08:00
|
|
|
#else
|
|
|
|
return 1e6;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-12-24 07:00:04 +08:00
|
|
|
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__ || defined __ppc__)
|
2010-05-12 01:44:00 +08:00
|
|
|
#if defined(__i386__)
|
|
|
|
|
|
|
|
int64 getCPUTickCount(void)
|
|
|
|
{
|
|
|
|
int64 x;
|
|
|
|
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
#elif defined(__x86_64__)
|
|
|
|
|
|
|
|
int64 getCPUTickCount(void)
|
|
|
|
{
|
|
|
|
unsigned hi, lo;
|
|
|
|
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
|
|
|
|
return (int64)lo | ((int64)hi << 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined(__ppc__)
|
|
|
|
|
|
|
|
int64 getCPUTickCount(void)
|
|
|
|
{
|
|
|
|
unsigned upper, lower, tmp;
|
|
|
|
__asm__ volatile(
|
|
|
|
"0: \n"
|
|
|
|
"\tmftbu %0 \n"
|
|
|
|
"\tmftb %1 \n"
|
|
|
|
"\tmftbu %2 \n"
|
|
|
|
"\tcmpw %2,%0 \n"
|
|
|
|
"\tbne 0b \n"
|
|
|
|
: "=r"(upper),"=r"(lower),"=r"(tmp)
|
|
|
|
);
|
|
|
|
return lower | ((int64)upper << 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#error "RDTSC not defined"
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#elif defined _MSC_VER && defined _WIN32 && defined _M_IX86
|
2010-05-12 01:44:00 +08:00
|
|
|
|
|
|
|
int64 getCPUTickCount(void)
|
|
|
|
{
|
|
|
|
__asm _emit 0x0f;
|
|
|
|
__asm _emit 0x31;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2014-03-21 19:27:56 +08:00
|
|
|
//#ifdef HAVE_IPP
|
|
|
|
//int64 getCPUTickCount(void)
|
|
|
|
//{
|
|
|
|
// return ippGetCpuClocks();
|
|
|
|
//}
|
|
|
|
//#else
|
2010-12-24 07:00:04 +08:00
|
|
|
int64 getCPUTickCount(void)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
return getTickCount();
|
|
|
|
}
|
2014-03-21 19:27:56 +08:00
|
|
|
//#endif
|
2010-05-12 01:44:00 +08:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2021-11-20 23:34:23 +08:00
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
class Timestamp
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
const int64 zeroTickCount;
|
|
|
|
const double ns_in_ticks;
|
|
|
|
|
|
|
|
Timestamp()
|
|
|
|
: zeroTickCount(getTickCount())
|
|
|
|
, ns_in_ticks(1e9 / getTickFrequency())
|
|
|
|
{
|
|
|
|
// nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 getTimestamp()
|
|
|
|
{
|
|
|
|
int64 t = getTickCount();
|
|
|
|
return (int64)((t - zeroTickCount) * ns_in_ticks);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Timestamp& getInstance()
|
|
|
|
{
|
|
|
|
static Timestamp g_timestamp;
|
|
|
|
return g_timestamp;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class InitTimestamp {
|
|
|
|
public:
|
|
|
|
InitTimestamp() {
|
|
|
|
Timestamp::getInstance();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
static InitTimestamp g_initialize_timestamp; // force zero timestamp initialization
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
int64 getTimestampNS()
|
|
|
|
{
|
|
|
|
return internal::Timestamp::getInstance().getTimestamp();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-23 00:37:49 +08:00
|
|
|
const String& getBuildInformation()
|
2012-04-14 05:50:59 +08:00
|
|
|
{
|
2013-03-23 00:37:49 +08:00
|
|
|
static String build_info =
|
2012-04-14 05:50:59 +08:00
|
|
|
#include "version_string.inc"
|
|
|
|
;
|
|
|
|
return build_info;
|
|
|
|
}
|
|
|
|
|
2018-04-06 18:13:53 +08:00
|
|
|
String getVersionString() { return String(CV_VERSION); }
|
|
|
|
|
|
|
|
int getVersionMajor() { return CV_VERSION_MAJOR; }
|
|
|
|
|
|
|
|
int getVersionMinor() { return CV_VERSION_MINOR; }
|
|
|
|
|
|
|
|
int getVersionRevision() { return CV_VERSION_REVISION; }
|
|
|
|
|
2013-03-23 00:37:49 +08:00
|
|
|
String format( const char* fmt, ... )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2014-03-25 03:07:00 +08:00
|
|
|
AutoBuffer<char, 1024> buf;
|
2013-03-26 15:54:04 +08:00
|
|
|
|
2014-03-25 03:07:00 +08:00
|
|
|
for ( ; ; )
|
2013-03-26 15:54:04 +08:00
|
|
|
{
|
2014-03-25 03:07:00 +08:00
|
|
|
va_list va;
|
2013-03-26 15:54:04 +08:00
|
|
|
va_start(va, fmt);
|
2017-05-23 03:24:17 +08:00
|
|
|
int bsize = static_cast<int>(buf.size());
|
2018-06-11 06:42:00 +08:00
|
|
|
int len = cv_vsnprintf(buf.data(), bsize, fmt, va);
|
2013-03-26 15:54:04 +08:00
|
|
|
va_end(va);
|
|
|
|
|
2017-06-09 03:53:16 +08:00
|
|
|
CV_Assert(len >= 0 && "Check format string for errors");
|
|
|
|
if (len >= bsize)
|
2014-03-25 03:07:00 +08:00
|
|
|
{
|
2017-06-09 03:53:16 +08:00
|
|
|
buf.resize(len + 1);
|
2014-03-25 03:07:00 +08:00
|
|
|
continue;
|
|
|
|
}
|
2017-05-23 03:24:17 +08:00
|
|
|
buf[bsize - 1] = 0;
|
2018-06-11 06:42:00 +08:00
|
|
|
return String(buf.data(), len);
|
2014-03-25 03:07:00 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2013-03-23 00:37:49 +08:00
|
|
|
String tempfile( const char* suffix )
|
2011-04-11 22:47:06 +08:00
|
|
|
{
|
2013-03-23 00:37:49 +08:00
|
|
|
String fname;
|
2019-07-25 04:12:09 +08:00
|
|
|
#ifndef NO_GETENV
|
2014-05-06 04:59:39 +08:00
|
|
|
const char *temp_dir = getenv("OPENCV_TEMP_PATH");
|
2013-07-19 17:43:05 +08:00
|
|
|
#endif
|
2012-12-08 19:43:23 +08:00
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined _WIN32
|
2015-02-21 00:47:45 +08:00
|
|
|
#ifdef WINRT
|
2013-07-19 17:43:05 +08:00
|
|
|
RoInitialize(RO_INIT_MULTITHREADED);
|
2015-03-04 15:35:06 +08:00
|
|
|
std::wstring temp_dir = GetTempPathWinRT();
|
2013-07-19 17:43:05 +08:00
|
|
|
|
2015-03-04 15:35:06 +08:00
|
|
|
std::wstring temp_file = GetTempFileNameWinRT(L"ocv");
|
2013-07-19 17:43:05 +08:00
|
|
|
if (temp_file.empty())
|
2014-05-06 04:59:39 +08:00
|
|
|
return String();
|
2013-07-19 17:43:05 +08:00
|
|
|
|
2015-02-21 00:47:45 +08:00
|
|
|
temp_file = temp_dir.append(std::wstring(L"\\")).append(temp_file);
|
2013-07-19 17:43:05 +08:00
|
|
|
DeleteFileW(temp_file.c_str());
|
|
|
|
|
2013-08-19 16:25:53 +08:00
|
|
|
char aname[MAX_PATH];
|
|
|
|
size_t copied = wcstombs(aname, temp_file.c_str(), MAX_PATH);
|
|
|
|
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
|
2014-05-06 04:59:39 +08:00
|
|
|
fname = String(aname);
|
2013-07-19 17:43:05 +08:00
|
|
|
RoUninitialize();
|
2019-07-25 04:12:09 +08:00
|
|
|
#elif defined(_WIN32_WCE)
|
|
|
|
const auto kMaxPathSize = MAX_PATH+1;
|
|
|
|
wchar_t temp_dir[kMaxPathSize] = {0};
|
|
|
|
wchar_t temp_file[kMaxPathSize] = {0};
|
|
|
|
|
|
|
|
::GetTempPathW(kMaxPathSize, temp_dir);
|
|
|
|
|
|
|
|
if(0 != ::GetTempFileNameW(temp_dir, L"ocv", 0, temp_file)) {
|
|
|
|
DeleteFileW(temp_file);
|
|
|
|
char aname[MAX_PATH];
|
|
|
|
size_t copied = wcstombs(aname, temp_file, MAX_PATH);
|
|
|
|
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
|
|
|
|
fname = String(aname);
|
|
|
|
}
|
2013-07-19 17:43:05 +08:00
|
|
|
#else
|
2013-08-19 16:25:53 +08:00
|
|
|
char temp_dir2[MAX_PATH] = { 0 };
|
|
|
|
char temp_file[MAX_PATH] = { 0 };
|
2012-06-25 19:24:06 +08:00
|
|
|
|
2012-12-08 19:43:23 +08:00
|
|
|
if (temp_dir == 0 || temp_dir[0] == 0)
|
|
|
|
{
|
|
|
|
::GetTempPathA(sizeof(temp_dir2), temp_dir2);
|
|
|
|
temp_dir = temp_dir2;
|
|
|
|
}
|
2012-07-02 20:23:57 +08:00
|
|
|
if(0 == ::GetTempFileNameA(temp_dir, "ocv", 0, temp_file))
|
2013-03-23 00:37:49 +08:00
|
|
|
return String();
|
2012-06-25 19:24:06 +08:00
|
|
|
|
2012-10-04 13:44:29 +08:00
|
|
|
DeleteFileA(temp_file);
|
|
|
|
|
2012-12-08 19:43:23 +08:00
|
|
|
fname = temp_file;
|
2013-07-19 17:43:05 +08:00
|
|
|
#endif
|
2012-06-25 19:24:06 +08:00
|
|
|
# else
|
2017-07-10 17:43:59 +08:00
|
|
|
# ifdef __ANDROID__
|
2012-06-25 19:24:06 +08:00
|
|
|
//char defaultTemplate[] = "/mnt/sdcard/__opencv_temp.XXXXXX";
|
|
|
|
char defaultTemplate[] = "/data/local/tmp/__opencv_temp.XXXXXX";
|
|
|
|
# else
|
|
|
|
char defaultTemplate[] = "/tmp/__opencv_temp.XXXXXX";
|
|
|
|
# endif
|
|
|
|
|
2012-12-08 19:43:23 +08:00
|
|
|
if (temp_dir == 0 || temp_dir[0] == 0)
|
2012-06-25 19:24:06 +08:00
|
|
|
fname = defaultTemplate;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fname = temp_dir;
|
|
|
|
char ech = fname[fname.size() - 1];
|
|
|
|
if(ech != '/' && ech != '\\')
|
2013-03-20 21:53:13 +08:00
|
|
|
fname = fname + "/";
|
|
|
|
fname = fname + "__opencv_temp.XXXXXX";
|
2012-06-25 19:24:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const int fd = mkstemp((char*)fname.c_str());
|
2013-03-23 00:37:49 +08:00
|
|
|
if (fd == -1) return String();
|
2012-12-08 19:43:23 +08:00
|
|
|
|
2012-06-25 19:24:06 +08:00
|
|
|
close(fd);
|
|
|
|
remove(fname.c_str());
|
2012-12-08 19:43:23 +08:00
|
|
|
# endif
|
2012-06-25 19:24:06 +08:00
|
|
|
|
2012-12-08 19:43:23 +08:00
|
|
|
if (suffix)
|
2012-06-25 19:24:06 +08:00
|
|
|
{
|
|
|
|
if (suffix[0] != '.')
|
2012-12-08 19:43:23 +08:00
|
|
|
return fname + "." + suffix;
|
2012-06-25 19:24:06 +08:00
|
|
|
else
|
2012-12-08 19:43:23 +08:00
|
|
|
return fname + suffix;
|
2012-06-25 19:24:06 +08:00
|
|
|
}
|
|
|
|
return fname;
|
2011-04-11 22:47:06 +08:00
|
|
|
}
|
|
|
|
|
2016-11-15 07:33:11 +08:00
|
|
|
static ErrorCallback customErrorCallback = 0;
|
2010-05-12 01:44:00 +08:00
|
|
|
static void* customErrorCallbackData = 0;
|
|
|
|
static bool breakOnError = false;
|
|
|
|
|
|
|
|
bool setBreakOnError(bool value)
|
|
|
|
{
|
|
|
|
bool prevVal = breakOnError;
|
|
|
|
breakOnError = value;
|
|
|
|
return prevVal;
|
2010-12-24 07:00:04 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2017-06-09 03:53:16 +08:00
|
|
|
int cv_snprintf(char* buf, int len, const char* fmt, ...)
|
2017-05-23 03:24:17 +08:00
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
2017-06-09 03:53:16 +08:00
|
|
|
int res = cv_vsnprintf(buf, len, fmt, va);
|
2017-05-23 03:24:17 +08:00
|
|
|
va_end(va);
|
2017-06-09 03:53:16 +08:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cv_vsnprintf(char* buf, int len, const char* fmt, va_list args)
|
|
|
|
{
|
|
|
|
#if defined _MSC_VER
|
|
|
|
if (len <= 0) return len == 0 ? 1024 : -1;
|
|
|
|
int res = _vsnprintf_s(buf, len, _TRUNCATE, fmt, args);
|
|
|
|
// ensure null terminating on VS
|
|
|
|
if (res >= 0 && res < len)
|
|
|
|
{
|
|
|
|
buf[res] = 0;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf[len - 1] = 0; // truncate happened
|
|
|
|
return res >= len ? res : (len * 2);
|
|
|
|
}
|
2017-05-23 03:24:17 +08:00
|
|
|
#else
|
2017-06-09 03:53:16 +08:00
|
|
|
return vsnprintf(buf, len, fmt, args);
|
2017-05-23 03:24:17 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-04-23 20:06:43 +08:00
|
|
|
static void dumpException(const Exception& exc)
|
|
|
|
{
|
|
|
|
const char* errorStr = cvErrorStr(exc.code);
|
|
|
|
char buf[1 << 12];
|
|
|
|
|
|
|
|
cv_snprintf(buf, sizeof(buf),
|
|
|
|
"OpenCV(%s) Error: %s (%s) in %s, file %s, line %d",
|
|
|
|
CV_VERSION,
|
|
|
|
errorStr, exc.err.c_str(), exc.func.size() > 0 ?
|
|
|
|
exc.func.c_str() : "unknown function", exc.file.c_str(), exc.line);
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
__android_log_print(ANDROID_LOG_ERROR, "cv::error()", "%s", buf);
|
|
|
|
#else
|
|
|
|
fflush(stdout); fflush(stderr);
|
|
|
|
fprintf(stderr, "%s\n", buf);
|
|
|
|
fflush(stderr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CV_ERROR_SET_TERMINATE_HANDLER
|
|
|
|
static bool cv_terminate_handler_installed = false;
|
|
|
|
static std::terminate_handler cv_old_terminate_handler;
|
|
|
|
static cv::Exception cv_terminate_handler_exception;
|
|
|
|
static bool param_setupTerminateHandler = utils::getConfigurationParameterBool("OPENCV_SETUP_TERMINATE_HANDLER", true);
|
|
|
|
static void cv_terminate_handler() {
|
|
|
|
std::cerr << "OpenCV: terminate handler is called! The last OpenCV error is:\n";
|
|
|
|
dumpException(cv_terminate_handler_exception);
|
|
|
|
if (false /*cv_old_terminate_handler*/) // buggy behavior is observed with doubled "abort/retry/ignore" windows
|
|
|
|
cv_old_terminate_handler();
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
void error( const Exception& exc )
|
|
|
|
{
|
2018-04-23 20:06:43 +08:00
|
|
|
#ifdef CV_ERROR_SET_TERMINATE_HANDLER
|
|
|
|
{
|
|
|
|
cv::AutoLock lock(getInitializationMutex());
|
|
|
|
if (!cv_terminate_handler_installed)
|
|
|
|
{
|
|
|
|
if (param_setupTerminateHandler)
|
|
|
|
cv_old_terminate_handler = std::set_terminate(cv_terminate_handler);
|
|
|
|
cv_terminate_handler_installed = true;
|
|
|
|
}
|
|
|
|
cv_terminate_handler_exception = exc;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-12-24 07:00:04 +08:00
|
|
|
if (customErrorCallback != 0)
|
2010-05-12 01:44:00 +08:00
|
|
|
customErrorCallback(exc.code, exc.func.c_str(), exc.err.c_str(),
|
|
|
|
exc.file.c_str(), exc.line, customErrorCallbackData);
|
2018-04-20 17:29:12 +08:00
|
|
|
else if (param_dumpErrors)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2018-04-23 20:06:43 +08:00
|
|
|
dumpException(exc);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
if(breakOnError)
|
|
|
|
{
|
|
|
|
static volatile int* p = 0;
|
|
|
|
*p = 0;
|
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2018-11-09 00:46:25 +08:00
|
|
|
throw exc;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2013-03-27 22:43:06 +08:00
|
|
|
void error(int _code, const String& _err, const char* _func, const char* _file, int _line)
|
|
|
|
{
|
|
|
|
error(cv::Exception(_code, _err, _func, _file, _line));
|
|
|
|
}
|
|
|
|
|
2017-05-23 03:24:17 +08:00
|
|
|
|
2016-11-15 07:33:11 +08:00
|
|
|
ErrorCallback
|
|
|
|
redirectError( ErrorCallback errCallback, void* userdata, void** prevUserdata)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
if( prevUserdata )
|
|
|
|
*prevUserdata = customErrorCallbackData;
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2016-11-15 07:33:11 +08:00
|
|
|
ErrorCallback prevCallback = customErrorCallback;
|
2010-12-24 07:00:04 +08:00
|
|
|
|
|
|
|
customErrorCallback = errCallback;
|
2010-05-12 01:44:00 +08:00
|
|
|
customErrorCallbackData = userdata;
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
return prevCallback;
|
|
|
|
}
|
2010-12-24 07:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvCheckHardwareSupport(int feature)
|
|
|
|
{
|
|
|
|
CV_DbgAssert( 0 <= feature && feature <= CV_HARDWARE_MAX_FEATURE );
|
2015-12-15 20:55:43 +08:00
|
|
|
return cv::currentFeatures->have[feature];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvUseOptimized( int flag )
|
|
|
|
{
|
2015-12-15 20:55:43 +08:00
|
|
|
int prevMode = cv::useOptimizedFlag;
|
2010-05-12 01:44:00 +08:00
|
|
|
cv::setUseOptimized( flag != 0 );
|
|
|
|
return prevMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int64 cvGetTickCount(void)
|
|
|
|
{
|
|
|
|
return cv::getTickCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL double cvGetTickFrequency(void)
|
|
|
|
{
|
|
|
|
return cv::getTickFrequency()*1e-6;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL CvErrorCallback
|
|
|
|
cvRedirectError( CvErrorCallback errCallback, void* userdata, void** prevUserdata)
|
|
|
|
{
|
|
|
|
return cv::redirectError(errCallback, userdata, prevUserdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvNulDevReport( int, const char*, const char*,
|
|
|
|
const char*, int, void* )
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvStdErrReport( int, const char*, const char*,
|
|
|
|
const char*, int, void* )
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvGuiBoxReport( int, const char*, const char*,
|
|
|
|
const char*, int, void* )
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvGetErrInfo( const char**, const char**, const char**, int* )
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CV_IMPL const char* cvErrorStr( int status )
|
|
|
|
{
|
|
|
|
static char buf[256];
|
|
|
|
|
|
|
|
switch (status)
|
|
|
|
{
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_StsOk : return "No Error";
|
|
|
|
case CV_StsBackTrace : return "Backtrace";
|
|
|
|
case CV_StsError : return "Unspecified error";
|
|
|
|
case CV_StsInternal : return "Internal error";
|
|
|
|
case CV_StsNoMem : return "Insufficient memory";
|
|
|
|
case CV_StsBadArg : return "Bad argument";
|
|
|
|
case CV_StsNoConv : return "Iterations do not converge";
|
|
|
|
case CV_StsAutoTrace : return "Autotrace call";
|
|
|
|
case CV_StsBadSize : return "Incorrect size of input array";
|
|
|
|
case CV_StsNullPtr : return "Null pointer";
|
2017-02-21 17:48:15 +08:00
|
|
|
case CV_StsDivByZero : return "Division by zero occurred";
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_BadStep : return "Image step is wrong";
|
2010-05-12 01:44:00 +08:00
|
|
|
case CV_StsInplaceNotSupported : return "Inplace operation is not supported";
|
|
|
|
case CV_StsObjectNotFound : return "Requested object was not found";
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_BadDepth : return "Input image depth is not supported by function";
|
|
|
|
case CV_StsUnmatchedFormats : return "Formats of input arguments do not match";
|
|
|
|
case CV_StsUnmatchedSizes : return "Sizes of input arguments do not match";
|
2019-05-15 23:41:43 +08:00
|
|
|
case CV_StsOutOfRange : return "One of the arguments\' values is out of range";
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_StsUnsupportedFormat : return "Unsupported format or combination of formats";
|
|
|
|
case CV_BadCOI : return "Input COI is not supported";
|
|
|
|
case CV_BadNumChannels : return "Bad number of channels";
|
|
|
|
case CV_StsBadFlag : return "Bad flag (parameter or structure field)";
|
|
|
|
case CV_StsBadPoint : return "Bad parameter of type CvPoint";
|
|
|
|
case CV_StsBadMask : return "Bad type of mask argument";
|
|
|
|
case CV_StsParseError : return "Parsing error";
|
|
|
|
case CV_StsNotImplemented : return "The function/feature is not implemented";
|
|
|
|
case CV_StsBadMemBlock : return "Memory block has been corrupted";
|
|
|
|
case CV_StsAssert : return "Assertion failed";
|
2013-07-24 17:55:18 +08:00
|
|
|
case CV_GpuNotSupported : return "No CUDA support";
|
2011-11-21 19:58:52 +08:00
|
|
|
case CV_GpuApiCallError : return "Gpu API call";
|
|
|
|
case CV_OpenGlNotSupported : return "No OpenGL support";
|
|
|
|
case CV_OpenGlApiCallError : return "OpenGL API call";
|
2010-05-12 01:44:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
sprintf(buf, "Unknown %s code %d", status >= 0 ? "status":"error", status);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvGetErrMode(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL int cvSetErrMode(int)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-24 07:00:04 +08:00
|
|
|
CV_IMPL int cvGetErrStatus(void)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CV_IMPL void cvSetErrStatus(int)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CV_IMPL void cvError( int code, const char* func_name,
|
|
|
|
const char* err_msg,
|
|
|
|
const char* file_name, int line )
|
|
|
|
{
|
|
|
|
cv::error(cv::Exception(code, err_msg, func_name, file_name, line));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* function, which converts int to int */
|
|
|
|
CV_IMPL int
|
|
|
|
cvErrorFromIppStatus( int status )
|
|
|
|
{
|
|
|
|
switch (status)
|
|
|
|
{
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_BADSIZE_ERR: return CV_StsBadSize;
|
|
|
|
case CV_BADMEMBLOCK_ERR: return CV_StsBadMemBlock;
|
|
|
|
case CV_NULLPTR_ERR: return CV_StsNullPtr;
|
|
|
|
case CV_DIV_BY_ZERO_ERR: return CV_StsDivByZero;
|
|
|
|
case CV_BADSTEP_ERR: return CV_BadStep;
|
|
|
|
case CV_OUTOFMEM_ERR: return CV_StsNoMem;
|
|
|
|
case CV_BADARG_ERR: return CV_StsBadArg;
|
|
|
|
case CV_NOTDEFINED_ERR: return CV_StsError;
|
2010-05-12 01:44:00 +08:00
|
|
|
case CV_INPLACE_NOT_SUPPORTED_ERR: return CV_StsInplaceNotSupported;
|
2010-12-24 07:00:04 +08:00
|
|
|
case CV_NOTFOUND_ERR: return CV_StsObjectNotFound;
|
|
|
|
case CV_BADCONVERGENCE_ERR: return CV_StsNoConv;
|
|
|
|
case CV_BADDEPTH_ERR: return CV_BadDepth;
|
|
|
|
case CV_UNMATCHED_FORMATS_ERR: return CV_StsUnmatchedFormats;
|
|
|
|
case CV_UNSUPPORTED_COI_ERR: return CV_BadCOI;
|
|
|
|
case CV_UNSUPPORTED_CHANNELS_ERR: return CV_BadNumChannels;
|
|
|
|
case CV_BADFLAG_ERR: return CV_StsBadFlag;
|
|
|
|
case CV_BADRANGE_ERR: return CV_StsBadArg;
|
|
|
|
case CV_BADCOEF_ERR: return CV_StsBadArg;
|
|
|
|
case CV_BADFACTOR_ERR: return CV_StsBadArg;
|
|
|
|
case CV_BADPOINT_ERR: return CV_StsBadPoint;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return CV_StsError;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-11 04:31:34 +08:00
|
|
|
namespace cv {
|
|
|
|
bool __termination = false;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
namespace cv
|
|
|
|
{
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined _WIN32 || defined WINCE
|
2012-08-17 21:32:06 +08:00
|
|
|
|
|
|
|
struct Mutex::Impl
|
|
|
|
{
|
2013-07-19 17:43:05 +08:00
|
|
|
Impl()
|
|
|
|
{
|
|
|
|
#if (_WIN32_WINNT >= 0x0600)
|
|
|
|
::InitializeCriticalSectionEx(&cs, 1000, 0);
|
|
|
|
#else
|
|
|
|
::InitializeCriticalSection(&cs);
|
|
|
|
#endif
|
|
|
|
refcount = 1;
|
|
|
|
}
|
2012-08-17 21:32:06 +08:00
|
|
|
~Impl() { DeleteCriticalSection(&cs); }
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
void lock() { EnterCriticalSection(&cs); }
|
|
|
|
bool trylock() { return TryEnterCriticalSection(&cs) != 0; }
|
|
|
|
void unlock() { LeaveCriticalSection(&cs); }
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
CRITICAL_SECTION cs;
|
|
|
|
int refcount;
|
|
|
|
};
|
2012-10-17 07:18:30 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
#else
|
|
|
|
|
|
|
|
struct Mutex::Impl
|
|
|
|
{
|
2013-11-22 17:00:37 +08:00
|
|
|
Impl()
|
|
|
|
{
|
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
pthread_mutexattr_init(&attr);
|
|
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
|
|
pthread_mutex_init(&mt, &attr);
|
|
|
|
pthread_mutexattr_destroy(&attr);
|
|
|
|
|
|
|
|
refcount = 1;
|
|
|
|
}
|
|
|
|
~Impl() { pthread_mutex_destroy(&mt); }
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2013-11-22 17:00:37 +08:00
|
|
|
void lock() { pthread_mutex_lock(&mt); }
|
|
|
|
bool trylock() { return pthread_mutex_trylock(&mt) == 0; }
|
|
|
|
void unlock() { pthread_mutex_unlock(&mt); }
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2013-11-22 17:00:37 +08:00
|
|
|
pthread_mutex_t mt;
|
2012-08-17 21:32:06 +08:00
|
|
|
int refcount;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Mutex::Mutex()
|
|
|
|
{
|
|
|
|
impl = new Mutex::Impl;
|
|
|
|
}
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
Mutex::~Mutex()
|
|
|
|
{
|
|
|
|
if( CV_XADD(&impl->refcount, -1) == 1 )
|
|
|
|
delete impl;
|
|
|
|
impl = 0;
|
|
|
|
}
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
Mutex::Mutex(const Mutex& m)
|
|
|
|
{
|
|
|
|
impl = m.impl;
|
|
|
|
CV_XADD(&impl->refcount, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Mutex& Mutex::operator = (const Mutex& m)
|
|
|
|
{
|
2017-06-28 21:26:55 +08:00
|
|
|
if (this != &m)
|
|
|
|
{
|
|
|
|
CV_XADD(&m.impl->refcount, 1);
|
|
|
|
if( CV_XADD(&impl->refcount, -1) == 1 )
|
|
|
|
delete impl;
|
|
|
|
impl = m.impl;
|
|
|
|
}
|
2012-08-17 21:32:06 +08:00
|
|
|
return *this;
|
|
|
|
}
|
2012-08-17 22:28:50 +08:00
|
|
|
|
2012-08-17 21:32:06 +08:00
|
|
|
void Mutex::lock() { impl->lock(); }
|
|
|
|
void Mutex::unlock() { impl->unlock(); }
|
2012-08-17 22:28:50 +08:00
|
|
|
bool Mutex::trylock() { return impl->trylock(); }
|
2012-08-17 21:32:06 +08:00
|
|
|
|
2013-12-11 22:49:13 +08:00
|
|
|
|
|
|
|
//////////////////////////////// thread-local storage ////////////////////////////////
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
namespace details {
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#ifdef _WIN32
|
2015-08-12 21:23:02 +08:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(disable:4505) // unreferenced local function has been removed
|
|
|
|
#endif
|
|
|
|
#ifndef TLS_OUT_OF_INDEXES
|
|
|
|
#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// TLS platform abstraction layer
|
2021-10-08 09:36:58 +08:00
|
|
|
class TlsAbstraction
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
|
|
|
public:
|
2015-08-12 21:23:02 +08:00
|
|
|
TlsAbstraction();
|
2021-10-08 09:36:58 +08:00
|
|
|
~TlsAbstraction()
|
2019-12-04 14:18:36 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
// TlsAbstraction singleton should not be released
|
|
|
|
// There is no reliable way to avoid problems caused by static initialization order fiasco
|
|
|
|
// NB: Do NOT use logging here
|
|
|
|
fprintf(stderr, "OpenCV FATAL: TlsAbstraction::~TlsAbstraction() call is not expected\n");
|
|
|
|
fflush(stderr);
|
2019-12-04 14:18:36 +08:00
|
|
|
}
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2021-10-08 09:36:58 +08:00
|
|
|
void* getData() const;
|
|
|
|
void setData(void *pData);
|
|
|
|
|
|
|
|
void releaseSystemResources();
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
private:
|
2019-12-04 14:18:36 +08:00
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#ifdef _WIN32
|
2015-08-12 21:23:02 +08:00
|
|
|
#ifndef WINRT
|
|
|
|
DWORD tlsKey;
|
2021-10-08 09:36:58 +08:00
|
|
|
bool disposed;
|
2013-12-30 16:31:00 +08:00
|
|
|
#endif
|
2017-07-25 18:23:44 +08:00
|
|
|
#else // _WIN32
|
2015-08-12 21:23:02 +08:00
|
|
|
pthread_key_t tlsKey;
|
2021-10-08 09:36:58 +08:00
|
|
|
#if OPENCV_WITH_THREAD_SANITIZER
|
|
|
|
std::atomic<bool> disposed;
|
|
|
|
#else
|
|
|
|
bool disposed;
|
|
|
|
#endif
|
2015-08-12 21:23:02 +08:00
|
|
|
#endif
|
|
|
|
};
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2021-10-08 09:36:58 +08:00
|
|
|
class TlsAbstractionReleaseGuard
|
2019-12-04 14:18:36 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
TlsAbstraction& tls_;
|
|
|
|
public:
|
|
|
|
TlsAbstractionReleaseGuard(TlsAbstraction& tls) : tls_(tls)
|
|
|
|
{
|
|
|
|
/* nothing */
|
|
|
|
}
|
|
|
|
~TlsAbstractionReleaseGuard()
|
|
|
|
{
|
|
|
|
tls_.releaseSystemResources();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO use reference
|
2019-12-04 14:18:36 +08:00
|
|
|
static TlsAbstraction* getTlsAbstraction()
|
|
|
|
{
|
|
|
|
#ifdef CV_CXX11
|
2021-10-08 09:36:58 +08:00
|
|
|
static TlsAbstraction *g_tls = new TlsAbstraction(); // memory leak is intended here to avoid disposing of TLS container
|
|
|
|
static TlsAbstractionReleaseGuard g_tlsReleaseGuard(*g_tls);
|
2019-12-04 14:18:36 +08:00
|
|
|
#else
|
2021-10-08 09:36:58 +08:00
|
|
|
static TlsAbstraction* volatile g_tls = NULL;
|
|
|
|
if (g_tls == NULL)
|
2019-12-04 14:18:36 +08:00
|
|
|
{
|
|
|
|
cv::AutoLock lock(cv::getInitializationMutex());
|
2021-10-08 09:36:58 +08:00
|
|
|
if (g_tls == NULL)
|
|
|
|
{
|
|
|
|
g_tls = new TlsAbstraction();
|
|
|
|
static TlsAbstractionReleaseGuard g_tlsReleaseGuard(*g_tls);
|
|
|
|
}
|
2019-12-04 14:18:36 +08:00
|
|
|
}
|
|
|
|
#endif
|
2021-10-08 09:36:58 +08:00
|
|
|
return g_tls;
|
2019-12-04 14:18:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#ifdef _WIN32
|
2015-02-21 00:47:45 +08:00
|
|
|
#ifdef WINRT
|
2015-08-12 21:23:02 +08:00
|
|
|
static __declspec( thread ) void* tlsData = NULL; // using C++11 thread attribute for local thread data
|
|
|
|
TlsAbstraction::TlsAbstraction() {}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::releaseSystemResources()
|
|
|
|
{
|
|
|
|
cv::__termination = true; // DllMain is missing in static builds
|
|
|
|
}
|
|
|
|
void* TlsAbstraction::getData() const
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
|
|
|
return tlsData;
|
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::setData(void *pData)
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
|
|
|
tlsData = pData;
|
|
|
|
}
|
|
|
|
#else //WINRT
|
2019-11-02 03:33:12 +08:00
|
|
|
#ifdef CV_USE_FLS
|
|
|
|
static void NTAPI opencv_fls_destructor(void* pData);
|
|
|
|
#endif // CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
TlsAbstraction::TlsAbstraction()
|
2021-10-08 09:36:58 +08:00
|
|
|
: disposed(false)
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2019-11-02 03:33:12 +08:00
|
|
|
#ifndef CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
tlsKey = TlsAlloc();
|
2019-11-02 03:33:12 +08:00
|
|
|
#else // CV_USE_FLS
|
|
|
|
tlsKey = FlsAlloc(opencv_fls_destructor);
|
|
|
|
#endif // CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
CV_Assert(tlsKey != TLS_OUT_OF_INDEXES);
|
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::releaseSystemResources()
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
cv::__termination = true; // DllMain is missing in static builds
|
|
|
|
disposed = true;
|
2019-11-02 03:33:12 +08:00
|
|
|
#ifndef CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
TlsFree(tlsKey);
|
2019-11-02 03:33:12 +08:00
|
|
|
#else // CV_USE_FLS
|
|
|
|
FlsFree(tlsKey);
|
|
|
|
#endif // CV_USE_FLS
|
2019-12-04 14:18:36 +08:00
|
|
|
tlsKey = TLS_OUT_OF_INDEXES;
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void* TlsAbstraction::getData() const
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
if (disposed)
|
|
|
|
return NULL;
|
2019-11-02 03:33:12 +08:00
|
|
|
#ifndef CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
return TlsGetValue(tlsKey);
|
2019-11-02 03:33:12 +08:00
|
|
|
#else // CV_USE_FLS
|
|
|
|
return FlsGetValue(tlsKey);
|
|
|
|
#endif // CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::setData(void *pData)
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
if (disposed)
|
|
|
|
return; // no-op
|
2019-11-02 03:33:12 +08:00
|
|
|
#ifndef CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
CV_Assert(TlsSetValue(tlsKey, pData) == TRUE);
|
2019-11-02 03:33:12 +08:00
|
|
|
#else // CV_USE_FLS
|
|
|
|
CV_Assert(FlsSetValue(tlsKey, pData) == TRUE);
|
|
|
|
#endif // CV_USE_FLS
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2019-11-02 03:33:12 +08:00
|
|
|
#endif // WINRT
|
2017-07-25 18:23:44 +08:00
|
|
|
#else // _WIN32
|
2019-10-13 19:14:41 +08:00
|
|
|
static void opencv_tls_destructor(void* pData);
|
2015-08-12 21:23:02 +08:00
|
|
|
TlsAbstraction::TlsAbstraction()
|
2021-10-08 09:36:58 +08:00
|
|
|
: disposed(false)
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
CV_Assert(pthread_key_create(&tlsKey, opencv_tls_destructor) == 0);
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::releaseSystemResources()
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
cv::__termination = true; // DllMain is missing in static builds
|
|
|
|
disposed = true;
|
2019-12-04 14:18:36 +08:00
|
|
|
if (pthread_key_delete(tlsKey) != 0)
|
|
|
|
{
|
|
|
|
// Don't use logging here
|
|
|
|
fprintf(stderr, "OpenCV ERROR: TlsAbstraction::~TlsAbstraction(): pthread_key_delete() call failed\n");
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void* TlsAbstraction::getData() const
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
if (disposed)
|
|
|
|
return NULL;
|
2015-08-12 21:23:02 +08:00
|
|
|
return pthread_getspecific(tlsKey);
|
|
|
|
}
|
2021-10-08 09:36:58 +08:00
|
|
|
void TlsAbstraction::setData(void *pData)
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
if (disposed)
|
|
|
|
return; // no-op
|
2015-08-12 21:23:02 +08:00
|
|
|
CV_Assert(pthread_setspecific(tlsKey, pData) == 0);
|
|
|
|
}
|
|
|
|
#endif
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Per-thread data structure
|
|
|
|
struct ThreadData
|
|
|
|
{
|
|
|
|
ThreadData()
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
idx = 0;
|
|
|
|
slots.reserve(32);
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
std::vector<void*> slots; // Data array for a thread
|
|
|
|
size_t idx; // Thread index in TLS storage. This is not OS thread ID!
|
|
|
|
};
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2021-04-09 23:46:11 +08:00
|
|
|
|
|
|
|
static bool g_isTlsStorageInitialized = false;
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Main TLS storage class
|
|
|
|
class TlsStorage
|
|
|
|
{
|
|
|
|
public:
|
2017-07-27 03:45:55 +08:00
|
|
|
TlsStorage() :
|
|
|
|
tlsSlotsSize(0)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2021-10-08 09:36:58 +08:00
|
|
|
(void)getTlsAbstraction(); // ensure singeton initialization (for correct order of atexit calls)
|
2015-09-16 19:00:36 +08:00
|
|
|
tlsSlots.reserve(32);
|
2015-08-12 21:23:02 +08:00
|
|
|
threads.reserve(32);
|
2021-04-09 23:46:11 +08:00
|
|
|
g_isTlsStorageInitialized = true;
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
~TlsStorage()
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
// TlsStorage object should not be released
|
|
|
|
// There is no reliable way to avoid problems caused by static initialization order fiasco
|
2019-12-04 14:18:36 +08:00
|
|
|
// Don't use logging here
|
|
|
|
fprintf(stderr, "OpenCV FATAL: TlsStorage::~TlsStorage() call is not expected\n");
|
|
|
|
fflush(stderr);
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
void releaseThread(void* tlsValue = NULL)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2019-12-04 14:18:36 +08:00
|
|
|
TlsAbstraction* tls = getTlsAbstraction();
|
|
|
|
if (NULL == tls)
|
2019-12-26 19:45:03 +08:00
|
|
|
return; // TLS singleton is not available (terminated)
|
2019-12-04 14:18:36 +08:00
|
|
|
ThreadData *pTD = tlsValue == NULL ? (ThreadData*)tls->getData() : (ThreadData*)tlsValue;
|
2019-10-13 19:14:41 +08:00
|
|
|
if (pTD == NULL)
|
|
|
|
return; // no OpenCV TLS data for this thread
|
2015-08-12 21:23:02 +08:00
|
|
|
AutoLock guard(mtxGlobalAccess);
|
2019-10-13 19:14:41 +08:00
|
|
|
for (size_t i = 0; i < threads.size(); i++)
|
2014-03-25 04:38:57 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
if (pTD == threads[i])
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
threads[i] = NULL;
|
|
|
|
if (tlsValue == NULL)
|
2019-12-04 14:18:36 +08:00
|
|
|
tls->setData(0);
|
2019-10-13 19:14:41 +08:00
|
|
|
std::vector<void*>& thread_slots = pTD->slots;
|
|
|
|
for (size_t slotIdx = 0; slotIdx < thread_slots.size(); slotIdx++)
|
|
|
|
{
|
|
|
|
void* pData = thread_slots[slotIdx];
|
|
|
|
thread_slots[slotIdx] = NULL;
|
|
|
|
if (!pData)
|
|
|
|
continue;
|
|
|
|
TLSDataContainer* container = tlsSlots[slotIdx].container;
|
|
|
|
if (container)
|
|
|
|
container->deleteDataInstance(pData);
|
|
|
|
else
|
2019-12-04 14:18:36 +08:00
|
|
|
{
|
|
|
|
fprintf(stderr, "OpenCV ERROR: TLS: container for slotIdx=%d is NULL. Can't release thread data\n", (int)slotIdx);
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
2019-10-13 19:14:41 +08:00
|
|
|
}
|
|
|
|
delete pTD;
|
|
|
|
return;
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2014-03-25 04:38:57 +08:00
|
|
|
}
|
2019-12-04 14:18:36 +08:00
|
|
|
fprintf(stderr, "OpenCV WARNING: TLS: Can't release thread TLS data (unknown pointer or data race): %p\n", (void*)pTD); fflush(stderr);
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Reserve TLS storage index
|
2019-10-13 19:14:41 +08:00
|
|
|
size_t reserveSlot(TLSDataContainer* container)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
AutoLock guard(mtxGlobalAccess);
|
2017-07-27 03:45:55 +08:00
|
|
|
CV_Assert(tlsSlotsSize == tlsSlots.size());
|
2015-09-16 19:00:36 +08:00
|
|
|
|
|
|
|
// Find unused slots
|
2017-07-27 03:45:55 +08:00
|
|
|
for(size_t slot = 0; slot < tlsSlotsSize; slot++)
|
2015-09-16 19:00:36 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
if (tlsSlots[slot].container == NULL)
|
2015-09-16 19:00:36 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
tlsSlots[slot].container = container;
|
2015-09-16 19:00:36 +08:00
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new slot
|
2019-10-13 19:14:41 +08:00
|
|
|
tlsSlots.push_back(TlsSlotInfo(container)); tlsSlotsSize++;
|
2017-07-27 03:45:55 +08:00
|
|
|
return tlsSlotsSize - 1;
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2017-02-21 17:48:15 +08:00
|
|
|
// Release TLS storage index and pass associated data to caller
|
2017-02-16 01:20:38 +08:00
|
|
|
void releaseSlot(size_t slotIdx, std::vector<void*> &dataVec, bool keepSlot = false)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
AutoLock guard(mtxGlobalAccess);
|
2017-07-27 03:45:55 +08:00
|
|
|
CV_Assert(tlsSlotsSize == tlsSlots.size());
|
|
|
|
CV_Assert(tlsSlotsSize > slotIdx);
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
for(size_t i = 0; i < threads.size(); i++)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2016-01-14 19:38:37 +08:00
|
|
|
if(threads[i])
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
2016-01-14 19:38:37 +08:00
|
|
|
std::vector<void*>& thread_slots = threads[i]->slots;
|
|
|
|
if (thread_slots.size() > slotIdx && thread_slots[slotIdx])
|
|
|
|
{
|
|
|
|
dataVec.push_back(thread_slots[slotIdx]);
|
2017-02-16 01:20:38 +08:00
|
|
|
thread_slots[slotIdx] = NULL;
|
2016-01-14 19:38:37 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
2015-09-16 19:00:36 +08:00
|
|
|
|
2017-02-16 01:20:38 +08:00
|
|
|
if (!keepSlot)
|
2019-10-13 19:14:41 +08:00
|
|
|
{
|
|
|
|
tlsSlots[slotIdx].container = NULL; // mark slot as free (see reserveSlot() implementation)
|
|
|
|
}
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Get data by TLS storage index
|
|
|
|
void* getData(size_t slotIdx) const
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2017-07-27 22:31:51 +08:00
|
|
|
#ifndef CV_THREAD_SANITIZER
|
2017-07-27 03:45:55 +08:00
|
|
|
CV_Assert(tlsSlotsSize > slotIdx);
|
2017-07-27 22:31:51 +08:00
|
|
|
#endif
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2019-12-04 14:18:36 +08:00
|
|
|
TlsAbstraction* tls = getTlsAbstraction();
|
|
|
|
if (NULL == tls)
|
2019-12-26 19:45:03 +08:00
|
|
|
return NULL; // TLS singleton is not available (terminated)
|
2019-12-04 14:18:36 +08:00
|
|
|
|
|
|
|
ThreadData* threadData = (ThreadData*)tls->getData();
|
2015-08-12 21:23:02 +08:00
|
|
|
if(threadData && threadData->slots.size() > slotIdx)
|
|
|
|
return threadData->slots[slotIdx];
|
|
|
|
|
|
|
|
return NULL;
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-09-16 19:00:36 +08:00
|
|
|
// Gather data from threads by TLS storage index
|
|
|
|
void gather(size_t slotIdx, std::vector<void*> &dataVec)
|
|
|
|
{
|
|
|
|
AutoLock guard(mtxGlobalAccess);
|
2017-07-27 03:45:55 +08:00
|
|
|
CV_Assert(tlsSlotsSize == tlsSlots.size());
|
|
|
|
CV_Assert(tlsSlotsSize > slotIdx);
|
2015-09-16 19:00:36 +08:00
|
|
|
|
|
|
|
for(size_t i = 0; i < threads.size(); i++)
|
|
|
|
{
|
2016-01-14 19:38:37 +08:00
|
|
|
if(threads[i])
|
|
|
|
{
|
|
|
|
std::vector<void*>& thread_slots = threads[i]->slots;
|
|
|
|
if (thread_slots.size() > slotIdx && thread_slots[slotIdx])
|
|
|
|
dataVec.push_back(thread_slots[slotIdx]);
|
|
|
|
}
|
2015-09-16 19:00:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Set data to storage index
|
|
|
|
void setData(size_t slotIdx, void* pData)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2017-07-27 22:31:51 +08:00
|
|
|
#ifndef CV_THREAD_SANITIZER
|
2017-07-27 03:45:55 +08:00
|
|
|
CV_Assert(tlsSlotsSize > slotIdx);
|
2017-07-27 22:31:51 +08:00
|
|
|
#endif
|
2015-08-12 21:23:02 +08:00
|
|
|
|
2019-12-04 14:18:36 +08:00
|
|
|
TlsAbstraction* tls = getTlsAbstraction();
|
|
|
|
if (NULL == tls)
|
2019-12-26 19:45:03 +08:00
|
|
|
return; // TLS singleton is not available (terminated)
|
2019-12-04 14:18:36 +08:00
|
|
|
|
|
|
|
ThreadData* threadData = (ThreadData*)tls->getData();
|
2015-08-12 21:23:02 +08:00
|
|
|
if(!threadData)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
threadData = new ThreadData;
|
2019-12-04 14:18:36 +08:00
|
|
|
tls->setData((void*)threadData);
|
2015-08-12 21:23:02 +08:00
|
|
|
{
|
|
|
|
AutoLock guard(mtxGlobalAccess);
|
2019-10-13 19:14:41 +08:00
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
// Find unused slots
|
|
|
|
for(size_t slot = 0; slot < threads.size(); slot++)
|
|
|
|
{
|
|
|
|
if (threads[slot] == NULL)
|
|
|
|
{
|
|
|
|
threadData->idx = (int)slot;
|
|
|
|
threads[slot] = threadData;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
// Create new slot
|
|
|
|
threadData->idx = threads.size();
|
|
|
|
threads.push_back(threadData);
|
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
|
|
|
|
if(slotIdx >= threadData->slots.size())
|
2015-09-16 19:00:36 +08:00
|
|
|
{
|
2017-07-27 03:45:55 +08:00
|
|
|
AutoLock guard(mtxGlobalAccess); // keep synchronization with gather() calls
|
|
|
|
threadData->slots.resize(slotIdx + 1, NULL);
|
2015-09-16 19:00:36 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
threadData->slots[slotIdx] = pData;
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
Mutex mtxGlobalAccess; // Shared objects operation guard
|
2017-07-27 03:45:55 +08:00
|
|
|
size_t tlsSlotsSize; // equal to tlsSlots.size() in synchronized sections
|
2019-10-13 19:14:41 +08:00
|
|
|
// without synchronization this counter doesn't decrease - it is used for slotIdx sanity checks
|
|
|
|
|
|
|
|
struct TlsSlotInfo
|
|
|
|
{
|
|
|
|
TlsSlotInfo(TLSDataContainer* _container) : container(_container) {}
|
|
|
|
TLSDataContainer* container; // attached container (to dispose data of terminated threads)
|
|
|
|
};
|
|
|
|
std::vector<struct TlsSlotInfo> tlsSlots; // TLS keys state
|
2015-08-12 21:23:02 +08:00
|
|
|
std::vector<ThreadData*> threads; // Array for all allocated data. Thread data pointers are placed here to allow data cleanup
|
2013-12-11 22:49:13 +08:00
|
|
|
};
|
2014-01-19 07:39:50 +08:00
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
// Create global TLS storage object
|
|
|
|
static TlsStorage &getTlsStorage()
|
2014-01-19 07:39:50 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TlsStorage, new TlsStorage())
|
2014-01-19 07:39:50 +08:00
|
|
|
}
|
2013-12-11 22:49:13 +08:00
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
#ifndef _WIN32 // pthread key destructor
|
|
|
|
static void opencv_tls_destructor(void* pData)
|
|
|
|
{
|
2021-04-30 05:25:42 +08:00
|
|
|
if (!g_isTlsStorageInitialized)
|
|
|
|
return; // nothing to release, so prefer to avoid creation of new global structures
|
2019-10-13 19:14:41 +08:00
|
|
|
getTlsStorage().releaseThread(pData);
|
|
|
|
}
|
2019-11-02 03:33:12 +08:00
|
|
|
#else // _WIN32
|
|
|
|
#ifdef CV_USE_FLS
|
|
|
|
static void WINAPI opencv_fls_destructor(void* pData)
|
|
|
|
{
|
2021-04-30 05:25:42 +08:00
|
|
|
// Empiric detection of ExitProcess call
|
|
|
|
DWORD code = STILL_ACTIVE/*259*/;
|
|
|
|
BOOL res = GetExitCodeProcess(GetCurrentProcess(), &code);
|
|
|
|
if (res && code != STILL_ACTIVE)
|
|
|
|
{
|
|
|
|
// Looks like we are in ExitProcess() call
|
|
|
|
// This is FLS specific only because their callback is called before DllMain.
|
|
|
|
// TLS doesn't have similar problem, DllMain() is called first which mark __termination properly.
|
|
|
|
// Note: this workaround conflicts with ExitProcess() steps order described in documentation, however it works:
|
|
|
|
// 3. ... called with DLL_PROCESS_DETACH
|
|
|
|
// 7. The termination status of the process changes from STILL_ACTIVE to the exit value of the process.
|
|
|
|
// (ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess)
|
|
|
|
cv::__termination = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_isTlsStorageInitialized)
|
|
|
|
return; // nothing to release, so prefer to avoid creation of new global structures
|
2019-11-02 03:33:12 +08:00
|
|
|
getTlsStorage().releaseThread(pData);
|
|
|
|
}
|
|
|
|
#endif // CV_USE_FLS
|
|
|
|
#endif // _WIN32
|
2019-10-13 19:14:41 +08:00
|
|
|
|
2021-10-08 09:36:58 +08:00
|
|
|
static TlsStorage* const g_force_initialization_of_TlsStorage
|
2021-10-04 17:40:16 +08:00
|
|
|
#if defined __GNUC__
|
|
|
|
__attribute__((unused))
|
|
|
|
#endif
|
2021-10-08 09:36:58 +08:00
|
|
|
= &getTlsStorage();
|
2021-10-04 17:40:16 +08:00
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
} // namespace details
|
|
|
|
using namespace details;
|
|
|
|
|
2021-04-09 23:46:11 +08:00
|
|
|
void releaseTlsStorageThread()
|
|
|
|
{
|
|
|
|
if (!g_isTlsStorageInitialized)
|
|
|
|
return; // nothing to release, so prefer to avoid creation of new global structures
|
|
|
|
getTlsStorage().releaseThread();
|
|
|
|
}
|
|
|
|
|
2013-12-11 22:49:13 +08:00
|
|
|
TLSDataContainer::TLSDataContainer()
|
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
key_ = (int)getTlsStorage().reserveSlot(this); // Reserve key from TLS storage
|
2012-08-17 21:32:06 +08:00
|
|
|
}
|
|
|
|
|
2013-12-11 22:49:13 +08:00
|
|
|
TLSDataContainer::~TLSDataContainer()
|
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
CV_Assert(key_ == -1); // Key must be released in child object
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-09-16 19:00:36 +08:00
|
|
|
void TLSDataContainer::gatherData(std::vector<void*> &data) const
|
|
|
|
{
|
|
|
|
getTlsStorage().gather(key_, data);
|
|
|
|
}
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
void TLSDataContainer::detachData(std::vector<void*> &data)
|
|
|
|
{
|
|
|
|
getTlsStorage().releaseSlot(key_, data, true);
|
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
void TLSDataContainer::release()
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
if (key_ == -1)
|
|
|
|
return; // already released
|
|
|
|
std::vector<void*> data; data.reserve(32);
|
|
|
|
getTlsStorage().releaseSlot(key_, data, false); // Release key and get stored data for proper destruction
|
2017-02-16 01:20:38 +08:00
|
|
|
key_ = -1;
|
|
|
|
for(size_t i = 0; i < data.size(); i++) // Delete all associated data
|
|
|
|
deleteDataInstance(data[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TLSDataContainer::cleanup()
|
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
std::vector<void*> data; data.reserve(32);
|
2017-02-16 01:20:38 +08:00
|
|
|
getTlsStorage().releaseSlot(key_, data, true); // Extract stored data with removal from TLS tables
|
2017-02-21 17:48:15 +08:00
|
|
|
for(size_t i = 0; i < data.size(); i++) // Delete all associated data
|
2015-08-12 21:23:02 +08:00
|
|
|
deleteDataInstance(data[i]);
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
void* TLSDataContainer::getData() const
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2017-05-25 23:59:01 +08:00
|
|
|
CV_Assert(key_ != -1 && "Can't fetch data from terminated TLS container.");
|
2015-08-12 21:23:02 +08:00
|
|
|
void* pData = getTlsStorage().getData(key_); // Check if data was already allocated
|
|
|
|
if(!pData)
|
2013-12-11 22:49:13 +08:00
|
|
|
{
|
2015-08-12 21:23:02 +08:00
|
|
|
// Create new data instance and save it to TLS storage
|
|
|
|
pData = createDataInstance();
|
|
|
|
getTlsStorage().setData(key_, pData);
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
2015-08-12 21:23:02 +08:00
|
|
|
return pData;
|
2013-12-11 22:49:13 +08:00
|
|
|
}
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
static TLSData<CoreTLSData>& getCoreTlsDataTLS()
|
2015-02-04 18:03:27 +08:00
|
|
|
{
|
2015-06-23 19:31:01 +08:00
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TLSData<CoreTLSData>, new TLSData<CoreTLSData>())
|
2015-02-04 18:03:27 +08:00
|
|
|
}
|
|
|
|
|
2019-10-13 19:14:41 +08:00
|
|
|
CoreTLSData& getCoreTlsData()
|
|
|
|
{
|
|
|
|
return getCoreTlsDataTLS().getRef();
|
|
|
|
}
|
|
|
|
|
2017-07-25 18:23:44 +08:00
|
|
|
#if defined CVAPI_EXPORTS && defined _WIN32 && !defined WINCE
|
2015-08-12 21:23:02 +08:00
|
|
|
#ifdef WINRT
|
|
|
|
#pragma warning(disable:4447) // Disable warning 'main' signature found without threading model
|
|
|
|
#endif
|
|
|
|
|
2015-09-01 05:59:08 +08:00
|
|
|
extern "C"
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved);
|
|
|
|
|
2015-08-12 21:23:02 +08:00
|
|
|
extern "C"
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved)
|
|
|
|
{
|
|
|
|
if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH)
|
|
|
|
{
|
|
|
|
if (lpReserved != NULL) // called after ExitProcess() call
|
|
|
|
{
|
|
|
|
cv::__termination = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Not allowed to free resources if lpReserved is non-null
|
|
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583.aspx
|
2021-04-09 23:46:11 +08:00
|
|
|
releaseTlsStorageThread();
|
2015-08-12 21:23:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
2015-02-04 18:03:27 +08:00
|
|
|
|
2017-05-25 23:59:01 +08:00
|
|
|
|
|
|
|
namespace {
|
2019-12-08 05:41:15 +08:00
|
|
|
|
|
|
|
#ifdef OPENCV_WITH_ITT
|
|
|
|
bool overrideThreadName()
|
|
|
|
{
|
|
|
|
static bool param = utils::getConfigurationParameterBool("OPENCV_TRACE_ITT_SET_THREAD_NAME", false);
|
|
|
|
return param;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-05-25 23:59:01 +08:00
|
|
|
static int g_threadNum = 0;
|
|
|
|
class ThreadID {
|
|
|
|
public:
|
|
|
|
const int id;
|
|
|
|
ThreadID() :
|
|
|
|
id(CV_XADD(&g_threadNum, 1))
|
|
|
|
{
|
|
|
|
#ifdef OPENCV_WITH_ITT
|
2019-12-08 05:41:15 +08:00
|
|
|
if (overrideThreadName())
|
|
|
|
__itt_thread_set_name(cv::format("OpenCVThread-%03d", id).c_str());
|
2017-05-25 23:59:01 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static TLSData<ThreadID>& getThreadIDTLS()
|
|
|
|
{
|
|
|
|
CV_SINGLETON_LAZY_INIT_REF(TLSData<ThreadID>, new TLSData<ThreadID>());
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
int utils::getThreadID() { return getThreadIDTLS().get()->id; }
|
|
|
|
|
2018-09-27 20:52:42 +08:00
|
|
|
|
|
|
|
class ParseError
|
2017-05-25 23:59:01 +08:00
|
|
|
{
|
2018-09-27 20:52:42 +08:00
|
|
|
std::string bad_value;
|
|
|
|
public:
|
2021-03-13 03:17:11 +08:00
|
|
|
ParseError(const std::string &bad_value_) :bad_value(bad_value_) {}
|
2018-09-27 20:52:42 +08:00
|
|
|
std::string toString(const std::string ¶m) const
|
2017-05-25 23:59:01 +08:00
|
|
|
{
|
2018-09-27 20:52:42 +08:00
|
|
|
std::ostringstream out;
|
|
|
|
out << "Invalid value for parameter " << param << ": " << bad_value;
|
|
|
|
return out.str();
|
2017-05-25 23:59:01 +08:00
|
|
|
}
|
2018-09-27 20:52:42 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T parseOption(const std::string &);
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline bool parseOption(const std::string & value)
|
|
|
|
{
|
2017-05-25 23:59:01 +08:00
|
|
|
if (value == "1" || value == "True" || value == "true" || value == "TRUE")
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (value == "0" || value == "False" || value == "false" || value == "FALSE")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2018-09-27 20:52:42 +08:00
|
|
|
throw ParseError(value);
|
2017-05-25 23:59:01 +08:00
|
|
|
}
|
|
|
|
|
2018-09-27 20:52:42 +08:00
|
|
|
template<>
|
|
|
|
inline size_t parseOption(const std::string &value)
|
2017-05-25 23:59:01 +08:00
|
|
|
{
|
|
|
|
size_t pos = 0;
|
|
|
|
for (; pos < value.size(); pos++)
|
|
|
|
{
|
|
|
|
if (!isdigit(value[pos]))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cv::String valueStr = value.substr(0, pos);
|
|
|
|
cv::String suffixStr = value.substr(pos, value.length() - pos);
|
2020-05-26 04:25:18 +08:00
|
|
|
#ifdef CV_CXX11
|
|
|
|
size_t v = (size_t)std::stoull(valueStr);
|
|
|
|
#else
|
|
|
|
size_t v = (size_t)atol(valueStr.c_str());
|
|
|
|
#endif
|
2017-05-25 23:59:01 +08:00
|
|
|
if (suffixStr.length() == 0)
|
|
|
|
return v;
|
|
|
|
else if (suffixStr == "MB" || suffixStr == "Mb" || suffixStr == "mb")
|
|
|
|
return v * 1024 * 1024;
|
|
|
|
else if (suffixStr == "KB" || suffixStr == "Kb" || suffixStr == "kb")
|
|
|
|
return v * 1024;
|
2018-09-27 20:52:42 +08:00
|
|
|
throw ParseError(value);
|
2017-05-25 23:59:01 +08:00
|
|
|
}
|
|
|
|
|
2018-09-27 20:52:42 +08:00
|
|
|
template<>
|
|
|
|
inline cv::String parseOption(const std::string &value)
|
|
|
|
{
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline utils::Paths parseOption(const std::string &value)
|
|
|
|
{
|
|
|
|
utils::Paths result;
|
|
|
|
#ifdef _WIN32
|
|
|
|
const char sep = ';';
|
|
|
|
#else
|
|
|
|
const char sep = ':';
|
|
|
|
#endif
|
|
|
|
size_t start_pos = 0;
|
|
|
|
while (start_pos != std::string::npos)
|
|
|
|
{
|
|
|
|
const size_t pos = value.find(sep, start_pos);
|
|
|
|
const std::string one_piece(value, start_pos, pos == std::string::npos ? pos : pos - start_pos);
|
|
|
|
if (!one_piece.empty())
|
|
|
|
result.push_back(one_piece);
|
|
|
|
start_pos = pos == std::string::npos ? pos : pos + 1;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char * envRead(const char * name)
|
2017-05-25 23:59:01 +08:00
|
|
|
{
|
|
|
|
#ifdef NO_GETENV
|
2018-09-27 20:52:42 +08:00
|
|
|
CV_UNUSED(name);
|
|
|
|
return NULL;
|
2017-05-25 23:59:01 +08:00
|
|
|
#else
|
2018-09-27 20:52:42 +08:00
|
|
|
return getenv(name);
|
2017-05-25 23:59:01 +08:00
|
|
|
#endif
|
2018-09-27 20:52:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
inline T read(const std::string & k, const T & defaultValue)
|
|
|
|
{
|
|
|
|
try
|
2017-05-25 23:59:01 +08:00
|
|
|
{
|
2018-09-27 20:52:42 +08:00
|
|
|
const char * res = envRead(k.c_str());
|
|
|
|
if (res)
|
|
|
|
return parseOption<T>(std::string(res));
|
2017-05-25 23:59:01 +08:00
|
|
|
}
|
2018-09-27 20:52:42 +08:00
|
|
|
catch (const ParseError &err)
|
|
|
|
{
|
|
|
|
CV_Error(cv::Error::StsBadArg, err.toString(k));
|
|
|
|
}
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool utils::getConfigurationParameterBool(const char* name, bool defaultValue)
|
|
|
|
{
|
|
|
|
return read<bool>(name, defaultValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t utils::getConfigurationParameterSizeT(const char* name, size_t defaultValue)
|
|
|
|
{
|
|
|
|
return read<size_t>(name, defaultValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::String utils::getConfigurationParameterString(const char* name, const char* defaultValue)
|
|
|
|
{
|
|
|
|
return read<cv::String>(name, defaultValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
utils::Paths utils::getConfigurationParameterPaths(const char* name, const utils::Paths &defaultValue)
|
|
|
|
{
|
|
|
|
return read<utils::Paths>(name, defaultValue);
|
2017-05-25 23:59:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-03 19:17:28 +08:00
|
|
|
#ifdef CV_COLLECT_IMPL_DATA
|
2015-03-12 22:58:03 +08:00
|
|
|
ImplCollector& getImplData()
|
|
|
|
{
|
2015-06-23 19:31:01 +08:00
|
|
|
CV_SINGLETON_LAZY_INIT_REF(ImplCollector, new ImplCollector())
|
2015-03-12 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2014-10-03 19:17:28 +08:00
|
|
|
void setImpl(int flags)
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
cv::AutoLock lock(getImplData().mutex);
|
|
|
|
|
|
|
|
getImplData().implFlags = flags;
|
|
|
|
getImplData().implCode.clear();
|
|
|
|
getImplData().implFun.clear();
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void addImpl(int flag, const char* func)
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
cv::AutoLock lock(getImplData().mutex);
|
|
|
|
|
|
|
|
getImplData().implFlags |= flag;
|
2014-10-03 19:17:28 +08:00
|
|
|
if(func) // use lazy collection if name was not specified
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
size_t index = getImplData().implCode.size();
|
|
|
|
if(!index || (getImplData().implCode[index-1] != flag || getImplData().implFun[index-1].compare(func))) // avoid duplicates
|
2014-10-03 19:17:28 +08:00
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
getImplData().implCode.push_back(flag);
|
|
|
|
getImplData().implFun.push_back(func);
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getImpl(std::vector<int> &impl, std::vector<String> &funName)
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
cv::AutoLock lock(getImplData().mutex);
|
|
|
|
|
|
|
|
impl = getImplData().implCode;
|
|
|
|
funName = getImplData().implFun;
|
|
|
|
return getImplData().implFlags; // return actual flags for lazy collection
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool useCollection()
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
return getImplData().useCollection;
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void setUseCollection(bool flag)
|
|
|
|
{
|
2015-03-12 22:58:03 +08:00
|
|
|
cv::AutoLock lock(getImplData().mutex);
|
|
|
|
|
|
|
|
getImplData().useCollection = flag;
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-08-15 22:12:45 +08:00
|
|
|
namespace instr
|
|
|
|
{
|
|
|
|
bool useInstrumentation()
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
|
|
|
return getInstrumentStruct().useInstr;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUseInstrumentation(bool flag)
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
|
|
|
getInstrumentStruct().useInstr = flag;
|
|
|
|
#else
|
|
|
|
CV_UNUSED(flag);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
InstrNode* getTrace()
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
|
|
|
return &getInstrumentStruct().rootNode;
|
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void resetTrace()
|
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
|
|
|
getInstrumentStruct().rootNode.removeChilds();
|
|
|
|
getInstrumentTLSStruct().pCurrentNode = &getInstrumentStruct().rootNode;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-08-25 22:26:46 +08:00
|
|
|
void setFlags(FLAGS modeFlags)
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
2016-11-07 17:15:51 +08:00
|
|
|
getInstrumentStruct().flags = modeFlags;
|
2016-08-15 22:12:45 +08:00
|
|
|
#else
|
|
|
|
CV_UNUSED(modeFlags);
|
|
|
|
#endif
|
|
|
|
}
|
2016-08-25 22:26:46 +08:00
|
|
|
FLAGS getFlags()
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
2016-11-07 17:15:51 +08:00
|
|
|
return (FLAGS)getInstrumentStruct().flags;
|
2016-08-15 22:12:45 +08:00
|
|
|
#else
|
2016-08-25 22:26:46 +08:00
|
|
|
return (FLAGS)0;
|
2016-08-15 22:12:45 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
NodeData::NodeData(const char* funName, const char* fileName, int lineNum, void* retAddress, bool alwaysExpand, cv::instr::TYPE instrType, cv::instr::IMPL implType)
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
2018-09-28 03:39:06 +08:00
|
|
|
m_funName = funName ? cv::String(funName) : cv::String(); // std::string doesn't accept NULL
|
2016-11-07 17:15:51 +08:00
|
|
|
m_instrType = instrType;
|
|
|
|
m_implType = implType;
|
|
|
|
m_fileName = fileName;
|
|
|
|
m_lineNum = lineNum;
|
|
|
|
m_retAddress = retAddress;
|
|
|
|
m_alwaysExpand = alwaysExpand;
|
2016-08-15 22:12:45 +08:00
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
m_threads = 1;
|
|
|
|
m_counter = 0;
|
2016-08-25 22:26:46 +08:00
|
|
|
m_ticksTotal = 0;
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
m_funError = false;
|
2016-08-15 22:12:45 +08:00
|
|
|
}
|
|
|
|
NodeData::NodeData(NodeData &ref)
|
|
|
|
{
|
|
|
|
*this = ref;
|
|
|
|
}
|
|
|
|
NodeData& NodeData::operator=(const NodeData &right)
|
|
|
|
{
|
2016-11-07 17:15:51 +08:00
|
|
|
this->m_funName = right.m_funName;
|
|
|
|
this->m_instrType = right.m_instrType;
|
|
|
|
this->m_implType = right.m_implType;
|
|
|
|
this->m_fileName = right.m_fileName;
|
|
|
|
this->m_lineNum = right.m_lineNum;
|
|
|
|
this->m_retAddress = right.m_retAddress;
|
|
|
|
this->m_alwaysExpand = right.m_alwaysExpand;
|
|
|
|
|
|
|
|
this->m_threads = right.m_threads;
|
2016-08-25 22:26:46 +08:00
|
|
|
this->m_counter = right.m_counter;
|
|
|
|
this->m_ticksTotal = right.m_ticksTotal;
|
2016-11-07 17:15:51 +08:00
|
|
|
|
2016-08-25 22:26:46 +08:00
|
|
|
this->m_funError = right.m_funError;
|
2016-11-07 17:15:51 +08:00
|
|
|
|
2016-08-15 22:12:45 +08:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
NodeData::~NodeData()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
bool operator==(const NodeData& left, const NodeData& right)
|
|
|
|
{
|
|
|
|
if(left.m_lineNum == right.m_lineNum && left.m_funName == right.m_funName && left.m_fileName == right.m_fileName)
|
2016-11-07 17:15:51 +08:00
|
|
|
{
|
|
|
|
if(left.m_retAddress == right.m_retAddress || !(cv::instr::getFlags()&cv::instr::FLAGS_EXPAND_SAME_NAMES || left.m_alwaysExpand))
|
|
|
|
return true;
|
|
|
|
}
|
2016-08-15 22:12:45 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ENABLE_INSTRUMENTATION
|
|
|
|
InstrStruct& getInstrumentStruct()
|
|
|
|
{
|
|
|
|
static InstrStruct instr;
|
|
|
|
return instr;
|
|
|
|
}
|
|
|
|
|
|
|
|
InstrTLSStruct& getInstrumentTLSStruct()
|
|
|
|
{
|
|
|
|
return *getInstrumentStruct().tlsStruct.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
InstrNode* getCurrentNode()
|
|
|
|
{
|
|
|
|
return getInstrumentTLSStruct().pCurrentNode;
|
|
|
|
}
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
IntrumentationRegion::IntrumentationRegion(const char* funName, const char* fileName, int lineNum, void *retAddress, bool alwaysExpand, TYPE instrType, IMPL implType)
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
|
|
|
m_disabled = false;
|
|
|
|
m_regionTicks = 0;
|
|
|
|
|
|
|
|
InstrStruct *pStruct = &getInstrumentStruct();
|
|
|
|
if(pStruct->useInstr)
|
|
|
|
{
|
|
|
|
InstrTLSStruct *pTLS = &getInstrumentTLSStruct();
|
|
|
|
|
|
|
|
// Disable in case of failure
|
|
|
|
if(!pTLS->pCurrentNode)
|
|
|
|
{
|
|
|
|
m_disabled = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
int depth = pTLS->pCurrentNode->getDepth();
|
|
|
|
if(pStruct->maxDepth && pStruct->maxDepth <= depth)
|
|
|
|
{
|
|
|
|
m_disabled = true;
|
2016-08-15 22:12:45 +08:00
|
|
|
return;
|
2016-11-07 17:15:51 +08:00
|
|
|
}
|
2016-08-15 22:12:45 +08:00
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
NodeData payload(funName, fileName, lineNum, retAddress, alwaysExpand, instrType, implType);
|
2016-08-15 22:12:45 +08:00
|
|
|
Node<NodeData>* pChild = NULL;
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
if(pStruct->flags&FLAGS_MAPPING)
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
|
|
|
// Critical section
|
|
|
|
cv::AutoLock guard(pStruct->mutexCreate); // Guard from concurrent child creation
|
|
|
|
pChild = pTLS->pCurrentNode->findChild(payload);
|
|
|
|
if(!pChild)
|
|
|
|
{
|
|
|
|
pChild = new Node<NodeData>(payload);
|
|
|
|
pTLS->pCurrentNode->addChild(pChild);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pChild = pTLS->pCurrentNode->findChild(payload);
|
|
|
|
if(!pChild)
|
|
|
|
{
|
2016-11-07 17:15:51 +08:00
|
|
|
m_disabled = true;
|
2016-08-15 22:12:45 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pTLS->pCurrentNode = pChild;
|
|
|
|
|
|
|
|
m_regionTicks = getTickCount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IntrumentationRegion::~IntrumentationRegion()
|
|
|
|
{
|
|
|
|
InstrStruct *pStruct = &getInstrumentStruct();
|
|
|
|
if(pStruct->useInstr)
|
|
|
|
{
|
|
|
|
if(!m_disabled)
|
|
|
|
{
|
|
|
|
InstrTLSStruct *pTLS = &getInstrumentTLSStruct();
|
2016-11-07 17:15:51 +08:00
|
|
|
|
|
|
|
if (pTLS->pCurrentNode->m_payload.m_implType == cv::instr::IMPL_OPENCL &&
|
|
|
|
(pTLS->pCurrentNode->m_payload.m_instrType == cv::instr::TYPE_FUN ||
|
|
|
|
pTLS->pCurrentNode->m_payload.m_instrType == cv::instr::TYPE_WRAPPER))
|
2016-08-15 22:12:45 +08:00
|
|
|
{
|
2016-11-07 17:15:51 +08:00
|
|
|
cv::ocl::finish(); // TODO Support "async" OpenCL instrumentation
|
2016-08-15 22:12:45 +08:00
|
|
|
}
|
|
|
|
|
2016-11-07 17:15:51 +08:00
|
|
|
uint64 ticks = (getTickCount() - m_regionTicks);
|
|
|
|
{
|
|
|
|
cv::AutoLock guard(pStruct->mutexCount); // Concurrent ticks accumulation
|
|
|
|
pTLS->pCurrentNode->m_payload.m_counter++;
|
|
|
|
pTLS->pCurrentNode->m_payload.m_ticksTotal += ticks;
|
|
|
|
pTLS->pCurrentNode->m_payload.m_tls.get()->m_ticksTotal += ticks;
|
2016-08-15 22:12:45 +08:00
|
|
|
}
|
2016-11-07 17:15:51 +08:00
|
|
|
|
|
|
|
pTLS->pCurrentNode = pTLS->pCurrentNode->m_pParent;
|
2016-08-15 22:12:45 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:21:30 +08:00
|
|
|
namespace ipp
|
|
|
|
{
|
|
|
|
|
2017-04-13 20:50:23 +08:00
|
|
|
#ifdef HAVE_IPP
|
2016-08-12 14:50:42 +08:00
|
|
|
struct IPPInitSingleton
|
2015-09-25 23:00:53 +08:00
|
|
|
{
|
|
|
|
public:
|
2016-08-12 14:50:42 +08:00
|
|
|
IPPInitSingleton()
|
2015-09-25 23:00:53 +08:00
|
|
|
{
|
2017-08-17 19:57:58 +08:00
|
|
|
useIPP = true;
|
|
|
|
useIPP_NE = false;
|
|
|
|
ippStatus = 0;
|
|
|
|
funcname = NULL;
|
|
|
|
filename = NULL;
|
|
|
|
linen = 0;
|
|
|
|
cpuFeatures = 0;
|
|
|
|
ippFeatures = 0;
|
|
|
|
ippTopFeatures = 0;
|
|
|
|
pIppLibInfo = NULL;
|
|
|
|
|
|
|
|
ippStatus = ippGetCpuFeatures(&cpuFeatures, NULL);
|
|
|
|
if(ippStatus < 0)
|
|
|
|
{
|
|
|
|
std::cerr << "ERROR: IPP cannot detect CPU features, IPP was disabled " << std::endl;
|
|
|
|
useIPP = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ippFeatures = cpuFeatures;
|
2015-09-25 23:00:53 +08:00
|
|
|
|
|
|
|
const char* pIppEnv = getenv("OPENCV_IPP");
|
|
|
|
cv::String env = pIppEnv;
|
|
|
|
if(env.size())
|
|
|
|
{
|
2018-10-24 20:02:53 +08:00
|
|
|
#if IPP_VERSION_X100 >= 201900
|
|
|
|
const Ipp64u minorFeatures = ippCPUID_MOVBE|ippCPUID_AES|ippCPUID_CLMUL|ippCPUID_ABR|ippCPUID_RDRAND|ippCPUID_F16C|
|
|
|
|
ippCPUID_ADCOX|ippCPUID_RDSEED|ippCPUID_PREFETCHW|ippCPUID_SHA|ippCPUID_MPX|ippCPUID_AVX512CD|ippCPUID_AVX512ER|
|
|
|
|
ippCPUID_AVX512PF|ippCPUID_AVX512BW|ippCPUID_AVX512DQ|ippCPUID_AVX512VL|ippCPUID_AVX512VBMI|ippCPUID_AVX512_4FMADDPS|
|
|
|
|
ippCPUID_AVX512_4VNNIW|ippCPUID_AVX512IFMA;
|
|
|
|
#elif IPP_VERSION_X100 >= 201703
|
2017-09-08 16:08:24 +08:00
|
|
|
const Ipp64u minorFeatures = ippCPUID_MOVBE|ippCPUID_AES|ippCPUID_CLMUL|ippCPUID_ABR|ippCPUID_RDRAND|ippCPUID_F16C|
|
|
|
|
ippCPUID_ADCOX|ippCPUID_RDSEED|ippCPUID_PREFETCHW|ippCPUID_SHA|ippCPUID_MPX|ippCPUID_AVX512CD|ippCPUID_AVX512ER|
|
|
|
|
ippCPUID_AVX512PF|ippCPUID_AVX512BW|ippCPUID_AVX512DQ|ippCPUID_AVX512VL|ippCPUID_AVX512VBMI;
|
|
|
|
#elif IPP_VERSION_X100 >= 201700
|
|
|
|
const Ipp64u minorFeatures = ippCPUID_MOVBE|ippCPUID_AES|ippCPUID_CLMUL|ippCPUID_ABR|ippCPUID_RDRAND|ippCPUID_F16C|
|
|
|
|
ippCPUID_ADCOX|ippCPUID_RDSEED|ippCPUID_PREFETCHW|ippCPUID_SHA|ippCPUID_AVX512CD|ippCPUID_AVX512ER|
|
|
|
|
ippCPUID_AVX512PF|ippCPUID_AVX512BW|ippCPUID_AVX512DQ|ippCPUID_AVX512VL|ippCPUID_AVX512VBMI;
|
|
|
|
#else
|
|
|
|
const Ipp64u minorFeatures = 0;
|
|
|
|
#endif
|
|
|
|
|
2017-08-17 19:57:58 +08:00
|
|
|
env = env.toLowerCase();
|
|
|
|
if(env.substr(0, 2) == "ne")
|
|
|
|
{
|
|
|
|
useIPP_NE = true;
|
|
|
|
env = env.substr(3, env.size());
|
|
|
|
}
|
|
|
|
|
2015-09-25 23:00:53 +08:00
|
|
|
if(env == "disabled")
|
|
|
|
{
|
|
|
|
std::cerr << "WARNING: IPP was disabled by OPENCV_IPP environment variable" << std::endl;
|
|
|
|
useIPP = false;
|
|
|
|
}
|
|
|
|
else if(env == "sse42")
|
2017-09-08 16:08:24 +08:00
|
|
|
ippFeatures = minorFeatures|ippCPUID_SSE2|ippCPUID_SSE3|ippCPUID_SSSE3|ippCPUID_SSE41|ippCPUID_SSE42;
|
2015-09-25 23:00:53 +08:00
|
|
|
else if(env == "avx2")
|
2017-09-08 16:08:24 +08:00
|
|
|
ippFeatures = minorFeatures|ippCPUID_SSE2|ippCPUID_SSE3|ippCPUID_SSSE3|ippCPUID_SSE41|ippCPUID_SSE42|ippCPUID_AVX|ippCPUID_AVX2;
|
|
|
|
#if IPP_VERSION_X100 >= 201700
|
2017-08-17 19:57:58 +08:00
|
|
|
#if defined (_M_AMD64) || defined (__x86_64__)
|
|
|
|
else if(env == "avx512")
|
2017-09-08 16:08:24 +08:00
|
|
|
ippFeatures = minorFeatures|ippCPUID_SSE2|ippCPUID_SSE3|ippCPUID_SSSE3|ippCPUID_SSE41|ippCPUID_SSE42|ippCPUID_AVX|ippCPUID_AVX2|ippCPUID_AVX512F;
|
|
|
|
#endif
|
2015-09-25 23:00:53 +08:00
|
|
|
#endif
|
|
|
|
else
|
2017-08-17 19:57:58 +08:00
|
|
|
std::cerr << "ERROR: Improper value of OPENCV_IPP: " << env.c_str() << ". Correct values are: disabled, sse42, avx2, avx512 (Intel64 only)" << std::endl;
|
|
|
|
|
2017-09-08 16:08:24 +08:00
|
|
|
// Trim unsupported features
|
|
|
|
ippFeatures &= cpuFeatures;
|
2017-08-17 19:57:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Disable AVX1 since we don't track regressions for it. SSE42 will be used instead
|
|
|
|
if(cpuFeatures&ippCPUID_AVX && !(cpuFeatures&ippCPUID_AVX2))
|
2017-09-05 22:10:16 +08:00
|
|
|
ippFeatures &= ~((Ipp64u)ippCPUID_AVX);
|
2017-08-17 19:57:58 +08:00
|
|
|
|
|
|
|
// IPP integrations in OpenCV support only SSE4.2, AVX2 and AVX-512 optimizations.
|
|
|
|
if(!(
|
2017-09-08 16:08:24 +08:00
|
|
|
#if IPP_VERSION_X100 >= 201700
|
2017-08-17 19:57:58 +08:00
|
|
|
cpuFeatures&ippCPUID_AVX512F ||
|
2017-09-08 16:08:24 +08:00
|
|
|
#endif
|
2017-08-17 19:57:58 +08:00
|
|
|
cpuFeatures&ippCPUID_AVX2 ||
|
|
|
|
cpuFeatures&ippCPUID_SSE42
|
|
|
|
))
|
|
|
|
{
|
|
|
|
useIPP = false;
|
|
|
|
return;
|
2015-09-25 23:00:53 +08:00
|
|
|
}
|
|
|
|
|
2017-09-08 16:08:24 +08:00
|
|
|
if(ippFeatures == cpuFeatures)
|
|
|
|
IPP_INITIALIZER(0)
|
|
|
|
else
|
|
|
|
IPP_INITIALIZER(ippFeatures)
|
2017-04-13 20:50:23 +08:00
|
|
|
ippFeatures = ippGetEnabledCpuFeatures();
|
2017-08-17 19:57:58 +08:00
|
|
|
|
|
|
|
// Detect top level optimizations to make comparison easier for optimizations dependent conditions
|
2017-09-08 16:08:24 +08:00
|
|
|
#if IPP_VERSION_X100 >= 201700
|
2017-08-17 19:57:58 +08:00
|
|
|
if(ippFeatures&ippCPUID_AVX512F)
|
|
|
|
{
|
|
|
|
if((ippFeatures&ippCPUID_AVX512_SKX) == ippCPUID_AVX512_SKX)
|
|
|
|
ippTopFeatures = ippCPUID_AVX512_SKX;
|
|
|
|
else if((ippFeatures&ippCPUID_AVX512_KNL) == ippCPUID_AVX512_KNL)
|
|
|
|
ippTopFeatures = ippCPUID_AVX512_KNL;
|
|
|
|
else
|
|
|
|
ippTopFeatures = ippCPUID_AVX512F; // Unknown AVX512 configuration
|
|
|
|
}
|
2017-09-08 16:08:24 +08:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if(ippFeatures&ippCPUID_AVX2)
|
2017-08-17 19:57:58 +08:00
|
|
|
ippTopFeatures = ippCPUID_AVX2;
|
|
|
|
else if(ippFeatures&ippCPUID_SSE42)
|
|
|
|
ippTopFeatures = ippCPUID_SSE42;
|
|
|
|
|
|
|
|
pIppLibInfo = ippiGetLibVersion();
|
2020-12-02 08:21:19 +08:00
|
|
|
|
|
|
|
// workaround: https://github.com/opencv/opencv/issues/12959
|
|
|
|
std::string ippName(pIppLibInfo->Name ? pIppLibInfo->Name : "");
|
|
|
|
if (ippName.find("SSE4.2") != std::string::npos)
|
|
|
|
{
|
|
|
|
ippTopFeatures = ippCPUID_SSE42;
|
|
|
|
}
|
2015-09-25 23:00:53 +08:00
|
|
|
}
|
|
|
|
|
2017-08-17 19:57:58 +08:00
|
|
|
public:
|
|
|
|
bool useIPP;
|
|
|
|
bool useIPP_NE;
|
2015-09-25 23:00:53 +08:00
|
|
|
|
2017-08-17 19:57:58 +08:00
|
|
|
int ippStatus; // 0 - all is ok, -1 - IPP functions failed
|
2015-09-25 23:00:53 +08:00
|
|
|
const char *funcname;
|
|
|
|
const char *filename;
|
|
|
|
int linen;
|
2017-04-13 20:50:23 +08:00
|
|
|
Ipp64u ippFeatures;
|
2017-08-17 19:57:58 +08:00
|
|
|
Ipp64u cpuFeatures;
|
|
|
|
Ipp64u ippTopFeatures;
|
|
|
|
const IppLibraryVersion *pIppLibInfo;
|
2015-09-25 23:00:53 +08:00
|
|
|
};
|
|
|
|
|
2016-08-12 14:50:42 +08:00
|
|
|
static IPPInitSingleton& getIPPSingleton()
|
2015-09-25 23:00:53 +08:00
|
|
|
{
|
2016-08-12 14:50:42 +08:00
|
|
|
CV_SINGLETON_LAZY_INIT_REF(IPPInitSingleton, new IPPInitSingleton())
|
2015-09-25 23:00:53 +08:00
|
|
|
}
|
2017-04-13 20:50:23 +08:00
|
|
|
#endif
|
2015-09-25 23:00:53 +08:00
|
|
|
|
2017-04-13 20:50:23 +08:00
|
|
|
#if OPENCV_ABI_COMPATIBILITY > 300
|
|
|
|
unsigned long long getIppFeatures()
|
|
|
|
#else
|
2015-09-25 23:00:53 +08:00
|
|
|
int getIppFeatures()
|
2017-04-13 20:50:23 +08:00
|
|
|
#endif
|
2015-09-25 23:00:53 +08:00
|
|
|
{
|
|
|
|
#ifdef HAVE_IPP
|
2017-04-13 20:50:23 +08:00
|
|
|
#if OPENCV_ABI_COMPATIBILITY > 300
|
2016-08-12 14:50:42 +08:00
|
|
|
return getIPPSingleton().ippFeatures;
|
2017-04-13 20:50:23 +08:00
|
|
|
#else
|
|
|
|
return (int)getIPPSingleton().ippFeatures;
|
|
|
|
#endif
|
2015-09-25 23:00:53 +08:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
2014-04-16 22:50:23 +08:00
|
|
|
|
2020-12-02 08:21:19 +08:00
|
|
|
#ifdef HAVE_IPP
|
2017-08-17 19:57:58 +08:00
|
|
|
unsigned long long getIppTopFeatures()
|
|
|
|
{
|
|
|
|
return getIPPSingleton().ippTopFeatures;
|
|
|
|
}
|
2020-12-02 08:21:19 +08:00
|
|
|
#endif
|
2017-08-17 19:57:58 +08:00
|
|
|
|
2014-04-16 22:50:23 +08:00
|
|
|
void setIppStatus(int status, const char * const _funcname, const char * const _filename, int _line)
|
2014-04-16 19:34:18 +08:00
|
|
|
{
|
2017-04-13 20:50:23 +08:00
|
|
|
#ifdef HAVE_IPP
|
2016-08-12 14:50:42 +08:00
|
|
|
getIPPSingleton().ippStatus = status;
|
|
|
|
getIPPSingleton().funcname = _funcname;
|
|
|
|
getIPPSingleton().filename = _filename;
|
|
|
|
getIPPSingleton().linen = _line;
|
2017-04-13 20:50:23 +08:00
|
|
|
#else
|
|
|
|
CV_UNUSED(status); CV_UNUSED(_funcname); CV_UNUSED(_filename); CV_UNUSED(_line);
|
|
|
|
#endif
|
2014-04-16 19:34:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int getIppStatus()
|
|
|
|
{
|
2017-04-13 20:50:23 +08:00
|
|
|
#ifdef HAVE_IPP
|
2016-08-12 14:50:42 +08:00
|
|
|
return getIPPSingleton().ippStatus;
|
2017-04-13 20:50:23 +08:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2014-04-16 22:50:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
String getIppErrorLocation()
|
|
|
|
{
|
2017-04-13 20:50:23 +08:00
|
|
|
#ifdef HAVE_IPP
|
2016-08-12 14:50:42 +08:00
|
|
|
return format("%s:%d %s", getIPPSingleton().filename ? getIPPSingleton().filename : "", getIPPSingleton().linen, getIPPSingleton().funcname ? getIPPSingleton().funcname : "");
|
2017-04-13 20:50:23 +08:00
|
|
|
#else
|
|
|
|
return String();
|
|
|
|
#endif
|
2014-04-16 19:34:18 +08:00
|
|
|
}
|
|
|
|
|
2017-08-17 19:57:58 +08:00
|
|
|
String getIppVersion()
|
|
|
|
{
|
|
|
|
#ifdef HAVE_IPP
|
|
|
|
const IppLibraryVersion *pInfo = getIPPSingleton().pIppLibInfo;
|
|
|
|
if(pInfo)
|
|
|
|
return format("%s %s %s", pInfo->Name, pInfo->Version, pInfo->BuildDate);
|
|
|
|
else
|
|
|
|
return String("error");
|
|
|
|
#else
|
|
|
|
return String("disabled");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-10-03 19:17:28 +08:00
|
|
|
bool useIPP()
|
|
|
|
{
|
|
|
|
#ifdef HAVE_IPP
|
2019-10-13 19:14:41 +08:00
|
|
|
CoreTLSData& data = getCoreTlsData();
|
|
|
|
if (data.useIPP < 0)
|
2014-10-03 19:17:28 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP = getIPPSingleton().useIPP;
|
2014-10-03 19:17:28 +08:00
|
|
|
}
|
2019-10-13 19:14:41 +08:00
|
|
|
return (data.useIPP > 0);
|
2014-10-03 19:17:28 +08:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUseIPP(bool flag)
|
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
CoreTLSData& data = getCoreTlsData();
|
2014-10-03 19:17:28 +08:00
|
|
|
#ifdef HAVE_IPP
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP = (getIPPSingleton().useIPP)?flag:false;
|
2014-10-03 19:17:28 +08:00
|
|
|
#else
|
2018-09-07 19:33:52 +08:00
|
|
|
CV_UNUSED(flag);
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP = false;
|
2014-10-03 19:17:28 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-10-24 20:02:53 +08:00
|
|
|
bool useIPP_NotExact()
|
2017-08-17 19:57:58 +08:00
|
|
|
{
|
|
|
|
#ifdef HAVE_IPP
|
2019-10-13 19:14:41 +08:00
|
|
|
CoreTLSData& data = getCoreTlsData();
|
|
|
|
if (data.useIPP_NE < 0)
|
2017-08-17 19:57:58 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP_NE = getIPPSingleton().useIPP_NE;
|
2017-08-17 19:57:58 +08:00
|
|
|
}
|
2019-10-13 19:14:41 +08:00
|
|
|
return (data.useIPP_NE > 0);
|
2017-08-17 19:57:58 +08:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-10-24 20:02:53 +08:00
|
|
|
void setUseIPP_NotExact(bool flag)
|
2017-08-17 19:57:58 +08:00
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
CoreTLSData& data = getCoreTlsData();
|
2017-08-17 19:57:58 +08:00
|
|
|
#ifdef HAVE_IPP
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP_NE = flag;
|
2017-08-17 19:57:58 +08:00
|
|
|
#else
|
2018-09-07 19:33:52 +08:00
|
|
|
CV_UNUSED(flag);
|
2019-10-13 19:14:41 +08:00
|
|
|
data.useIPP_NE = false;
|
2017-08-17 19:57:58 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-10-24 20:02:53 +08:00
|
|
|
#if OPENCV_ABI_COMPATIBILITY < 400
|
|
|
|
bool useIPP_NE()
|
|
|
|
{
|
|
|
|
return useIPP_NotExact();
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUseIPP_NE(bool flag)
|
|
|
|
{
|
|
|
|
setUseIPP_NotExact(flag);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-04-17 19:21:30 +08:00
|
|
|
} // namespace ipp
|
|
|
|
|
2022-01-11 11:06:43 +08:00
|
|
|
|
|
|
|
namespace details {
|
|
|
|
|
|
|
|
#if OPENCV_IMPL_FP_HINTS_X86
|
|
|
|
#ifndef _MM_DENORMALS_ZERO_ON // requires pmmintrin.h (SSE3)
|
|
|
|
#define _MM_DENORMALS_ZERO_ON 0x0040
|
|
|
|
#endif
|
|
|
|
#ifndef _MM_DENORMALS_ZERO_MASK // requires pmmintrin.h (SSE3)
|
|
|
|
#define _MM_DENORMALS_ZERO_MASK 0x0040
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void setFPDenormalsIgnoreHint(bool ignore, CV_OUT FPDenormalsModeState& state)
|
|
|
|
{
|
|
|
|
#if OPENCV_IMPL_FP_HINTS_X86
|
|
|
|
unsigned mask = _MM_FLUSH_ZERO_MASK;
|
|
|
|
unsigned value = ignore ? _MM_FLUSH_ZERO_ON : 0;
|
|
|
|
if (featuresEnabled.have[CPU_SSE3])
|
|
|
|
{
|
|
|
|
mask |= _MM_DENORMALS_ZERO_MASK;
|
|
|
|
value |= ignore ? _MM_DENORMALS_ZERO_ON : 0;
|
|
|
|
}
|
|
|
|
const unsigned old_flags = _mm_getcsr();
|
|
|
|
const unsigned old_value = old_flags & mask;
|
|
|
|
unsigned flags = (old_flags & ~mask) | value;
|
|
|
|
CV_LOG_DEBUG(NULL, "core: update FP mxcsr flags = " << cv::format("0x%08x", flags));
|
|
|
|
// save state
|
|
|
|
state.reserved[0] = (uint32_t)mask;
|
|
|
|
state.reserved[1] = (uint32_t)old_value;
|
|
|
|
_mm_setcsr(flags);
|
|
|
|
#else
|
|
|
|
CV_UNUSED(ignore); CV_UNUSED(state);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int saveFPDenormalsState(CV_OUT FPDenormalsModeState& state)
|
|
|
|
{
|
|
|
|
#if OPENCV_IMPL_FP_HINTS_X86
|
|
|
|
unsigned mask = _MM_FLUSH_ZERO_MASK;
|
|
|
|
if (featuresEnabled.have[CPU_SSE3])
|
|
|
|
{
|
|
|
|
mask |= _MM_DENORMALS_ZERO_MASK;
|
|
|
|
}
|
|
|
|
const unsigned old_flags = _mm_getcsr();
|
|
|
|
const unsigned old_value = old_flags & mask;
|
|
|
|
// save state
|
|
|
|
state.reserved[0] = (uint32_t)mask;
|
|
|
|
state.reserved[1] = (uint32_t)old_value;
|
|
|
|
return 2;
|
|
|
|
#else
|
|
|
|
CV_UNUSED(state);
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool restoreFPDenormalsState(const FPDenormalsModeState& state)
|
|
|
|
{
|
|
|
|
#if OPENCV_IMPL_FP_HINTS_X86
|
|
|
|
const unsigned mask = (unsigned)state.reserved[0];
|
|
|
|
CV_DbgAssert(mask != 0); // invalid state (ensure that state is properly saved earlier)
|
|
|
|
const unsigned value = (unsigned)state.reserved[1];
|
|
|
|
CV_DbgCheck((int)value, value == (value & mask), "invalid SSE FP state");
|
|
|
|
const unsigned old_flags = _mm_getcsr();
|
|
|
|
unsigned flags = (old_flags & ~mask) | value;
|
|
|
|
CV_LOG_DEBUG(NULL, "core: restore FP mxcsr flags = " << cv::format("0x%08x", flags));
|
|
|
|
_mm_setcsr(flags);
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
CV_UNUSED(state);
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace details
|
|
|
|
|
|
|
|
|
2015-02-27 17:52:11 +08:00
|
|
|
} // namespace cv
|
|
|
|
|
2015-02-27 00:34:20 +08:00
|
|
|
#ifdef HAVE_TEGRA_OPTIMIZATION
|
|
|
|
|
|
|
|
namespace tegra {
|
|
|
|
|
|
|
|
bool useTegra()
|
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
cv::CoreTLSData* data = cv::getCoreTlsData();
|
2015-02-27 00:34:20 +08:00
|
|
|
|
|
|
|
if (data->useTegra < 0)
|
|
|
|
{
|
|
|
|
const char* pTegraEnv = getenv("OPENCV_TEGRA");
|
|
|
|
if (pTegraEnv && (cv::String(pTegraEnv) == "disabled"))
|
|
|
|
data->useTegra = false;
|
|
|
|
else
|
|
|
|
data->useTegra = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (data->useTegra > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUseTegra(bool flag)
|
|
|
|
{
|
2019-10-13 19:14:41 +08:00
|
|
|
cv::CoreTLSData* data = cv::getCoreTlsData();
|
2015-02-27 00:34:20 +08:00
|
|
|
data->useTegra = flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace tegra
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2013-07-10 21:43:46 +08:00
|
|
|
/* End of file. */
|