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 <vector> |
11 | #include <stack> |
12 | #include <unordered_map> |
13 | |
14 | #include <ade/util/algorithm.hpp> // any_of |
15 | #include <ade/util/zip_range.hpp> // zip_range, indexed |
16 | |
17 | #include <ade/graph.hpp> |
18 | #include <ade/passes/check_cycles.hpp> |
19 | |
20 | #include "api/gcomputation_priv.hpp" |
21 | #include "api/gnode_priv.hpp" // FIXME: why it is here? |
22 | #include "api/gproto_priv.hpp" // FIXME: why it is here? |
23 | #include "api/gcall_priv.hpp" // FIXME: why it is here? |
24 | |
25 | #include "api/gbackend_priv.hpp" // Backend basic API (newInstance, etc) |
26 | |
27 | #include "compiler/gmodel.hpp" |
28 | #include "compiler/gmodelbuilder.hpp" |
29 | #include "compiler/gcompiler.hpp" |
30 | #include "compiler/gcompiled_priv.hpp" |
31 | #include "compiler/gstreaming_priv.hpp" |
32 | #include "compiler/passes/passes.hpp" |
33 | #include "compiler/passes/pattern_matching.hpp" |
34 | |
35 | #include "executor/gexecutor.hpp" |
36 | #include "executor/gthreadedexecutor.hpp" |
37 | #include "executor/gstreamingexecutor.hpp" |
38 | #include "backends/common/gbackend.hpp" |
39 | #include "backends/common/gmetabackend.hpp" |
40 | #include "backends/streaming/gstreamingbackend.hpp" // cv::gimpl::streaming::kernels() |
41 | |
42 | // <FIXME:> |
43 | #if !defined(GAPI_STANDALONE) |
44 | #include <opencv2/gapi/cpu/core.hpp> // Also directly refer to Core, |
45 | #include <opencv2/gapi/cpu/imgproc.hpp> // ...Imgproc |
46 | #include <opencv2/gapi/cpu/video.hpp> // ...and Video kernel implementations |
47 | #include <opencv2/gapi/render/render.hpp> // render::ocv::backend() |
48 | #endif // !defined(GAPI_STANDALONE) |
49 | // </FIXME:> |
50 | |
51 | #include <opencv2/gapi/gcompoundkernel.hpp> // compound::backend() |
52 | |
53 | #include "logger.hpp" |
54 | |
55 | namespace |
56 | { |
57 | cv::GKernelPackage getKernelPackage(cv::GCompileArgs &args) |
58 | { |
59 | auto withAuxKernels = [](const cv::GKernelPackage& pkg) { |
60 | cv::GKernelPackage aux_pkg; |
61 | for (const auto &b : pkg.backends()) { |
62 | aux_pkg = cv::gapi::combine(lhs: aux_pkg, rhs: b.priv().auxiliaryKernels()); |
63 | } |
64 | // Always include built-in meta<> and copy implementation |
65 | return cv::gapi::combine(a: pkg, |
66 | b: aux_pkg, |
67 | rest: cv::gimpl::meta::kernels(), |
68 | rest: cv::gimpl::streaming::kernels()); |
69 | }; |
70 | |
71 | auto has_use_only = cv::gapi::getCompileArg<cv::gapi::use_only>(args); |
72 | if (has_use_only) |
73 | return withAuxKernels(has_use_only.value().pkg); |
74 | |
75 | static auto ocv_pkg = |
76 | #if !defined(GAPI_STANDALONE) |
77 | cv::gapi::combine(a: cv::gapi::core::cpu::kernels(), |
78 | b: cv::gapi::imgproc::cpu::kernels(), |
79 | rest: cv::gapi::video::cpu::kernels(), |
80 | rest: cv::gapi::render::ocv::kernels(), |
81 | rest: cv::gapi::streaming::kernels()); |
82 | #else |
83 | cv::GKernelPackage(); |
84 | #endif // !defined(GAPI_STANDALONE) |
85 | |
86 | auto user_pkg = cv::gapi::getCompileArg<cv::GKernelPackage>(args); |
87 | auto user_pkg_with_aux = withAuxKernels(user_pkg.value_or(default_value: cv::GKernelPackage{})); |
88 | return cv::gapi::combine(lhs: ocv_pkg, rhs: user_pkg_with_aux); |
89 | } |
90 | |
91 | cv::gapi::GNetPackage getNetworkPackage(cv::GCompileArgs &args) |
92 | { |
93 | return cv::gapi::getCompileArg<cv::gapi::GNetPackage>(args) |
94 | .value_or(default_value: cv::gapi::GNetPackage{}); |
95 | } |
96 | |
97 | cv::util::optional<std::string> getGraphDumpDirectory(cv::GCompileArgs& args) |
98 | { |
99 | auto dump_info = cv::gapi::getCompileArg<cv::graph_dump_path>(args); |
100 | if (!dump_info.has_value()) |
101 | { |
102 | const std::string path = cv::utils::getConfigurationParameterString(name: "GRAPH_DUMP_PATH" ); |
103 | return !path.empty() |
104 | ? cv::util::make_optional(value: path) |
105 | : cv::util::optional<std::string>(); |
106 | } |
107 | else |
108 | { |
109 | return cv::util::make_optional(value&: dump_info.value().m_dump_path); |
110 | } |
111 | } |
112 | |
113 | template<typename C> |
114 | cv::GKernelPackage auxKernelsFrom(const C& c) { |
115 | cv::GKernelPackage result; |
116 | for (const auto &b : c) { |
117 | result = cv::gapi::combine(result, b.priv().auxiliaryKernels()); |
118 | } |
119 | return result; |
120 | } |
121 | |
122 | using adeGraphs = std::vector<std::unique_ptr<ade::Graph>>; |
123 | |
124 | // Creates ADE graphs (patterns and substitutes) from pkg's transformations |
125 | void makeTransformationGraphs(const cv::GKernelPackage& pkg, |
126 | adeGraphs& patterns, |
127 | adeGraphs& substitutes) { |
128 | const auto& transforms = pkg.get_transformations(); |
129 | const auto size = transforms.size(); |
130 | if (0u == size) return; |
131 | |
132 | // pre-generate all required graphs |
133 | patterns.resize(new_size: size); |
134 | substitutes.resize(new_size: size); |
135 | for (auto it : ade::util::zip(ranges: ade::util::toRange(val: transforms), |
136 | ranges: ade::util::toRange(val&: patterns), |
137 | ranges: ade::util::toRange(val&: substitutes))) { |
138 | const auto& t = std::get<0>(t&: it); |
139 | auto& p = std::get<1>(t&: it); |
140 | auto& s = std::get<2>(t&: it); |
141 | p = cv::gimpl::GCompiler::makeGraph(t.pattern().priv()); |
142 | s = cv::gimpl::GCompiler::makeGraph(t.substitute().priv()); |
143 | } |
144 | } |
145 | |
146 | void checkTransformations(const cv::GKernelPackage& pkg, |
147 | const adeGraphs& patterns, |
148 | const adeGraphs& substitutes) { |
149 | const auto& transforms = pkg.get_transformations(); |
150 | const auto size = transforms.size(); |
151 | if (0u == size) return; |
152 | |
153 | GAPI_Assert(size == patterns.size()); |
154 | GAPI_Assert(size == substitutes.size()); |
155 | |
156 | const auto empty = [] (const cv::gimpl::SubgraphMatch& m) { |
157 | return m.inputDataNodes.empty() && m.startOpNodes.empty() |
158 | && m.finishOpNodes.empty() && m.outputDataNodes.empty() |
159 | && m.inputTestDataNodes.empty() && m.outputTestDataNodes.empty(); |
160 | }; |
161 | |
162 | // **FIXME**: verify other types of endless loops. now, only checking if pattern exists in |
163 | // substitute within __the same__ transformation |
164 | for (size_t i = 0; i < size; ++i) { |
165 | const auto& p = patterns[i]; |
166 | const auto& s = substitutes[i]; |
167 | |
168 | auto matchInSubstitute = cv::gimpl::findMatches(patternGraph: *p, compGraph: *s); |
169 | if (!empty(matchInSubstitute)) { |
170 | std::stringstream ss; |
171 | ss << "Error: (in transformation with description: '" |
172 | << transforms[i].description |
173 | << "') pattern is detected inside substitute" ; |
174 | throw std::runtime_error(ss.str()); |
175 | } |
176 | } |
177 | } |
178 | } // anonymous namespace |
179 | |
180 | |
181 | // GCompiler implementation //////////////////////////////////////////////////// |
182 | |
183 | cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c, |
184 | GMetaArgs &&metas, |
185 | GCompileArgs &&args) |
186 | : m_c(c), m_metas(std::move(metas)), m_args(std::move(args)) |
187 | { |
188 | using namespace std::placeholders; |
189 | |
190 | auto kernels_to_use = getKernelPackage(args&: m_args); |
191 | auto networks_to_use = getNetworkPackage(args&: m_args); |
192 | std::unordered_set<cv::gapi::GBackend> all_backends; |
193 | const auto take = [&](std::vector<cv::gapi::GBackend> &&v) { |
194 | all_backends.insert(first: v.begin(), last: v.end()); |
195 | }; |
196 | take(kernels_to_use.backends()); |
197 | take(networks_to_use.backends()); |
198 | |
199 | m_all_kernels = cv::gapi::combine(lhs: kernels_to_use, |
200 | rhs: auxKernelsFrom(c: all_backends)); |
201 | // NB: The expectation in the line above is that |
202 | // NN backends (present here via network package) always add their |
203 | // inference kernels via auxiliary...() |
204 | |
205 | // sanity check transformations |
206 | { |
207 | adeGraphs patterns, substitutes; |
208 | // FIXME: returning vectors of unique_ptrs from makeTransformationGraphs results in |
209 | // compile error (at least) on GCC 9 with usage of copy-ctor of std::unique_ptr, so |
210 | // using initialization by lvalue reference instead |
211 | makeTransformationGraphs(pkg: m_all_kernels, patterns, substitutes); |
212 | checkTransformations(pkg: m_all_kernels, patterns, substitutes); |
213 | |
214 | // NB: saving generated patterns to m_all_patterns to be used later in passes |
215 | m_all_patterns = std::move(patterns); |
216 | } |
217 | |
218 | auto dump_path = getGraphDumpDirectory(args&: m_args); |
219 | |
220 | m_e.addPassStage(stageName: "init" ); |
221 | m_e.addPass(stageName: "init" , passName: "check_cycles" , pass: ade::passes::CheckCycles()); |
222 | m_e.addPass(stageName: "init" , passName: "apply_transformations" , |
223 | pass: std::bind(f&: passes::applyTransformations, args: _1, args: std::cref(t: m_all_kernels), |
224 | args: std::cref(t: m_all_patterns))); // Note: and re-using patterns here |
225 | m_e.addPass(stageName: "init" , passName: "expand_kernels" , |
226 | pass: std::bind(f&: passes::expandKernels, args: _1, |
227 | args&: m_all_kernels)); // NB: package is copied |
228 | m_e.addPass(stageName: "init" , passName: "topo_sort" , pass: ade::passes::TopologicalSort()); |
229 | m_e.addPass(stageName: "init" , passName: "init_islands" , pass&: passes::initIslands); |
230 | m_e.addPass(stageName: "init" , passName: "check_islands" , pass&: passes::checkIslands); |
231 | // TODO: |
232 | // - Check basic graph validity (i.e., all inputs are connected) |
233 | // - Complex dependencies (i.e. parent-child) unrolling |
234 | // - etc, etc, etc |
235 | |
236 | // Remove GCompoundBackend to avoid calling setupBackend() with it in the list |
237 | m_all_kernels.remove(backend: cv::gapi::compound::backend()); |
238 | |
239 | m_e.addPassStage(stageName: "kernels" ); |
240 | m_e.addPass(stageName: "kernels" , passName: "bind_net_params" , |
241 | pass: std::bind(f&: passes::bindNetParams, args: _1, |
242 | args&: networks_to_use)); |
243 | m_e.addPass(stageName: "kernels" , passName: "resolve_kernels" , |
244 | pass: std::bind(f&: passes::resolveKernels, args: _1, |
245 | args: std::ref(t&: m_all_kernels))); // NB: and not copied here |
246 | // (no compound backend present here) |
247 | m_e.addPass(stageName: "kernels" , passName: "check_islands_content" , pass&: passes::checkIslandsContent); |
248 | |
249 | // Special stage for intrinsics handling |
250 | m_e.addPassStage(stageName: "intrin" ); |
251 | m_e.addPass(stageName: "intrin" , passName: "desync" , pass&: passes::intrinDesync); |
252 | m_e.addPass(stageName: "intrin" , passName: "finalizeIntrin" , pass&: passes::intrinFinalize); |
253 | |
254 | //Input metas may be empty when a graph is compiled for streaming |
255 | m_e.addPassStage(stageName: "meta" ); |
256 | if (!m_metas.empty()) |
257 | { |
258 | m_e.addPass(stageName: "meta" , passName: "initialize" , pass: std::bind(f&: passes::initMeta, args: _1, args: std::ref(t: m_metas))); |
259 | m_e.addPass(stageName: "meta" , passName: "propagate" , pass: std::bind(f&: passes::inferMeta, args: _1, args: false)); |
260 | m_e.addPass(stageName: "meta" , passName: "finalize" , pass&: passes::storeResultingMeta); |
261 | // moved to another stage, FIXME: two dumps? |
262 | // m_e.addPass("meta", "dump_dot", passes::dumpDotStdout); |
263 | } |
264 | // Special stage for backend-specific transformations |
265 | // FIXME: document passes hierarchy and order for backend developers |
266 | m_e.addPassStage(stageName: "transform" ); |
267 | |
268 | m_e.addPassStage(stageName: "exec" ); |
269 | m_e.addPass(stageName: "exec" , passName: "fuse_islands" , pass&: passes::fuseIslands); |
270 | m_e.addPass(stageName: "exec" , passName: "sync_islands" , pass&: passes::syncIslandTags); |
271 | |
272 | // FIXME: Since a set of passes is shared between |
273 | // GCompiled/GStreamingCompiled, this pass is added here unconditionally |
274 | // (even if it is not actually required to produce a GCompiled). |
275 | // FIXME: add a better way to do that! |
276 | m_e.addPass(stageName: "exec" , passName: "add_streaming" , pass&: passes::addStreaming); |
277 | |
278 | // Note: Must be called after addStreaming as addStreaming pass |
279 | // can possibly add new nodes to the IslandModel |
280 | m_e.addPass(stageName: "exec" , passName: "sort_islands" , pass&: passes::topoSortIslands); |
281 | |
282 | if (dump_path.has_value()) |
283 | { |
284 | m_e.addPass(stageName: "exec" , passName: "dump_dot" , pass: std::bind(f&: passes::dumpGraph, args: _1, |
285 | args&: dump_path.value())); |
286 | } |
287 | |
288 | // FIXME: This should be called for "ActiveBackends" only (see metadata). |
289 | // However, ActiveBackends are known only after passes are actually executed. |
290 | // At these stage, they are not executed yet. |
291 | ade::ExecutionEngineSetupContext ectx(m_e); |
292 | auto backends = m_all_kernels.backends(); |
293 | for (auto &b : backends) |
294 | { |
295 | b.priv().addBackendPasses(ectx); |
296 | if (!m_metas.empty()) |
297 | { |
298 | b.priv().addMetaSensitiveBackendPasses(ectx); |
299 | } |
300 | } |
301 | } |
302 | |
303 | void cv::gimpl::GCompiler::validateInputMeta() |
304 | { |
305 | // FIXME: implement testing/accessor methods at the Priv's API level? |
306 | if (!util::holds_alternative<GComputation::Priv::Expr>(v: m_c.priv().m_shape)) |
307 | { |
308 | GAPI_LOG_WARNING(NULL, "Metadata validation is not implemented yet for" |
309 | " deserialized graphs!" ); |
310 | return; |
311 | } |
312 | const auto &c_expr = util::get<cv::GComputation::Priv::Expr>(v: m_c.priv().m_shape); |
313 | if (m_metas.size() != c_expr.m_ins.size()) |
314 | { |
315 | util::throw_error(e: std::logic_error |
316 | ("COMPILE: GComputation interface / metadata mismatch! " |
317 | "(expected " + std::to_string(val: c_expr.m_ins.size()) + ", " |
318 | "got " + std::to_string(val: m_metas.size()) + " meta arguments)" )); |
319 | } |
320 | |
321 | const auto meta_matches = [](const GMetaArg &meta, const GProtoArg &proto) { |
322 | switch (proto.index()) |
323 | { |
324 | // FIXME: Auto-generate methods like this from traits: |
325 | case GProtoArg::index_of<cv::GMat>(): |
326 | case GProtoArg::index_of<cv::GMatP>(): |
327 | return util::holds_alternative<cv::GMatDesc>(v: meta); |
328 | |
329 | case GProtoArg::index_of<cv::GFrame>(): |
330 | return util::holds_alternative<cv::GFrameDesc>(v: meta); |
331 | |
332 | case GProtoArg::index_of<cv::GScalar>(): |
333 | return util::holds_alternative<cv::GScalarDesc>(v: meta); |
334 | |
335 | case GProtoArg::index_of<cv::detail::GArrayU>(): |
336 | return util::holds_alternative<cv::GArrayDesc>(v: meta); |
337 | |
338 | case GProtoArg::index_of<cv::detail::GOpaqueU>(): |
339 | return util::holds_alternative<cv::GOpaqueDesc>(v: meta); |
340 | |
341 | default: |
342 | GAPI_Error("InternalError" ); |
343 | } |
344 | return false; // should never happen |
345 | }; |
346 | |
347 | GAPI_LOG_DEBUG(nullptr, "Total count: " << m_metas.size()); |
348 | for (const auto meta_arg_idx : ade::util::indexed(conts: ade::util::zip(ranges: m_metas, ranges: c_expr.m_ins))) |
349 | { |
350 | const auto &meta = std::get<0>(t: ade::util::value(val: meta_arg_idx)); |
351 | const auto &proto = std::get<1>(t: ade::util::value(val: meta_arg_idx)); |
352 | |
353 | const auto index = ade::util::index(val: meta_arg_idx); |
354 | GAPI_LOG_DEBUG(nullptr, "Process index: " << index); |
355 | |
356 | // check types validity |
357 | if (!meta_matches(meta, proto)) |
358 | { |
359 | util::throw_error(e: std::logic_error |
360 | ("GComputation object type / metadata descriptor mismatch " |
361 | "(argument " + std::to_string(val: index) + ")" )); |
362 | // FIXME: report what we've got and what we've expected |
363 | } |
364 | |
365 | // check value consistency |
366 | gimpl::proto::validate_input_meta_arg(meta); //may throw |
367 | } |
368 | // All checks are ok |
369 | } |
370 | |
371 | void cv::gimpl::GCompiler::validateOutProtoArgs() |
372 | { |
373 | // FIXME: implement testing/accessor methods at the Priv's API level? |
374 | if (!util::holds_alternative<GComputation::Priv::Expr>(v: m_c.priv().m_shape)) |
375 | { |
376 | GAPI_LOG_WARNING(NULL, "Output parameter validation is not implemented yet for" |
377 | " deserialized graphs!" ); |
378 | return; |
379 | } |
380 | const auto &c_expr = util::get<cv::GComputation::Priv::Expr>(v: m_c.priv().m_shape); |
381 | for (const auto out_pos : ade::util::indexed(conts: c_expr.m_outs)) |
382 | { |
383 | const auto &node = proto::origin_of(arg: ade::util::value(val: out_pos)).node; |
384 | if (node.shape() != cv::GNode::NodeShape::CALL) |
385 | { |
386 | auto pos = ade::util::index(val: out_pos); |
387 | util::throw_error(e: std::logic_error |
388 | ("Computation output " + std::to_string(val: pos) + |
389 | " is not a result of any operation" )); |
390 | } |
391 | } |
392 | } |
393 | |
394 | cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::generateGraph() |
395 | { |
396 | if (!m_metas.empty()) |
397 | { |
398 | // Metadata may be empty if we're compiling our graph for streaming |
399 | validateInputMeta(); |
400 | } |
401 | validateOutProtoArgs(); |
402 | auto g = makeGraph(m_c.priv()); |
403 | if (!m_metas.empty()) |
404 | { |
405 | GModel::Graph(*g).metadata().set(OriginalInputMeta{.inputMeta: m_metas}); |
406 | } |
407 | // FIXME: remove m_args, remove GCompileArgs from backends' method signatures, |
408 | // rework backends to access GCompileArgs from graph metadata |
409 | GModel::Graph(*g).metadata().set(CompileArgs{.args: m_args}); |
410 | return g; |
411 | } |
412 | |
413 | void cv::gimpl::GCompiler::runPasses(ade::Graph &g) |
414 | { |
415 | m_e.runPasses(graph&: g); |
416 | GAPI_LOG_INFO(NULL, "All compiler passes are successful" ); |
417 | } |
418 | |
419 | void cv::gimpl::GCompiler::compileIslands(ade::Graph &g) |
420 | { |
421 | compileIslands(g, args: m_args); |
422 | } |
423 | |
424 | void cv::gimpl::GCompiler::compileIslands(ade::Graph &g, const cv::GCompileArgs &args) |
425 | { |
426 | GModel::Graph gm(g); |
427 | std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model); |
428 | GIslandModel::Graph gim(*gptr); |
429 | |
430 | GIslandModel::compileIslands(g&: gim, orig_g: g, args); |
431 | } |
432 | |
433 | cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg) |
434 | { |
435 | // This is the final compilation step. Here: |
436 | // - An instance of GExecutor is created. Depending on the platform, |
437 | // build configuration, etc, a GExecutor may be: |
438 | // - a naive single-thread graph interpreter; |
439 | // - a std::thread-based thing |
440 | // - a TBB-based thing, etc. |
441 | // - All this stuff is wrapped into a GCompiled object and returned |
442 | // to user. |
443 | |
444 | // Note: this happens in the last pass ("compile_islands"): |
445 | // - Each GIsland of GIslandModel instantiates its own, |
446 | // backend-specific executable object |
447 | // - Every backend gets a subgraph to execute, and builds |
448 | // an execution plan for it (backend-specific execution) |
449 | // ...before call to produceCompiled(); |
450 | |
451 | GModel::ConstGraph cgr(*pg); |
452 | const auto &outMetas = GModel::ConstGraph(*pg).metadata() |
453 | .get<OutputMeta>().outMeta; |
454 | // FIXME: select which executor will be actually used, |
455 | // make GExecutor abstract. |
456 | |
457 | auto use_threaded_exec = cv::gapi::getCompileArg<cv::use_threaded_executor>(args: m_args); |
458 | std::unique_ptr<GAbstractExecutor> pE; |
459 | if (use_threaded_exec) { |
460 | const auto num_threads = use_threaded_exec.value().num_threads; |
461 | GAPI_LOG_INFO(NULL, "Threaded executor with " << num_threads << " thread(s) will be used" ); |
462 | pE.reset(p: new GThreadedExecutor(num_threads, std::move(pg))); |
463 | } else { |
464 | pE.reset(p: new GExecutor(std::move(pg))); |
465 | } |
466 | GCompiled compiled; |
467 | compiled.priv().setup(metaArgs: m_metas, outMetas, pE: std::move(pE)); |
468 | |
469 | return compiled; |
470 | } |
471 | |
472 | cv::GStreamingCompiled cv::gimpl::GCompiler::produceStreamingCompiled(GPtr &&pg) |
473 | { |
474 | GStreamingCompiled compiled; |
475 | GMetaArgs outMetas; |
476 | |
477 | // FIXME: the whole below construct is ugly, need to revise |
478 | // how G*Compiled learns about its meta. |
479 | if (!m_metas.empty()) |
480 | { |
481 | outMetas = GModel::ConstGraph(*pg).metadata().get<OutputMeta>().outMeta; |
482 | } |
483 | |
484 | GModel::ConstGraph cgr(*pg); |
485 | |
486 | std::unique_ptr<GStreamingExecutor> pE(new GStreamingExecutor(std::move(pg), |
487 | m_args)); |
488 | if (!m_metas.empty() && !outMetas.empty()) |
489 | { |
490 | compiled.priv().setup(metaArgs: m_metas, outMetas, pE: std::move(pE)); |
491 | } |
492 | else if (m_metas.empty() && outMetas.empty()) |
493 | { |
494 | // Otherwise, set it up with executor object only |
495 | compiled.priv().setup(std::move(pE)); |
496 | } |
497 | else GAPI_Error("Impossible happened -- please report a bug" ); |
498 | return compiled; |
499 | } |
500 | |
501 | cv::GCompiled cv::gimpl::GCompiler::compile() |
502 | { |
503 | std::unique_ptr<ade::Graph> pG = generateGraph(); |
504 | runPasses(g&: *pG); |
505 | compileIslands(g&: *pG); |
506 | return produceCompiled(pg: std::move(pG)); |
507 | } |
508 | |
509 | cv::GStreamingCompiled cv::gimpl::GCompiler::compileStreaming() |
510 | { |
511 | // FIXME: self-note to DM: now keep these compile()/compileStreaming() in sync! |
512 | std::unique_ptr<ade::Graph> pG = generateGraph(); |
513 | GModel::Graph(*pG).metadata().set(Streaming{}); |
514 | runPasses(g&: *pG); |
515 | if (!m_metas.empty()) |
516 | { |
517 | // If the metadata has been passed, compile our islands! |
518 | compileIslands(g&: *pG); |
519 | } |
520 | return produceStreamingCompiled(pg: std::move(pG)); |
521 | } |
522 | |
523 | void cv::gimpl::GCompiler::runMetaPasses(ade::Graph &g, const cv::GMetaArgs &metas) |
524 | { |
525 | auto pass_ctx = ade::passes::PassContext{.graph: g}; |
526 | cv::gimpl::passes::initMeta(ctx&: pass_ctx, metas); |
527 | cv::gimpl::passes::inferMeta(ctx&: pass_ctx, meta_is_initialized: true); |
528 | cv::gimpl::passes::storeResultingMeta(ctx&: pass_ctx); |
529 | |
530 | // Also run meta-sensitive backend-specific passes, if there's any. |
531 | // FIXME: This may be hazardous if our backend are not very robust |
532 | // in their passes -- how can we guarantee correct functioning in the |
533 | // future? |
534 | ade::ExecutionEngine engine; |
535 | engine.addPassStage(stageName: "exec" ); // FIXME: Need a better decision on how we replicate |
536 | // our main compiler stages here. |
537 | ade::ExecutionEngineSetupContext ectx(engine); |
538 | |
539 | // NB: &&b or &b doesn't work here since "backends" is a set. Nevermind |
540 | for (auto b : GModel::Graph(g).metadata().get<ActiveBackends>().backends) |
541 | { |
542 | b.priv().addMetaSensitiveBackendPasses(ectx); |
543 | } |
544 | engine.runPasses(graph&: g); |
545 | } |
546 | |
547 | // Creates ADE graph from input/output proto args OR from its |
548 | // deserialized form |
549 | cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::makeGraph(const cv::GComputation::Priv &priv) { |
550 | std::unique_ptr<ade::Graph> pG(new ade::Graph); |
551 | ade::Graph& g = *pG; |
552 | |
553 | if (cv::util::holds_alternative<cv::GComputation::Priv::Expr>(v: priv.m_shape)) { |
554 | auto c_expr = cv::util::get<cv::GComputation::Priv::Expr>(v: priv.m_shape); |
555 | cv::gimpl::GModel::Graph gm(g); |
556 | cv::gimpl::GModel::init(g&: gm); |
557 | cv::gimpl::GModelBuilder builder(g); |
558 | auto proto_slots = builder.put(ins: c_expr.m_ins, outs: c_expr.m_outs); |
559 | |
560 | // Store Computation's protocol in metadata |
561 | cv::gimpl::Protocol p; |
562 | std::tie(args&: p.inputs, args&: p.outputs, args&: p.in_nhs, args&: p.out_nhs) = proto_slots; |
563 | gm.metadata().set(p); |
564 | } else if (cv::util::holds_alternative<cv::GComputation::Priv::Dump>(v: priv.m_shape)) { |
565 | auto c_dump = cv::util::get<cv::GComputation::Priv::Dump>(v: priv.m_shape); |
566 | cv::gapi::s11n::reconstruct(s: c_dump, g); |
567 | } |
568 | return pG; |
569 | } |
570 | |