mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
8.2 KiB
299 lines
8.2 KiB
// This file is part of OpenCV project. |
|
// It is subject to the license terms in the LICENSE file found in the top-level directory |
|
// of this distribution and at http://opencv.org/license.html. |
|
// |
|
// Copyright (C) 2019 Intel Corporation |
|
|
|
|
|
#include "test_precomp.hpp" |
|
#include "opencv2/gapi/gcomputation_async.hpp" |
|
#include "opencv2/gapi/gcompiled_async.hpp" |
|
|
|
#include <condition_variable> |
|
#include <stdexcept> |
|
|
|
namespace opencv_test |
|
{ |
|
//Main idea behind these tests is to have the same test script that is parameterized in order to test all setups (GCompiled vs apply, callback vs future). |
|
//So these differences are factored into devoted helper classes (mixins) which are then used by the common test script by help of CRTP. |
|
//Actual GAPI Computation with parameters to run on is mixed into test via CRTP as well. |
|
|
|
struct SumOfSum2x2 { |
|
cv::GComputation sum_of_sum; |
|
SumOfSum2x2() : sum_of_sum([]{ |
|
cv::GMat in; |
|
cv::GScalar out = cv::gapi::sum(in + in); |
|
return GComputation{in, out}; |
|
}) |
|
{} |
|
|
|
const cv::Size sz{2, 2}; |
|
cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)}; |
|
cv::Scalar out_sc; |
|
|
|
cv::GCompiled compile(){ |
|
return sum_of_sum.compile(descr_of(in_mat)); |
|
} |
|
|
|
cv::GComputation& computation(){ |
|
return sum_of_sum; |
|
} |
|
|
|
cv::GCompileArgs compile_args(){ |
|
return {}; |
|
} |
|
|
|
cv::GRunArgs in_args(){ |
|
return cv::gin(in_mat); |
|
} |
|
|
|
cv::GRunArgsP out_args(){ |
|
return cv::gout(out_sc); |
|
} |
|
|
|
void verify(){ |
|
EXPECT_EQ(8, out_sc[0]); |
|
} |
|
}; |
|
|
|
namespace { |
|
G_TYPED_KERNEL(GThrow, <GMat(GMat)>, "org.opencv.test.throw") |
|
{ |
|
static GMatDesc outMeta(GMatDesc in) { return in; } |
|
|
|
}; |
|
|
|
struct gthrow_exception : std::runtime_error { |
|
using std::runtime_error::runtime_error; |
|
}; |
|
|
|
GAPI_OCV_KERNEL(GThrowImpl, GThrow) |
|
{ |
|
static void run(const cv::Mat& in, cv::Mat&) |
|
{ |
|
//this condition is needed to avoid "Unreachable code" warning on windows inside OCVCallHelper |
|
if (!in.empty()) |
|
{ |
|
throw gthrow_exception{"test"}; |
|
} |
|
} |
|
}; |
|
} |
|
|
|
struct ExceptionOnExecution { |
|
cv::GComputation throwing_gcomp; |
|
ExceptionOnExecution() : throwing_gcomp([]{ |
|
cv::GMat in; |
|
auto gout = GThrow::on(in); |
|
return GComputation{in, gout}; |
|
}) |
|
{} |
|
|
|
|
|
const cv::Size sz{2, 2}; |
|
cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)}; |
|
cv::Mat out; |
|
|
|
cv::GCompiled compile(){ |
|
return throwing_gcomp.compile(descr_of(in_mat), compile_args()); |
|
} |
|
|
|
cv::GComputation& computation(){ |
|
return throwing_gcomp; |
|
} |
|
|
|
cv::GRunArgs in_args(){ |
|
return cv::gin(in_mat); |
|
} |
|
|
|
cv::GRunArgsP out_args(){ |
|
return cv::gout(out); |
|
} |
|
|
|
cv::GCompileArgs compile_args(){ |
|
auto pkg = cv::gapi::kernels<GThrowImpl>(); |
|
return cv::compile_args(pkg); |
|
} |
|
|
|
}; |
|
|
|
template<typename crtp_final_t> |
|
struct crtp_cast { |
|
template<typename crtp_base_t> |
|
static crtp_final_t* crtp_cast_(crtp_base_t* this_) |
|
{ |
|
return static_cast<crtp_final_t*>(this_); |
|
} |
|
}; |
|
|
|
//Test Mixin, hiding details of callback based notification |
|
template<typename crtp_final_t> |
|
struct CallBack: crtp_cast<crtp_final_t> { |
|
std::atomic<bool> callback_called = {false}; |
|
std::mutex mtx; |
|
std::exception_ptr ep; |
|
|
|
std::condition_variable cv; |
|
|
|
std::function<void(std::exception_ptr)> callback(){ |
|
return [&](std::exception_ptr ep_){ |
|
ep = ep_; |
|
callback_called = true; |
|
mtx.lock(); |
|
mtx.unlock(); |
|
cv.notify_one(); |
|
}; |
|
}; |
|
|
|
template<typename... Args > |
|
void start_async(Args&&... args){ |
|
this->crtp_cast_(this)->async(callback(), std::forward<Args>(args)...); |
|
} |
|
|
|
void wait_for_result() |
|
{ |
|
std::unique_lock<std::mutex> lck{mtx}; |
|
cv.wait(lck,[&]{return callback_called == true;}); |
|
if (ep) |
|
{ |
|
std::rethrow_exception(ep); |
|
} |
|
} |
|
}; |
|
|
|
//Test Mixin, hiding details of future based notification |
|
template<typename crtp_final_t> |
|
struct Future: crtp_cast<crtp_final_t> { |
|
std::future<void> f; |
|
|
|
template<typename... Args > |
|
void start_async(Args&&... args){ |
|
f = this->crtp_cast_(this)->async(std::forward<Args>(args)...); |
|
} |
|
|
|
void wait_for_result() |
|
{ |
|
f.get(); |
|
} |
|
}; |
|
|
|
//Test Mixin, hiding details of using compiled GAPI object |
|
template<typename crtp_final_t> |
|
struct AsyncCompiled : crtp_cast<crtp_final_t>{ |
|
|
|
template<typename... Args> |
|
auto async(Args&&... args) -> decltype(cv::gapi::wip::async(std::declval<cv::GCompiled&>(), std::forward<Args>(args)...)){ |
|
auto gcmpld = this->crtp_cast_(this)->compile(); |
|
return cv::gapi::wip::async(gcmpld, std::forward<Args>(args)...); |
|
} |
|
}; |
|
|
|
//Test Mixin, hiding details of calling apply (async_apply) on GAPI Computation object |
|
template<typename crtp_final_t> |
|
struct AsyncApply : crtp_cast<crtp_final_t> { |
|
|
|
template<typename... Args> |
|
auto async(Args&&... args) ->decltype(cv::gapi::wip::async_apply(std::declval<cv::GComputation&>(), std::forward<Args>(args)...)) { |
|
return cv::gapi::wip::async_apply(this->crtp_cast_(this)->computation(), std::forward<Args>(args)..., this->crtp_cast_(this)->compile_args()); |
|
} |
|
}; |
|
|
|
|
|
template<typename case_t> |
|
struct normal: ::testing::Test, case_t{}; |
|
|
|
TYPED_TEST_CASE_P(normal); |
|
|
|
TYPED_TEST_P(normal, basic){ |
|
//Normal scenario: start function asynchronously and wait for the result, and verify it |
|
this->start_async(this->in_args(), this->out_args()); |
|
this->wait_for_result(); |
|
|
|
this->verify(); |
|
} |
|
|
|
REGISTER_TYPED_TEST_CASE_P(normal, |
|
basic |
|
); |
|
|
|
template<typename case_t> |
|
struct exception: ::testing::Test, case_t{}; |
|
TYPED_TEST_CASE_P(exception); |
|
|
|
TYPED_TEST_P(exception, basic){ |
|
//Exceptional scenario: start function asynchronously and make sure exception is passed to the user |
|
this->start_async(this->in_args(), this->out_args()); |
|
EXPECT_THROW(this->wait_for_result(), gthrow_exception); |
|
} |
|
|
|
REGISTER_TYPED_TEST_CASE_P(exception, |
|
basic |
|
); |
|
|
|
template<typename case_t> |
|
struct stress : ::testing::Test{}; |
|
TYPED_TEST_CASE_P(stress); |
|
|
|
TYPED_TEST_P(stress, test){ |
|
//Some stress testing: use a number of threads to start a bunch of async requests |
|
const std::size_t request_per_thread = 10; |
|
const std::size_t number_of_threads = 4; |
|
|
|
auto thread_body = [&](){ |
|
std::vector<TypeParam> requests{request_per_thread}; |
|
for (auto&& r : requests){ |
|
r.start_async(r.in_args(), r.out_args()); |
|
} |
|
|
|
for (auto&& r : requests){ |
|
r.wait_for_result(); |
|
r.verify(); |
|
} |
|
}; |
|
|
|
std::vector<std::thread> pool {number_of_threads}; |
|
for (auto&& t : pool){ |
|
t = std::thread{thread_body}; |
|
} |
|
|
|
for (auto&& t : pool){ |
|
t.join(); |
|
} |
|
} |
|
REGISTER_TYPED_TEST_CASE_P(stress, test); |
|
|
|
//little helpers to match up all combinations of setups |
|
template<typename compute_fixture_t,template <typename> class callback_or_future_t, template <typename> class compiled_or_apply_t> |
|
struct Case |
|
: compute_fixture_t, |
|
callback_or_future_t<Case<compute_fixture_t,callback_or_future_t,compiled_or_apply_t>>, |
|
compiled_or_apply_t <Case<compute_fixture_t,callback_or_future_t,compiled_or_apply_t>> |
|
{}; |
|
|
|
template<typename computation_t> |
|
using cases = ::testing::Types< |
|
Case<computation_t, CallBack, AsyncCompiled>, |
|
Case<computation_t, CallBack, AsyncApply>, |
|
Case<computation_t, Future, AsyncCompiled>, |
|
Case<computation_t, Future, AsyncApply> |
|
>; |
|
INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPINormalFlow_, normal, cases<SumOfSum2x2>); |
|
INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIExceptionHandling_, exception, cases<ExceptionOnExecution>); |
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIStress, stress, cases<SumOfSum2x2>); |
|
|
|
TEST(AsyncAPI, Sample){ |
|
cv::GComputation self_mul([]{ |
|
cv::GMat in; |
|
cv::GMat out = cv::gapi::mul(in, in); |
|
return GComputation{in, out}; |
|
}); |
|
|
|
const cv::Size sz{2, 2}; |
|
cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)}; |
|
cv::Mat out; |
|
|
|
auto f = cv::gapi::wip::async_apply(self_mul,cv::gin(in_mat), cv::gout(out)); |
|
f.wait(); |
|
} |
|
} // namespace opencv_test
|
|
|