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-2020 Intel Corporation
6 
7 
8 #include "precomp.hpp"
9 
10 #include <string>
11 #include <sstream> // used in GModel::log
12 
13 
14 #include <ade/util/zip_range.hpp>   // util::indexed
15 #include <ade/util/checked_cast.hpp>
16 
17 #include <opencv2/gapi/gproto.hpp>
18 #include "api/gnode_priv.hpp"
19 #include "compiler/gobjref.hpp"
20 #include "compiler/gmodel.hpp"
21 #include "api/gorigin.hpp"
22 #include "compiler/gmodel_priv.hpp"
23 
24 namespace cv { namespace gimpl {
25 
mkOpNode(GModel::Graph & g,const GKernel & k,const std::vector<GArg> & args,const cv::util::any & params,const std::string & island)26 ade::NodeHandle GModel::mkOpNode(GModel::Graph &g,
27                                  const GKernel &k,
28                                  const std::vector<GArg> &args,
29                                  const cv::util::any &params,
30                                  const std::string &island)
31 {
32     ade::NodeHandle op_h = g.createNode();
33     g.metadata(op_h).set(NodeType{NodeType::OP});
34     //These extra empty {} are to please GCC (-Wmissing-field-initializers)
35     g.metadata(op_h).set(Op{k, args, {}, {}, params});
36     if (!island.empty())
37         g.metadata(op_h).set(Island{island});
38     return op_h;
39 }
40 
mkDataNode(GModel::Graph & g,const GOrigin & origin)41 ade::NodeHandle GModel::mkDataNode(GModel::Graph &g, const GOrigin& origin)
42 {
43     ade::NodeHandle data_h = g.createNode();
44     const auto id = g.metadata().get<DataObjectCounter>().GetNewId(origin.shape);
45     g.metadata(data_h).set(NodeType{NodeType::DATA});
46 
47     GMetaArg meta;
48     Data::Storage storage = Data::Storage::INTERNAL; // By default, all objects are marked INTERNAL
49 
50     if (origin.node.shape() == GNode::NodeShape::CONST_BOUNDED)
51     {
52         auto value = value_of(origin);
53         meta       = descr_of(value);
54         storage    = Data::Storage::CONST_VAL;
55         g.metadata(data_h).set(ConstValue{value});
56     }
57     // FIXME: Sometimes a GArray-related node may be created w/o the
58     // associated host-type constructor (e.g. when the array is
59     // somewhere in the middle of the graph).
60     auto ctor_copy = origin.ctor;
61     g.metadata(data_h).set(Data{origin.shape, id, meta, ctor_copy, origin.kind, storage});
62     return data_h;
63 }
64 
mkDataNode(GModel::Graph & g,const GShape shape)65 ade::NodeHandle GModel::mkDataNode(GModel::Graph &g, const GShape shape)
66 {
67     ade::NodeHandle data_h = g.createNode();
68     g.metadata(data_h).set(NodeType{NodeType::DATA});
69 
70     const auto id = g.metadata().get<DataObjectCounter>().GetNewId(shape);
71     GMetaArg meta;
72     HostCtor ctor;
73     Data::Storage storage = Data::Storage::INTERNAL; // By default, all objects are marked INTERNAL
74     cv::detail::OpaqueKind kind = cv::detail::OpaqueKind::CV_UNKNOWN;
75 
76     g.metadata(data_h).set(Data{shape, id, meta, ctor, kind, storage});
77     return data_h;
78 }
79 
linkIn(Graph & g,ade::NodeHandle opH,ade::NodeHandle objH,std::size_t in_port)80 ade::EdgeHandle GModel::linkIn(Graph &g, ade::NodeHandle opH, ade::NodeHandle objH, std::size_t in_port)
81 {
82     // Check if input is already connected
83     for (const auto& in_e : opH->inEdges())
84     {
85         GAPI_Assert(g.metadata(in_e).get<Input>().port != in_port);
86     }
87 
88     auto &op = g.metadata(opH).get<Op>();
89     auto &gm = g.metadata(objH).get<Data>();
90 
91      // FIXME: check validity using kernel prototype
92     GAPI_Assert(in_port < op.args.size());
93 
94     ade::EdgeHandle eh = g.link(objH, opH);
95     g.metadata(eh).set(Input{in_port});
96 
97     // Replace an API object with a REF (G* -> GOBJREF)
98     op.args[in_port] = cv::GArg(RcDesc{gm.rc, gm.shape, {}});
99 
100     return eh;
101 }
102 
linkOut(Graph & g,ade::NodeHandle opH,ade::NodeHandle objH,std::size_t out_port)103 ade::EdgeHandle GModel::linkOut(Graph &g, ade::NodeHandle opH, ade::NodeHandle objH, std::size_t out_port)
104 {
105     // FIXME: check validity using kernel prototype
106 
107     // Check if output is already connected
108     for (const auto& out_e : opH->outEdges())
109     {
110         GAPI_Assert(g.metadata(out_e).get<Output>().port != out_port);
111     }
112 
113     auto &op = g.metadata(opH).get<Op>();
114     auto &gm = g.metadata(objH).get<Data>();
115 
116     GAPI_Assert(objH->inNodes().size() == 0u);
117 
118     ade::EdgeHandle eh = g.link(opH, objH);
119     g.metadata(eh).set(Output{out_port});
120 
121     // TODO: outs must be allocated according to kernel protocol!
122     const auto storage_with_port = ade::util::checked_cast<std::size_t>(out_port+1);
123     const auto min_out_size = std::max(op.outs.size(), storage_with_port);
124     op.outs.resize(min_out_size, RcDesc{-1,GShape::GMAT,{}}); // FIXME: Invalid shape instead?
125     op.outs[out_port] = RcDesc{gm.rc, gm.shape, {}};
126 
127     return eh;
128 }
129 
orderedInputs(const ConstGraph & g,ade::NodeHandle nh)130 std::vector<ade::NodeHandle> GModel::orderedInputs(const ConstGraph &g, ade::NodeHandle nh)
131 {
132     std::vector<ade::NodeHandle> sorted_in_nhs(nh->inEdges().size());
133     for (const auto& in_eh : nh->inEdges())
134     {
135         const auto port = g.metadata(in_eh).get<cv::gimpl::Input>().port;
136         GAPI_Assert(port < sorted_in_nhs.size());
137         sorted_in_nhs[port] = in_eh->srcNode();
138     }
139     return sorted_in_nhs;
140 }
141 
orderedOutputs(const ConstGraph & g,ade::NodeHandle nh)142 std::vector<ade::NodeHandle> GModel::orderedOutputs(const ConstGraph &g, ade::NodeHandle nh)
143 {
144     std::vector<ade::NodeHandle> sorted_out_nhs(nh->outEdges().size());
145     for (const auto& out_eh : nh->outEdges())
146     {
147         const auto port = g.metadata(out_eh).get<cv::gimpl::Output>().port;
148         GAPI_Assert(port < sorted_out_nhs.size());
149         sorted_out_nhs[port] = out_eh->dstNode();
150     }
151     return sorted_out_nhs;
152 }
153 
init(Graph & g)154 void GModel::init(Graph& g)
155 {
156     g.metadata().set(DataObjectCounter());
157 }
158 
log(Graph & g,ade::NodeHandle nh,std::string && msg,ade::NodeHandle updater)159 void GModel::log(Graph &g, ade::NodeHandle nh, std::string &&msg, ade::NodeHandle updater)
160 {
161     std::string s = std::move(msg);
162     if (updater != nullptr)
163     {
164         std::stringstream fmt;
165         fmt << " (via " << updater << ")";
166         s += fmt.str();
167     }
168 
169     if (g.metadata(nh).contains<Journal>())
170     {
171         g.metadata(nh).get<Journal>().messages.push_back(s);
172     }
173     else
174     {
175         g.metadata(nh).set(Journal{{s}});
176     }
177 }
178 
179 // FIXME:
180 // Unify with GModel::log(.. ade::NodeHandle ..)
log(Graph & g,ade::EdgeHandle eh,std::string && msg,ade::NodeHandle updater)181 void GModel::log(Graph &g, ade::EdgeHandle eh, std::string &&msg, ade::NodeHandle updater)
182 {
183     std::string s = std::move(msg);
184     if (updater != nullptr)
185     {
186         std::stringstream fmt;
187         fmt << " (via " << updater << ")";
188         s += fmt.str();
189     }
190 
191     if (g.metadata(eh).contains<Journal>())
192     {
193         g.metadata(eh).get<Journal>().messages.push_back(s);
194     }
195     else
196     {
197         g.metadata(eh).set(Journal{{s}});
198     }
199 }
200 
log_clear(Graph & g,ade::NodeHandle node)201 void GModel::log_clear(Graph &g, ade::NodeHandle node)
202 {
203     if (g.metadata(node).contains<Journal>())
204     {
205         // according to documentation, clear() doesn't deallocate (__capacity__ of vector preserved)
206         g.metadata(node).get<Journal>().messages.clear();
207     }
208 }
209 
210 
dataNodeOf(const ConstLayoutGraph & g,const GOrigin & origin)211 ade::NodeHandle GModel::detail::dataNodeOf(const ConstLayoutGraph &g, const GOrigin &origin)
212 {
213     // FIXME: Does it still work with graph transformations, e.g. redirectWriter()??
214     return g.metadata().get<Layout>().object_nodes.at(origin);
215 }
216 
redirectReaders(Graph & g,ade::NodeHandle from,ade::NodeHandle to)217 std::vector<ade::EdgeHandle> GModel::redirectReaders(Graph &g, ade::NodeHandle from, ade::NodeHandle to)
218 {
219     std::vector<ade::EdgeHandle> ehh(from->outEdges().begin(), from->outEdges().end());
220     std::vector<ade::EdgeHandle> ohh;
221     ohh.reserve(ehh.size());
222     for (auto e : ehh)
223     {
224         auto dst = e->dstNode();
225         auto input = g.metadata(e).get<Input>();
226         g.erase(e);
227         ohh.push_back(linkIn(g, dst, to, input.port));
228     }
229     return ohh;
230 }
231 
redirectWriter(Graph & g,ade::NodeHandle from,ade::NodeHandle to)232 ade::EdgeHandle GModel::redirectWriter(Graph &g, ade::NodeHandle from, ade::NodeHandle to)
233 {
234     GAPI_Assert(from->inEdges().size() == 1);
235     auto e = from->inEdges().front();
236     auto op = e->srcNode();
237     auto output = g.metadata(e).get<Output>();
238     g.erase(e);
239     return linkOut(g, op, to, output.port);
240 }
241 
collectInputMeta(const GModel::ConstGraph & cg,ade::NodeHandle node)242 GMetaArgs GModel::collectInputMeta(const GModel::ConstGraph &cg, ade::NodeHandle node)
243 {
244     GAPI_Assert(cg.metadata(node).get<NodeType>().t == NodeType::OP);
245     GMetaArgs in_meta_args(cg.metadata(node).get<Op>().args.size());
246 
247     for (const auto &e : node->inEdges())
248     {
249         const auto& in_data = cg.metadata(e->srcNode()).get<Data>();
250         in_meta_args[cg.metadata(e).get<Input>().port] = in_data.meta;
251     }
252 
253     return in_meta_args;
254 }
255 
256 
getInEdgeByPort(const GModel::ConstGraph & cg,const ade::NodeHandle & nh,std::size_t in_port)257 ade::EdgeHandle GModel::getInEdgeByPort(const GModel::ConstGraph& cg,
258                                         const ade::NodeHandle&    nh,
259                                               std::size_t         in_port)
260 {
261     auto inEdges = nh->inEdges();
262     const auto& edge = ade::util::find_if(inEdges, [&](ade::EdgeHandle eh) {
263         return cg.metadata(eh).get<Input>().port == in_port;
264     });
265     GAPI_Assert(edge != inEdges.end());
266     return *edge;
267 }
268 
collectOutputMeta(const GModel::ConstGraph & cg,ade::NodeHandle node)269 GMetaArgs GModel::collectOutputMeta(const GModel::ConstGraph &cg, ade::NodeHandle node)
270 {
271     GAPI_Assert(cg.metadata(node).get<NodeType>().t == NodeType::OP);
272     GMetaArgs out_meta_args(cg.metadata(node).get<Op>().outs.size());
273 
274     for (const auto &e : node->outEdges())
275     {
276         const auto& out_data = cg.metadata(e->dstNode()).get<Data>();
277         out_meta_args[cg.metadata(e).get<Output>().port] = out_data.meta;
278     }
279 
280     return out_meta_args;
281 }
282 
isActive(const GModel::Graph & cg,const cv::gapi::GBackend & backend)283 bool GModel::isActive(const GModel::Graph &cg, const cv::gapi::GBackend &backend)
284 {
285     return ade::util::contains(cg.metadata().get<ActiveBackends>().backends,
286                                backend);
287 }
288 
289 }} // cv::gimpl
290