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.
5 // Copyright (C) 2018-2019 Intel Corporation
8 #include "test_precomp.hpp"
9 #include "compiler/transactions.hpp"
11 #include "gapi_mock_kernels.hpp"
13 #include "compiler/gmodel.hpp"
14 #include "compiler/gislandmodel.hpp"
15 #include "compiler/gcompiler.hpp"
20 TEST(IslandFusion, TwoOps_OneIsland)
22 namespace J = Jupiter; // see mock_kernels.cpp
24 // Define a computation:
26 // (in) -> J::Foo1 -> (tmp0) -> J::Foo2 -> (out)
29 // :<----------------------------->:
32 cv::GMat tmp0 = I::Foo::on(in);
33 cv::GMat out = I::Foo::on(tmp0);
34 cv::GComputation cc(in, out);
36 // Prepare compilation parameters manually
37 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
38 const auto pkg = cv::gapi::kernels<J::Foo>();
40 // Directly instantiate G-API graph compiler and run partial compilation
41 cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
42 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
43 compiler.runPasses(*graph);
45 // Inspect the graph and verify the islands configuration
46 cv::gimpl::GModel::ConstGraph gm(*graph);
48 auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
49 auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
50 auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
52 // in/out mats shouldn't be assigned to any Island
53 EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
54 EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
56 // Since tmp is surrounded by two J kernels, tmp should be assigned
58 EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
61 TEST(IslandFusion, TwoOps_TwoIslands)
63 namespace J = Jupiter; // see mock_kernels.cpp
64 namespace S = Saturn; // see mock_kernels.cpp
66 // Define a computation:
68 // (in) -> J::Foo --> (tmp0) -> S::Bar --> (out)
71 // :<-------->: :<-------->:
74 cv::GMat tmp0 = I::Foo::on(in);
75 cv::GMat out = I::Bar::on(tmp0, tmp0);
76 cv::GComputation cc(in, out);
78 // Prepare compilation parameters manually
79 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
80 const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();
82 // Directly instantiate G-API graph compiler and run partial compilation
83 cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
84 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
85 compiler.runPasses(*graph);
87 // Inspect the graph and verify the islands configuration
88 cv::gimpl::GModel::ConstGraph gm(*graph);
90 auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
91 auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
92 auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
94 // in/tmp/out mats shouldn't be assigned to any Island
95 EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
96 EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
97 EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
99 auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
100 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
102 // There should be two islands in the GIslandModel
103 const auto is_island = [&](ade::NodeHandle nh) {
104 return (cv::gimpl::NodeKind::ISLAND
105 == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
107 const std::size_t num_isl = std::count_if(gim.nodes().begin(),
110 EXPECT_EQ(2u, num_isl);
112 auto isl_foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
113 auto isl_bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
114 ASSERT_NE(nullptr, isl_foo_nh);
115 ASSERT_NE(nullptr, isl_bar_nh);
117 // Islands should be different
118 auto isl_foo_obj = gim.metadata(isl_foo_nh).get<cv::gimpl::FusedIsland>().object;
119 auto isl_bar_obj = gim.metadata(isl_bar_nh).get<cv::gimpl::FusedIsland>().object;
120 EXPECT_FALSE(isl_foo_obj == isl_bar_obj);
123 TEST(IslandFusion, ConsumerHasTwoInputs)
125 namespace J = Jupiter; // see mock_kernels.cpp
127 // Define a computation: island
128 // ............................
129 // (in0) ->:J::Foo -> (tmp) -> S::Bar :--> (out)
130 // :....................^.....:
132 // (in1) -----------------------`
135 // Check that island is build correctly, when consumer has two inputs
138 GMat tmp = I::Foo::on(in[0]);
139 GMat out = I::Bar::on(tmp, in[1]);
141 cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
143 // Prepare compilation parameters manually
144 cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
145 GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
146 const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
148 // Directly instantiate G-API graph compiler and run partial compilation
149 cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
150 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
151 compiler.runPasses(*graph);
153 cv::gimpl::GModel::ConstGraph gm(*graph);
155 auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
156 auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
157 auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
158 auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
160 EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
161 EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
162 EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
163 EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
165 auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
166 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
168 const auto is_island = [&](ade::NodeHandle nh) {
169 return (cv::gimpl::NodeKind::ISLAND
170 == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
172 const std::size_t num_isl = std::count_if(gim.nodes().begin(),
175 EXPECT_EQ(1u, num_isl);
177 auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
178 auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
180 EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
182 EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->inNodes().size()));
183 EXPECT_EQ(1u, static_cast<std::size_t>(isl_nh->outNodes().size()));
186 TEST(IslandFusion, DataNodeUsedDifferentBackend)
188 // Define a computation:
191 // ...........................
192 // (in1) -> :J::Foo--> (tmp) -> J::Foo: --> (out0)
193 // :............|............:
195 // `---->:S::Baz: --> (out1)
198 // Check that the node was not dropped out of the island
199 // because it is used by the kernel from another backend
201 namespace J = Jupiter;
202 namespace S = Saturn;
204 cv::GMat in, tmp, out0;
206 tmp = I::Foo::on(in);
207 out0 = I::Foo::on(tmp);
208 out1 = I::Baz::on(tmp);
210 cv::GComputation cc(cv::GIn(in), cv::GOut(out0, out1));
212 // Prepare compilation parameters manually
213 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
214 const auto pkg = cv::gapi::kernels<J::Foo, S::Baz>();
216 // Directly instantiate G-API graph compiler and run partial compilation
217 cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
218 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
219 compiler.runPasses(*graph);
221 // Inspect the graph and verify the islands configuration
222 cv::gimpl::GModel::ConstGraph gm(*graph);
224 auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
225 auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
226 auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
227 auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
229 EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
231 auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
232 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
234 auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
235 auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
237 EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
239 EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->outNodes().size()));
240 EXPECT_EQ(7u, static_cast<std::size_t>(gm.nodes().size()));
241 EXPECT_EQ(6u, static_cast<std::size_t>(gim.nodes().size()));
244 TEST(IslandFusion, LoopBetweenDifferentBackends)
246 // Define a computation:
249 // .............................
250 // (in) -> :J::Baz -> (tmp0) -> J::Quux: -> (out0)
251 // | :............|..........^....
252 // | ........ | | ........
253 // `---->:S::Foo: `----------|-------->:S::Qux:-> (out1)
254 // :....|.: | :....^.:
256 // `-------------- (tmp1) -----------`
258 // Kernels S::Foo and S::Qux cannot merge, because there will be a cycle between islands
260 namespace J = Jupiter;
261 namespace S = Saturn;
264 cv::GMat in, tmp1, out0, out1;
266 tmp0 = I::Baz::on(in);
267 tmp1 = I::Foo::on(in);
268 out1 = I::Qux::on(tmp1, tmp0);
269 out0 = I::Quux::on(tmp0, tmp1);
271 cv::GComputation cc(cv::GIn(in), cv::GOut(out1, out0));
273 // Prepare compilation parameters manually
274 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
275 const auto pkg = cv::gapi::kernels<J::Baz, J::Quux, S::Foo, S::Qux>();
277 // Directly instantiate G-API graph compiler and run partial compilation
278 cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
279 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
280 compiler.runPasses(*graph);
282 cv::gimpl::GModel::ConstGraph gm(*graph);
283 auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
284 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
286 auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
287 auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
288 auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
289 auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
290 auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
292 EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
293 EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
294 EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
295 // The node does not belong to the island so as not to form a cycle
296 EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
298 EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
300 // There should be three islands in the GIslandModel
301 const auto is_island = [&](ade::NodeHandle nh) {
302 return (cv::gimpl::NodeKind::ISLAND
303 == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
305 const std::size_t num_isl = std::count_if(gim.nodes().begin(),
308 EXPECT_EQ(3u, num_isl);
311 TEST(IslandsFusion, PartionOverlapUserIsland)
313 // Define a computation:
317 // (in0) -> :J::Foo:--> (tmp) ->:S::Bar: --> (out)
321 // (in1) --------------------------`
323 // Check that internal islands does't overlap user island
325 namespace J = Jupiter;
326 namespace S = Saturn;
329 GMat tmp = I::Foo::on(in[0]);
330 GMat out = I::Bar::on(tmp, in[1]);
332 cv::gapi::island("isl0", cv::GIn(tmp, in[1]), cv::GOut(out));
333 cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
335 // Prepare compilation parameters manually
336 cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
337 GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
338 const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
340 // Directly instantiate G-API graph compiler and run partial compilation
341 cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
342 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
343 compiler.runPasses(*graph);
345 cv::gimpl::GModel::ConstGraph gm(*graph);
346 auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
347 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
349 auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
350 auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
351 auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
352 auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
354 auto foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
355 auto foo_obj = gim.metadata(foo_nh).get<cv::gimpl::FusedIsland>().object;
357 auto bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
358 auto bar_obj = gim.metadata(bar_nh).get<cv::gimpl::FusedIsland>().object;
360 EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
361 EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
362 EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
363 EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
364 EXPECT_FALSE(foo_obj->is_user_specified());
365 EXPECT_TRUE(bar_obj->is_user_specified());
368 TEST(IslandsFusion, DISABLED_IslandContainsDifferentBackends)
370 // Define a computation:
373 // ............................
374 // (in0) -> :J::Foo:--> (tmp) -> S::Bar: --> (out)
375 // :..........................:
378 // (in1) --------------------------`
380 // Try create island contains different backends
382 namespace J = Jupiter;
383 namespace S = Saturn;
386 GMat tmp = I::Foo::on(in[0]);
387 GMat out = I::Bar::on(tmp, in[1]);
389 cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(out));
390 cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
392 // Prepare compilation parameters manually
393 cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),
394 GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};
395 const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();
397 // Directly instantiate G-API graph compiler and run partial compilation
398 cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
399 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
400 EXPECT_ANY_THROW(compiler.runPasses(*graph));
403 TEST(IslandFusion, WithLoop)
405 namespace J = Jupiter; // see mock_kernels.cpp
407 // Define a computation:
409 // (in) -> J::Foo --> (tmp0) -> J::Foo --> (tmp1) -> J::Qux -> (out)
411 // '--> J::Baz --> (scl0) --'
413 // The whole thing should be merged to a single island
414 // There's a cycle warning if Foo/Foo/Qux are merged first
415 // Then this island both produces data for Baz and consumes data
416 // from Baz. This is a cycle and it should be avoided by the merging code.
419 cv::GMat tmp0 = I::Foo::on(in);
420 cv::GMat tmp1 = I::Foo::on(tmp0);
421 cv::GScalar scl0 = I::Baz::on(tmp0);
422 cv::GMat out = I::Qux::on(tmp1, scl0);
423 cv::GComputation cc(in, out);
425 // Prepare compilation parameters manually
426 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});
427 const auto pkg = cv::gapi::kernels<J::Foo, J::Baz, J::Qux>();
429 // Directly instantiate G-API graph compiler and run partial compilation
430 cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
431 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
432 compiler.runPasses(*graph);
434 // Inspect the graph and verify the islands configuration
435 cv::gimpl::GModel::ConstGraph gm(*graph);
437 auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
438 auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
439 auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
440 auto scl0_nh = cv::gimpl::GModel::dataNodeOf(gm, scl0);
441 auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
443 // in/out mats shouldn't be assigned to any Island
444 EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
445 EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
447 // tmp0/tmp1/scl should be assigned to island
448 EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
449 EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
450 EXPECT_TRUE(gm.metadata(scl0_nh).contains<cv::gimpl::Island>());
452 // Check that there's a single island object and it contains all
453 // that data object handles
455 cv::gimpl::GModel::ConstGraph cg(*graph);
456 auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
457 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
459 const auto is_island = [&](ade::NodeHandle nh) {
460 return (cv::gimpl::NodeKind::ISLAND
461 == gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
463 const std::size_t num_isl = std::count_if(gim.nodes().begin(),
466 EXPECT_EQ(1u, num_isl);
468 auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
469 auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
470 EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp0_nh));
471 EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp1_nh));
472 EXPECT_TRUE(ade::util::contains(isl_obj->contents(), scl0_nh));
475 TEST(IslandFusion, Regression_ShouldFuseAll)
477 // Initially the merge procedure didn't work as expected and
478 // stopped fusion even if it could be continued (e.g. full
479 // GModel graph could be fused into a single GIsland node).
480 // Example of this is custom RGB 2 YUV pipeline as shown below:
483 cv::GMat y = 0.299f*r + 0.587f*g + 0.114f*b;
484 cv::GMat u = 0.492f*(b - y);
485 cv::GMat v = 0.877f*(r - y);
487 cv::GComputation customCvt({r, g, b}, {y, u, v});
489 const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
491 // Directly instantiate G-API graph compiler and run partial compilation
492 cv::gimpl::GCompiler compiler(customCvt, {in_meta,in_meta,in_meta}, cv::compile_args());
493 cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
494 compiler.runPasses(*graph);
496 cv::gimpl::GModel::ConstGraph cg(*graph);
497 auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
498 cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
500 std::vector<ade::NodeHandle> data_nhs;
501 std::vector<ade::NodeHandle> isl_nhs;
502 for (auto &&nh : gim.nodes())
504 if (gim.metadata(nh).contains<cv::gimpl::FusedIsland>())
505 isl_nhs.push_back(std::move(nh));
506 else if (gim.metadata(nh).contains<cv::gimpl::DataSlot>())
507 data_nhs.push_back(std::move(nh));
508 else FAIL() << "GIslandModel node with unexpected metadata type";
511 EXPECT_EQ(6u, data_nhs.size()); // 3 input nodes + 3 output nodes
512 EXPECT_EQ(1u, isl_nhs.size()); // 1 island
515 // FIXME: add more tests on mixed (hetero) graphs
518 // FIXME: add test on combination of user-specified island
519 // which should be heterogeneous (based on kernel availability)
520 // but as we don't support this, compilation should fail
522 // FIXME: add tests on automatic inferred islands which are
523 // connected via 1) gmat 2) gscalar 3) garray,
524 // check the case with executor
525 // check the case when this 1/2/3 interim object is also gcomputation output
527 } // namespace opencv_test