gapi_int_executor_tests.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. // Copyright (C) 2018 Intel Corporation
  6. #include "../test_precomp.hpp"
  7. #include "../gapi_mock_kernels.hpp"
  8. namespace opencv_test
  9. {
  10. namespace
  11. {
  12. class GMockExecutable final: public cv::gimpl::GIslandExecutable
  13. {
  14. virtual inline bool canReshape() const override {
  15. return m_priv->m_can_reshape;
  16. }
  17. virtual void reshape(ade::Graph&, const GCompileArgs&) override
  18. {
  19. m_priv->m_reshape_counter++;
  20. }
  21. virtual void handleNewStream() override { }
  22. virtual void run(std::vector<InObj>&&, std::vector<OutObj>&&) { }
  23. virtual bool allocatesOutputs() const override
  24. {
  25. return true;
  26. }
  27. virtual cv::RMat allocate(const cv::GMatDesc&) const override
  28. {
  29. m_priv->m_allocate_counter++;
  30. return cv::RMat();
  31. }
  32. // NB: GMockBackendImpl creates new unique_ptr<GMockExecutable>
  33. // on every compile call. Need to share counters between instances in order
  34. // to validate it in tests.
  35. struct Priv
  36. {
  37. bool m_can_reshape;
  38. int m_reshape_counter;
  39. int m_allocate_counter;
  40. };
  41. std::shared_ptr<Priv> m_priv;
  42. public:
  43. GMockExecutable(bool can_reshape = true)
  44. : m_priv(new Priv{can_reshape, 0, 0})
  45. {
  46. };
  47. void setReshape(bool can_reshape) { m_priv->m_can_reshape = can_reshape; }
  48. int getReshapeCounter() const { return m_priv->m_reshape_counter; }
  49. int getAllocateCounter() const { return m_priv->m_allocate_counter; }
  50. };
  51. class GMockBackendImpl final: public cv::gapi::GBackend::Priv
  52. {
  53. virtual void unpackKernel(ade::Graph &,
  54. const ade::NodeHandle &,
  55. const cv::GKernelImpl &) override { }
  56. virtual EPtr compile(const ade::Graph &,
  57. const cv::GCompileArgs &,
  58. const std::vector<ade::NodeHandle> &) const override
  59. {
  60. ++m_compile_counter;
  61. return EPtr{new GMockExecutable(m_exec)};
  62. }
  63. mutable int m_compile_counter = 0;
  64. GMockExecutable m_exec;
  65. virtual bool controlsMerge() const override {
  66. return true;
  67. }
  68. virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &,
  69. const ade::NodeHandle &,
  70. const ade::NodeHandle &,
  71. const ade::NodeHandle &) const override {
  72. return false;
  73. }
  74. public:
  75. GMockBackendImpl(const GMockExecutable& exec) : m_exec(exec) { };
  76. int getCompileCounter() const { return m_compile_counter; }
  77. };
  78. class GMockFunctor : public gapi::cpu::GOCVFunctor
  79. {
  80. public:
  81. GMockFunctor(cv::gapi::GBackend backend,
  82. const char* id,
  83. const Meta &meta,
  84. const Impl& impl)
  85. : gapi::cpu::GOCVFunctor(id, meta, impl), m_backend(backend)
  86. {
  87. }
  88. cv::gapi::GBackend backend() const override { return m_backend; }
  89. private:
  90. cv::gapi::GBackend m_backend;
  91. };
  92. template<typename K, typename Callable>
  93. GMockFunctor mock_kernel(const cv::gapi::GBackend& backend, Callable c)
  94. {
  95. using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
  96. return GMockFunctor{ backend
  97. , K::id()
  98. , &K::getOutMeta
  99. , std::bind(&P::callFunctor, std::placeholders::_1, c)
  100. };
  101. }
  102. void dummyFooImpl(const cv::Mat&, cv::Mat&) { };
  103. void dummyBarImpl(const cv::Mat&, const cv::Mat&, cv::Mat&) { };
  104. struct GExecutorReshapeTest: public ::testing::Test
  105. {
  106. GExecutorReshapeTest()
  107. : comp([](){
  108. cv::GMat in;
  109. cv::GMat out = I::Bar::on(I::Foo::on(in), in);
  110. return cv::GComputation(in, out);
  111. })
  112. {
  113. backend_impl1 = std::make_shared<GMockBackendImpl>(island1);
  114. backend1 = cv::gapi::GBackend{backend_impl1};
  115. backend_impl2 = std::make_shared<GMockBackendImpl>(island2);
  116. backend2 = cv::gapi::GBackend{backend_impl2};
  117. auto kernel1 = mock_kernel<I::Foo>(backend1, dummyFooImpl);
  118. auto kernel2 = mock_kernel<I::Bar>(backend2, dummyBarImpl);
  119. pkg = cv::gapi::kernels(kernel1, kernel2);
  120. in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
  121. in_mat2 = cv::Mat::eye(64, 64, CV_8UC1);
  122. }
  123. cv::GComputation comp;
  124. GMockExecutable island1;
  125. std::shared_ptr<GMockBackendImpl> backend_impl1;
  126. cv::gapi::GBackend backend1;
  127. GMockExecutable island2;
  128. std::shared_ptr<GMockBackendImpl> backend_impl2;
  129. cv::gapi::GBackend backend2;
  130. cv::GKernelPackage pkg;
  131. cv::Mat in_mat1, in_mat2, out_mat;;
  132. };
  133. } // anonymous namespace
  134. // FIXME: avoid code duplication
  135. // The below graph and config is taken from ComplexIslands test suite
  136. TEST(GExecutor, SmokeTest)
  137. {
  138. cv::GMat in[2];
  139. cv::GMat tmp[4];
  140. cv::GScalar scl;
  141. cv::GMat out[2];
  142. tmp[0] = cv::gapi::bitwise_not(cv::gapi::bitwise_not(in[0]));
  143. tmp[1] = cv::gapi::boxFilter(in[1], -1, cv::Size(3,3));
  144. tmp[2] = tmp[0] + tmp[1]; // FIXME: handle tmp[2] = tmp[0]+tmp[2] typo
  145. scl = cv::gapi::sum(tmp[1]);
  146. tmp[3] = cv::gapi::medianBlur(tmp[1], 3);
  147. out[0] = tmp[2] + scl;
  148. out[1] = cv::gapi::boxFilter(tmp[3], -1, cv::Size(3,3));
  149. // isl0 #internal1
  150. // ........................... .........
  151. // (in1) -> NotNot ->(tmp0) --> Add ---------> (tmp2) --> AddC -------> (out1)
  152. // :.....................^...: :..^....:
  153. // : :
  154. // : :
  155. // #internal0 : :
  156. // .....................:......... :
  157. // (in2) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
  158. // :..........:..................: isl1
  159. // : ..............................
  160. // `------------> Median -> (tmp3) --> Blur -------> (out2)
  161. // :............................:
  162. cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
  163. cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
  164. cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
  165. cv::Mat in_mat2 = cv::Mat::eye(32, 32, CV_8UC1);
  166. cv::Mat out_gapi[2];
  167. // Run G-API:
  168. cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
  169. .apply(cv::gin(in_mat1, in_mat2), cv::gout(out_gapi[0], out_gapi[1]));
  170. // Run OpenCV
  171. cv::Mat out_ocv[2];
  172. {
  173. cv::Mat ocv_tmp0;
  174. cv::Mat ocv_tmp1;
  175. cv::Mat ocv_tmp2;
  176. cv::Mat ocv_tmp3;
  177. cv::Scalar ocv_scl;
  178. ocv_tmp0 = in_mat1; // skip !(!)
  179. cv::boxFilter(in_mat2, ocv_tmp1, -1, cv::Size(3,3));
  180. ocv_tmp2 = ocv_tmp0 + ocv_tmp1;
  181. ocv_scl = cv::sum(ocv_tmp1);
  182. cv::medianBlur(ocv_tmp1, ocv_tmp3, 3);
  183. out_ocv[0] = ocv_tmp2 + ocv_scl;
  184. cv::boxFilter(ocv_tmp3, out_ocv[1], -1, cv::Size(3,3));
  185. }
  186. EXPECT_EQ(0, cvtest::norm(out_gapi[0], out_ocv[0], NORM_INF));
  187. EXPECT_EQ(0, cvtest::norm(out_gapi[1], out_ocv[1], NORM_INF));
  188. // FIXME: check that GIslandModel has more than 1 island (e.g. fusion
  189. // with breakdown worked)
  190. }
  191. TEST_F(GExecutorReshapeTest, ReshapeInsteadOfRecompile)
  192. {
  193. // NB: Initial state
  194. EXPECT_EQ(0, backend_impl1->getCompileCounter());
  195. EXPECT_EQ(0, backend_impl2->getCompileCounter());
  196. EXPECT_EQ(0, island1.getReshapeCounter());
  197. EXPECT_EQ(0, island2.getReshapeCounter());
  198. // NB: First compilation.
  199. comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
  200. EXPECT_EQ(1, backend_impl1->getCompileCounter());
  201. EXPECT_EQ(1, backend_impl2->getCompileCounter());
  202. EXPECT_EQ(0, island1.getReshapeCounter());
  203. EXPECT_EQ(0, island2.getReshapeCounter());
  204. // NB: GMockBackendImpl implements "reshape" method,
  205. // so it won't be recompiled if the meta is changed.
  206. comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
  207. EXPECT_EQ(1, backend_impl1->getCompileCounter());
  208. EXPECT_EQ(1, backend_impl2->getCompileCounter());
  209. EXPECT_EQ(1, island1.getReshapeCounter());
  210. EXPECT_EQ(1, island2.getReshapeCounter());
  211. }
  212. TEST_F(GExecutorReshapeTest, OneBackendNotReshapable)
  213. {
  214. // NB: Make first island not reshapable
  215. island1.setReshape(false);
  216. // NB: Initial state
  217. EXPECT_EQ(0, backend_impl1->getCompileCounter());
  218. EXPECT_EQ(0, island1.getReshapeCounter());
  219. EXPECT_EQ(0, backend_impl2->getCompileCounter());
  220. EXPECT_EQ(0, island2.getReshapeCounter());
  221. // NB: First compilation.
  222. comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
  223. EXPECT_EQ(1, backend_impl1->getCompileCounter());
  224. EXPECT_EQ(1, backend_impl2->getCompileCounter());
  225. EXPECT_EQ(0, island1.getReshapeCounter());
  226. EXPECT_EQ(0, island2.getReshapeCounter());
  227. // NB: Since one of islands isn't reshapable
  228. // the entire graph isn't reshapable as well.
  229. comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
  230. EXPECT_EQ(2, backend_impl1->getCompileCounter());
  231. EXPECT_EQ(2, backend_impl2->getCompileCounter());
  232. EXPECT_EQ(0, island1.getReshapeCounter());
  233. EXPECT_EQ(0, island2.getReshapeCounter());
  234. }
  235. TEST_F(GExecutorReshapeTest, ReshapeCallAllocate)
  236. {
  237. // NB: Initial state
  238. EXPECT_EQ(0, island1.getAllocateCounter());
  239. EXPECT_EQ(0, island1.getReshapeCounter());
  240. // NB: First compilation.
  241. comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
  242. EXPECT_EQ(1, island1.getAllocateCounter());
  243. EXPECT_EQ(0, island1.getReshapeCounter());
  244. // NB: The entire graph is reshapable, so it won't be recompiled, but reshaped.
  245. // Check that reshape call "allocate" to reallocate buffers.
  246. comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
  247. EXPECT_EQ(2, island1.getAllocateCounter());
  248. EXPECT_EQ(1, island1.getReshapeCounter());
  249. }
  250. // FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
  251. // between executed islands
  252. } // namespace opencv_test