Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / src / executor / gexecutor.cpp
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-2019 Intel Corporation
6
7
8 #include "precomp.hpp"
9
10 #include <iostream>
11
12 #include <ade/util/zip_range.hpp>
13
14 #include "opencv2/gapi/opencv_includes.hpp"
15 #include "executor/gexecutor.hpp"
16 #include "compiler/passes/passes.hpp"
17
18 cv::gimpl::GExecutor::GExecutor(std::unique_ptr<ade::Graph> &&g_model)
19     : m_orig_graph(std::move(g_model))
20     , m_island_graph(GModel::Graph(*m_orig_graph).metadata()
21                      .get<IslandModel>().model)
22     , m_gm(*m_orig_graph)
23     , m_gim(*m_island_graph)
24 {
25     // NB: Right now GIslandModel is acyclic, so for a naive execution,
26     // simple unrolling to a list of triggers is enough
27
28     // Naive execution model is similar to current CPU (OpenCV) plugin
29     // execution model:
30     // 1. Allocate all internal resources first (NB - CPU plugin doesn't do it)
31     // 2. Put input/output GComputation arguments to the storage
32     // 3. For every Island, prepare vectors of input/output parameter descs
33     // 4. Iterate over a list of operations (sorted in the topological order)
34     // 5. For every operation, form a list of input/output data objects
35     // 6. Run GIslandExecutable
36     // 7. writeBack
37
38     auto sorted = m_gim.metadata().get<ade::passes::TopologicalSortData>();
39     for (auto nh : sorted.nodes())
40     {
41         switch (m_gim.metadata(nh).get<NodeKind>().k)
42         {
43         case NodeKind::ISLAND:
44             {
45                 std::vector<RcDesc> input_rcs;
46                 std::vector<RcDesc> output_rcs;
47                 input_rcs.reserve(nh->inNodes().size());
48                 output_rcs.reserve(nh->outNodes().size());
49
50                 auto xtract = [&](ade::NodeHandle slot_nh, std::vector<RcDesc> &vec) {
51                     const auto orig_data_nh
52                         = m_gim.metadata(slot_nh).get<DataSlot>().original_data_node;
53                     const auto &orig_data_info
54                         = m_gm.metadata(orig_data_nh).get<Data>();
55                     vec.emplace_back(RcDesc{ orig_data_info.rc
56                                            , orig_data_info.shape
57                                            , orig_data_info.ctor});
58                 };
59                 // (3)
60                 for (auto in_slot_nh  : nh->inNodes())  xtract(in_slot_nh,  input_rcs);
61                 for (auto out_slot_nh : nh->outNodes()) xtract(out_slot_nh, output_rcs);
62
63                 m_ops.emplace_back(OpDesc{ std::move(input_rcs)
64                                          , std::move(output_rcs)
65                                          , m_gim.metadata(nh).get<IslandExec>().object});
66             }
67             break;
68
69         case NodeKind::SLOT:
70             {
71                 const auto orig_data_nh
72                     = m_gim.metadata(nh).get<DataSlot>().original_data_node;
73                 // (1)
74                 initResource(orig_data_nh);
75                 m_slots.emplace_back(DataDesc{nh, orig_data_nh});
76             }
77             break;
78
79         default:
80             GAPI_Assert(false);
81             break;
82         } // switch(kind)
83     } // for(gim nodes)
84 }
85
86 void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh)
87 {
88     const Data &d = m_gm.metadata(orig_nh).get<Data>();
89
90     if (   d.storage != Data::Storage::INTERNAL
91         && d.storage != Data::Storage::CONST)
92         return;
93
94     // INTERNALS+CONST only! no need to allocate/reset output objects
95     // to as it is bound externally (e.g. already in the m_res)
96
97     switch (d.shape)
98     {
99     case GShape::GMAT:
100         {
101             const auto desc = util::get<cv::GMatDesc>(d.meta);
102             const auto type = CV_MAKETYPE(desc.depth, desc.chan);
103             m_res.slot<cv::gapi::own::Mat>()[d.rc].create(desc.size, type);
104         }
105         break;
106
107     case GShape::GSCALAR:
108         if (d.storage == Data::Storage::CONST)
109         {
110             auto rc = RcDesc{d.rc, d.shape, d.ctor};
111             magazine::bindInArg(m_res, rc, m_gm.metadata(orig_nh).get<ConstValue>().arg);
112         }
113         break;
114
115     case GShape::GARRAY:
116         // Constructed on Reset, do nothing here
117         break;
118
119     default:
120         GAPI_Assert(false);
121     }
122 }
123
124 void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
125 {
126     // (2)
127     const auto proto = m_gm.metadata().get<Protocol>();
128
129     // Basic check if input/output arguments are correct
130     // FIXME: Move to GCompiled (do once for all GExecutors)
131     if (proto.inputs.size() != args.inObjs.size()) // TODO: Also check types
132     {
133         util::throw_error(std::logic_error
134                           ("Computation's input protocol doesn\'t "
135                            "match actual arguments!"));
136     }
137     if (proto.outputs.size() != args.outObjs.size()) // TODO: Also check types
138     {
139         util::throw_error(std::logic_error
140                           ("Computation's output protocol doesn\'t "
141                            "match actual arguments!"));
142     }
143
144     namespace util = ade::util;
145
146     //ensure that output Mat parameters are correctly allocated
147     for (auto index : util::iota(proto.out_nhs.size()) )     //FIXME: avoid copy of NodeHandle and GRunRsltComp ?
148     {
149         auto& nh = proto.out_nhs.at(index);
150         const Data &d = m_gm.metadata(nh).get<Data>();
151         if (d.shape == GShape::GMAT)
152         {
153             using cv::util::get;
154             const auto desc = get<cv::GMatDesc>(d.meta);
155             const auto type = CV_MAKETYPE(desc.depth, desc.chan);
156
157 #if !defined(GAPI_STANDALONE)
158             // Building as part of OpenCV - follow OpenCV behavior
159             // if output buffer is not enough to hold the result, reallocate it
160             auto& out_mat   = *get<cv::Mat*>(args.outObjs.at(index));
161             out_mat.create(cv::gapi::own::to_ocv(desc.size), type);
162 #else
163             // Building standalone - output buffer should always exist,
164             // and _exact_ match our inferred metadata
165             auto& out_mat   = *get<cv::gapi::own::Mat*>(args.outObjs.at(index));
166             GAPI_Assert(   out_mat.type() == type
167                         && out_mat.data   != nullptr
168                         && out_mat.rows   == desc.size.height
169                         && out_mat.cols   == desc.size.width)
170 #endif // !defined(GAPI_STANDALONE)
171         }
172     }
173     // Update storage with user-passed objects
174     for (auto it : ade::util::zip(ade::util::toRange(proto.inputs),
175                                   ade::util::toRange(args.inObjs)))
176     {
177         magazine::bindInArg(m_res, std::get<0>(it), std::get<1>(it));
178     }
179     for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
180                                   ade::util::toRange(args.outObjs)))
181     {
182         magazine::bindOutArg(m_res, std::get<0>(it), std::get<1>(it));
183     }
184
185     // Reset internal data
186     for (auto &sd : m_slots)
187     {
188         const auto& data = m_gm.metadata(sd.data_nh).get<Data>();
189         magazine::resetInternalData(m_res, data);
190     }
191
192     // Run the script
193     for (auto &op : m_ops)
194     {
195         // (5)
196         using InObj  = GIslandExecutable::InObj;
197         using OutObj = GIslandExecutable::OutObj;
198         std::vector<InObj>  in_objs;
199         std::vector<OutObj> out_objs;
200         in_objs.reserve (op.in_objects.size());
201         out_objs.reserve(op.out_objects.size());
202
203         for (const auto &rc : op.in_objects)
204         {
205             in_objs.emplace_back(InObj{rc, magazine::getArg(m_res, rc)});
206         }
207         for (const auto &rc : op.out_objects)
208         {
209             out_objs.emplace_back(OutObj{rc, magazine::getObjPtr(m_res, rc)});
210         }
211
212         // (6)
213         op.isl_exec->run(std::move(in_objs), std::move(out_objs));
214     }
215
216     // (7)
217     for (auto it : ade::util::zip(ade::util::toRange(proto.outputs),
218                                   ade::util::toRange(args.outObjs)))
219     {
220         magazine::writeBack(m_res, std::get<0>(it), std::get<1>(it));
221     }
222 }
223
224 const cv::gimpl::GModel::Graph& cv::gimpl::GExecutor::model() const
225 {
226     return m_gm;
227 }
228
229 bool cv::gimpl::GExecutor::canReshape() const
230 {
231     // FIXME: Introduce proper reshaping support on GExecutor level
232     // for all cases!
233     return (m_ops.size() == 1) && m_ops[0].isl_exec->canReshape();
234 }
235
236 void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
237 {
238     GAPI_Assert(canReshape());
239     auto& g = *m_orig_graph.get();
240     ade::passes::PassContext ctx{g};
241     passes::initMeta(ctx, inMetas);
242     passes::inferMeta(ctx, true);
243     m_ops[0].isl_exec->reshape(g, args);
244 }