Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/gapi/src/compiler/gcompiler.cpp
16337 views
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
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
#include "api/gapi_priv.hpp" // FIXME: why it is here?
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/passes/passes.hpp"
32
33
#include "executor/gexecutor.hpp"
34
#include "backends/common/gbackend.hpp"
35
36
// <FIXME:>
37
#if !defined(GAPI_STANDALONE)
38
#include "opencv2/gapi/cpu/core.hpp" // Also directly refer to Core
39
#include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations
40
#endif // !defined(GAPI_STANDALONE)
41
// </FIXME:>
42
43
#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend()
44
45
#include "logger.hpp"
46
47
namespace
48
{
49
cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args)
50
{
51
static auto ocv_pkg =
52
#if !defined(GAPI_STANDALONE)
53
combine(cv::gapi::core::cpu::kernels(),
54
cv::gapi::imgproc::cpu::kernels(),
55
cv::unite_policy::KEEP);
56
#else
57
cv::gapi::GKernelPackage();
58
#endif // !defined(GAPI_STANDALONE)
59
auto user_pkg = cv::gimpl::getCompileArg<cv::gapi::GKernelPackage>(args);
60
return combine(ocv_pkg, user_pkg.value_or(cv::gapi::GKernelPackage{}), cv::unite_policy::REPLACE);
61
}
62
63
cv::util::optional<std::string> getGraphDumpDirectory(cv::GCompileArgs& args)
64
{
65
auto dump_info = cv::gimpl::getCompileArg<cv::graph_dump_path>(args);
66
if (!dump_info.has_value())
67
{
68
const char* path = std::getenv("GRAPH_DUMP_PATH");
69
return path
70
? cv::util::make_optional(std::string(path))
71
: cv::util::optional<std::string>();
72
}
73
else
74
{
75
return cv::util::make_optional(dump_info.value().m_dump_path);
76
}
77
}
78
} // anonymous namespace
79
80
81
// GCompiler implementation ////////////////////////////////////////////////////
82
83
cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c,
84
GMetaArgs &&metas,
85
GCompileArgs &&args)
86
: m_c(c), m_metas(std::move(metas)), m_args(std::move(args))
87
{
88
using namespace std::placeholders;
89
m_all_kernels = getKernelPackage(m_args);
90
auto lookup_order = getCompileArg<gapi::GLookupOrder>(m_args).value_or(gapi::GLookupOrder());
91
auto dump_path = getGraphDumpDirectory(m_args);
92
93
m_e.addPassStage("init");
94
m_e.addPass("init", "check_cycles", ade::passes::CheckCycles());
95
m_e.addPass("init", "expand_kernels", std::bind(passes::expandKernels, _1,
96
m_all_kernels)); // NB: package is copied
97
m_e.addPass("init", "topo_sort", ade::passes::TopologicalSort());
98
m_e.addPass("init", "init_islands", passes::initIslands);
99
m_e.addPass("init", "check_islands", passes::checkIslands);
100
// TODO:
101
// - Check basic graph validity (i.e., all inputs are connected)
102
// - Complex dependencies (i.e. parent-child) unrolling
103
// - etc, etc, etc
104
105
// Remove GCompoundBackend to avoid calling setupBackend() with it in the list
106
m_all_kernels.remove(cv::gapi::compound::backend());
107
m_e.addPass("init", "resolve_kernels", std::bind(passes::resolveKernels, _1,
108
std::ref(m_all_kernels), // NB: and not copied here
109
lookup_order));
110
111
m_e.addPass("init", "check_islands_content", passes::checkIslandsContent);
112
m_e.addPassStage("meta");
113
m_e.addPass("meta", "initialize", std::bind(passes::initMeta, _1, std::ref(m_metas)));
114
m_e.addPass("meta", "propagate", passes::inferMeta);
115
m_e.addPass("meta", "finalize", passes::storeResultingMeta);
116
// moved to another stage, FIXME: two dumps?
117
// m_e.addPass("meta", "dump_dot", passes::dumpDotStdout);
118
119
// Special stage for backend-specific transformations
120
// FIXME: document passes hierarchy and order for backend developers
121
m_e.addPassStage("transform");
122
123
m_e.addPassStage("exec");
124
m_e.addPass("exec", "fuse_islands", passes::fuseIslands);
125
m_e.addPass("exec", "sync_islands", passes::syncIslandTags);
126
127
if (dump_path.has_value())
128
{
129
m_e.addPass("exec", "dump_dot", std::bind(passes::dumpGraph, _1,
130
dump_path.value()));
131
}
132
133
// Process backends at the last moment (after all G-API passes are added).
134
ade::ExecutionEngineSetupContext ectx(m_e);
135
auto backends = m_all_kernels.backends();
136
for (auto &b : backends)
137
{
138
b.priv().addBackendPasses(ectx);
139
}
140
}
141
142
void cv::gimpl::GCompiler::validateInputMeta()
143
{
144
if (m_metas.size() != m_c.priv().m_ins.size())
145
{
146
util::throw_error(std::logic_error
147
("COMPILE: GComputation interface / metadata mismatch! "
148
"(expected " + std::to_string(m_c.priv().m_ins.size()) + ", "
149
"got " + std::to_string(m_metas.size()) + " meta arguments)"));
150
}
151
152
const auto meta_matches = [](const GMetaArg &meta, const GProtoArg &proto) {
153
switch (proto.index())
154
{
155
// FIXME: Auto-generate methods like this from traits:
156
case GProtoArg::index_of<cv::GMat>():
157
return util::holds_alternative<cv::GMatDesc>(meta);
158
159
case GProtoArg::index_of<cv::GScalar>():
160
return util::holds_alternative<cv::GScalarDesc>(meta);
161
162
case GProtoArg::index_of<cv::detail::GArrayU>():
163
return util::holds_alternative<cv::GArrayDesc>(meta);
164
165
default:
166
GAPI_Assert(false);
167
}
168
return false; // should never happen
169
};
170
171
for (const auto &meta_arg_idx : ade::util::indexed(ade::util::zip(m_metas, m_c.priv().m_ins)))
172
{
173
const auto &meta = std::get<0>(ade::util::value(meta_arg_idx));
174
const auto &proto = std::get<1>(ade::util::value(meta_arg_idx));
175
176
if (!meta_matches(meta, proto))
177
{
178
const auto index = ade::util::index(meta_arg_idx);
179
util::throw_error(std::logic_error
180
("GComputation object type / metadata descriptor mismatch "
181
"(argument " + std::to_string(index) + ")"));
182
// FIXME: report what we've got and what we've expected
183
}
184
}
185
// All checks are ok
186
}
187
188
void cv::gimpl::GCompiler::validateOutProtoArgs()
189
{
190
for (const auto &out_pos : ade::util::indexed(m_c.priv().m_outs))
191
{
192
const auto &node = proto::origin_of(ade::util::value(out_pos)).node;
193
if (node.shape() != cv::GNode::NodeShape::CALL)
194
{
195
auto pos = ade::util::index(out_pos);
196
util::throw_error(std::logic_error
197
("Computation output " + std::to_string(pos) +
198
" is not a result of any operation"));
199
}
200
}
201
}
202
203
cv::gimpl::GCompiler::GPtr cv::gimpl::GCompiler::generateGraph()
204
{
205
validateInputMeta();
206
validateOutProtoArgs();
207
208
// Generate ADE graph from expression-based computation
209
std::unique_ptr<ade::Graph> pG(new ade::Graph);
210
ade::Graph& g = *pG;
211
212
GModel::Graph gm(g);
213
cv::gimpl::GModel::init(gm);
214
cv::gimpl::GModelBuilder builder(g);
215
auto proto_slots = builder.put(m_c.priv().m_ins, m_c.priv().m_outs);
216
GAPI_LOG_INFO(NULL, "Generated graph: " << g.nodes().size() << " nodes" << std::endl);
217
218
// Store Computation's protocol in metadata
219
Protocol p;
220
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
221
gm.metadata().set(p);
222
223
return pG;
224
}
225
226
void cv::gimpl::GCompiler::runPasses(ade::Graph &g)
227
{
228
m_e.runPasses(g);
229
GAPI_LOG_INFO(NULL, "All compiler passes are successful");
230
}
231
232
void cv::gimpl::GCompiler::compileIslands(ade::Graph &g)
233
{
234
GModel::Graph gm(g);
235
std::shared_ptr<ade::Graph> gptr(gm.metadata().get<IslandModel>().model);
236
GIslandModel::Graph gim(*gptr);
237
238
// Run topological sort on GIslandModel first
239
auto pass_ctx = ade::passes::PassContext{*gptr};
240
ade::passes::TopologicalSort{}(pass_ctx);
241
242
// Now compile islands
243
GIslandModel::compileIslands(gim, g, m_args);
244
}
245
246
cv::GCompiled cv::gimpl::GCompiler::produceCompiled(GPtr &&pg)
247
{
248
// This is the final compilation step. Here:
249
// - An instance of GExecutor is created. Depening on the platform,
250
// build configuration, etc, a GExecutor may be:
251
// - a naive single-thread graph interpreter;
252
// - a std::thread-based thing
253
// - a TBB-based thing, etc.
254
// - All this stuff is wrapped into a GCompiled object and returned
255
// to user.
256
257
// Note: this happens in the last pass ("compile_islands"):
258
// - Each GIsland of GIslandModel instantiates its own,
259
// backend-specific executable object
260
// - Every backend gets a subgraph to execute, and builds
261
// an execution plan for it (backend-specific execution)
262
// ...before call to produceCompiled();
263
264
const auto &outMetas = GModel::ConstGraph(*pg).metadata()
265
.get<OutputMeta>().outMeta;
266
std::unique_ptr<GExecutor> pE(new GExecutor(std::move(pg)));
267
// FIXME: select which executor will be actually used,
268
// make GExecutor abstract.
269
270
GCompiled compiled;
271
compiled.priv().setup(m_metas, outMetas, std::move(pE));
272
return compiled;
273
}
274
275
cv::GCompiled cv::gimpl::GCompiler::compile()
276
{
277
std::unique_ptr<ade::Graph> pG = generateGraph();
278
runPasses(*pG);
279
compileIslands(*pG);
280
return produceCompiled(std::move(pG));
281
}
282
283