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
11 #include <unordered_set>
13 #include <ade/util/algorithm.hpp>
15 #include <ade/util/range.hpp>
16 #include <ade/util/zip_range.hpp>
17 #include <ade/util/chain_range.hpp>
19 #include <ade/typed_graph.hpp>
21 #include "opencv2/gapi/gcommon.hpp"
22 #include "opencv2/gapi/util/any.hpp"
23 #include "opencv2/gapi/gtype_traits.hpp"
25 #include "compiler/gobjref.hpp"
26 #include "compiler/gmodel.hpp"
28 #include "backends/gpu/ggpubackend.hpp"
29 #include "backends/gpu/ggpuimgproc.hpp"
30 #include "backends/gpu/ggpucore.hpp"
32 #include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK!
34 // FIXME: Is there a way to take a typed graph (our GModel),
35 // and create a new typed graph _ATOP_ of that (by extending with a couple of
37 // Alternatively, is there a way to compose types graphs?
39 // If not, we need to introduce that!
40 using GGPUModel = ade::TypedGraph
45 // FIXME: Same issue with Typed and ConstTyped
46 using GConstGGPUModel = ade::ConstTypedGraph
53 class GGPUBackendImpl final: public cv::gapi::GBackend::Priv
55 virtual void unpackKernel(ade::Graph &graph,
56 const ade::NodeHandle &op_node,
57 const cv::GKernelImpl &impl) override
60 auto gpu_impl = cv::util::any_cast<cv::GGPUKernel>(impl.opaque);
61 gm.metadata(op_node).set(cv::gimpl::Unit{gpu_impl});
64 virtual EPtr compile(const ade::Graph &graph,
65 const cv::GCompileArgs &,
66 const std::vector<ade::NodeHandle> &nodes) const override
68 return EPtr{new cv::gimpl::GGPUExecutable(graph, nodes)};
73 cv::gapi::GBackend cv::gapi::gpu::backend()
75 static cv::gapi::GBackend this_backend(std::make_shared<GGPUBackendImpl>());
79 // GGPUExcecutable implementation //////////////////////////////////////////////
80 cv::gimpl::GGPUExecutable::GGPUExecutable(const ade::Graph &g,
81 const std::vector<ade::NodeHandle> &nodes)
84 // Convert list of operations (which is topologically sorted already)
85 // into an execution script.
86 for (auto &nh : nodes)
88 switch (m_gm.metadata(nh).get<NodeType>().t)
90 case NodeType::OP: m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)}); break;
93 m_dataNodes.push_back(nh);
94 const auto &desc = m_gm.metadata(nh).get<Data>();
95 if (desc.storage == Data::Storage::CONST)
97 auto rc = RcDesc{desc.rc, desc.shape, desc.ctor};
98 magazine::bindInArg(m_res, rc, m_gm.metadata(nh).get<ConstValue>().arg);
100 //preallocate internal Mats in advance
101 if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT)
103 const auto mat_desc = util::get<cv::GMatDesc>(desc.meta);
104 const auto type = CV_MAKETYPE(mat_desc.depth, mat_desc.chan);
105 m_res.slot<cv::UMat>()[desc.rc].create(mat_desc.size.width, mat_desc.size.height, type);
109 default: util::throw_error(std::logic_error("Unsupported NodeType type"));
114 // FIXME: Document what it does
115 cv::GArg cv::gimpl::GGPUExecutable::packArg(const GArg &arg)
117 // No API placeholders allowed at this point
118 // FIXME: this check has to be done somewhere in compilation stage.
119 GAPI_Assert( arg.kind != cv::detail::ArgKind::GMAT
120 && arg.kind != cv::detail::ArgKind::GSCALAR
121 && arg.kind != cv::detail::ArgKind::GARRAY);
123 if (arg.kind != cv::detail::ArgKind::GOBJREF)
125 // All other cases - pass as-is, with no transformations to GArg contents.
128 GAPI_Assert(arg.kind == cv::detail::ArgKind::GOBJREF);
130 // Wrap associated CPU object (either host or an internal one)
131 // FIXME: object can be moved out!!! GExecutor faced that.
132 const cv::gimpl::RcDesc &ref = arg.get<cv::gimpl::RcDesc>();
135 case GShape::GMAT: return GArg(m_res.slot<cv::UMat>()[ref.id]);
136 case GShape::GSCALAR: return GArg(m_res.slot<cv::gapi::own::Scalar>()[ref.id]);
137 // Note: .at() is intentional for GArray as object MUST be already there
138 // (and constructed by either bindIn/Out or resetInternal)
139 case GShape::GARRAY: return GArg(m_res.slot<cv::detail::VectorRef>().at(ref.id));
141 util::throw_error(std::logic_error("Unsupported GShape type"));
146 void cv::gimpl::GGPUExecutable::run(std::vector<InObj> &&input_objs,
147 std::vector<OutObj> &&output_objs)
149 // Update resources with run-time information - what this Island
150 // has received from user (or from another Island, or mix...)
151 // FIXME: Check input/output objects against GIsland protocol
153 for (auto& it : input_objs) magazine::bindInArg (m_res, it.first, it.second, true);
154 for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second, true);
156 // Initialize (reset) internal data nodes with user structures
157 // before processing a frame (no need to do it for external data structures)
158 GModel::ConstGraph gm(m_g);
159 for (auto nh : m_dataNodes)
161 const auto &desc = gm.metadata(nh).get<Data>();
163 if ( desc.storage == Data::Storage::INTERNAL
164 && !util::holds_alternative<util::monostate>(desc.ctor))
166 // FIXME: Note that compile-time constant data objects (like
167 // a value-initialized GArray<T>) also satisfy this condition
168 // and should be excluded, but now we just don't support it
169 magazine::resetInternalData(m_res, desc);
173 // OpenCV backend execution is not a rocket science at all.
174 // Simply invoke our kernels in the proper order.
175 GConstGGPUModel gcm(m_g);
176 for (auto &op_info : m_script)
178 const auto &op = m_gm.metadata(op_info.nh).get<Op>();
180 // Obtain our real execution unit
181 // TODO: Should kernels be copyable?
182 GGPUKernel k = gcm.metadata(op_info.nh).get<Unit>().k;
184 // Initialize kernel's execution context:
185 // - Input parameters
187 context.m_args.reserve(op.args.size());
189 using namespace std::placeholders;
190 ade::util::transform(op.args,
191 std::back_inserter(context.m_args),
192 std::bind(&GGPUExecutable::packArg, this, _1));
194 // - Output parameters.
195 // FIXME: pre-allocate internal Mats, etc, according to the known meta
196 for (const auto &out_it : ade::util::indexed(op.outs))
198 // FIXME: Can the same GArg type resolution mechanism be reused here?
199 const auto out_port = ade::util::index(out_it);
200 const auto out_desc = ade::util::value(out_it);
201 context.m_results[out_port] = magazine::getObjPtr(m_res, out_desc, true);
204 // Now trigger the executable unit
207 for (const auto &out_it : ade::util::indexed(op_info.expected_out_metas))
209 const auto out_index = ade::util::index(out_it);
210 const auto expected_meta = ade::util::value(out_it);
211 const auto out_meta = descr_of(context.m_results[out_index]);
213 if (expected_meta != out_meta)
217 ("Output meta doesn't "
218 "coincide with the generated meta\n"
219 "Expected: " + ade::util::to_string(expected_meta) + "\n"
220 "Actual : " + ade::util::to_string(out_meta)));
225 for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second, true);