Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / thirdparty / fluid / modules / gapi / include / opencv2 / gapi / gkernel.hpp
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 #ifndef OPENCV_GAPI_GKERNEL_HPP
9 #define OPENCV_GAPI_GKERNEL_HPP
10
11 #include <functional>
12 #include <iostream>
13 #include <string>  // string
14 #include <type_traits> // false_type, true_type
15 #include <unordered_map> // map (for GKernelPackage)
16 #include <utility> // tuple
17 #include <vector>  // lookup order
18
19 #include <opencv2/gapi/gcommon.hpp> // CompileArgTag
20 #include <opencv2/gapi/util/util.hpp> // Seq
21 #include <opencv2/gapi/gcall.hpp>
22 #include <opencv2/gapi/garg.hpp>      // GArg
23 #include <opencv2/gapi/gmetaarg.hpp>  // GMetaArg
24 #include <opencv2/gapi/gtype_traits.hpp> // GTypeTraits
25 #include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
26
27
28 namespace cv {
29
30 using GShapes = std::vector<GShape>;
31
32 // GKernel describes kernel API to the system
33 // FIXME: add attributes of a kernel, (e.g. number and types
34 // of inputs, etc)
35 struct GAPI_EXPORTS GKernel
36 {
37     using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>;
38
39     const std::string name;       // kernel ID, defined by its API (signature)
40     const M           outMeta;    // generic adaptor to API::outMeta(...)
41     const GShapes     outShapes; // types (shapes) kernel's outputs
42 };
43
44 // GKernelImpl describes particular kernel implementation to the system
45 struct GAPI_EXPORTS GKernelImpl
46 {
47     util::any         opaque;    // backend-specific opaque info
48 };
49
50 template<typename, typename> class GKernelTypeM;
51
52 namespace detail
53 {
54     ////////////////////////////////////////////////////////////////////////////
55     // yield() is used in graph construction time as a generic method to obtain
56     // lazy "return value" of G-API operations
57     //
58     namespace
59     {
60
61         template<typename T> struct Yield;
62         template<> struct Yield<cv::GMat>
63         {
64             static inline cv::GMat yield(cv::GCall &call, int i) { return call.yield(i); }
65         };
66         template<> struct Yield<cv::GScalar>
67         {
68             static inline cv::GScalar yield(cv::GCall &call, int i) { return call.yieldScalar(i); }
69         };
70         template<typename U> struct Yield<cv::GArray<U> >
71         {
72             static inline cv::GArray<U> yield(cv::GCall &call, int i) { return call.yieldArray<U>(i); }
73         };
74     } // anonymous namespace
75
76     ////////////////////////////////////////////////////////////////////////////
77     // Helper classes which brings outputMeta() marshalling to kernel
78     // implementations
79     //
80     // 1. MetaType establishes G#Type -> G#Meta mapping between G-API dynamic
81     //    types and its metadata descriptor types.
82     //    This mapping is used to transform types to call outMeta() callback.
83     template<typename T> struct MetaType;
84     template<> struct MetaType<cv::GMat>    { using type = GMatDesc; };
85     template<> struct MetaType<cv::GScalar> { using type = GScalarDesc; };
86     template<typename U> struct MetaType<cv::GArray<U> > { using type = GArrayDesc; };
87     template<typename T> struct MetaType    { using type = T; }; // opaque args passed as-is
88
89     // 2. Hacky test based on MetaType to check if we operate on G-* type or not
90     template<typename T> using is_nongapi_type = std::is_same<T, typename MetaType<T>::type>;
91
92     // 3. Two ways to transform input arguments to its meta - for G-* and non-G* types:
93     template<typename T>
94     typename std::enable_if<!is_nongapi_type<T>::value, typename MetaType<T>::type>
95     ::type get_in_meta(const GMetaArgs &in_meta, const GArgs &, int idx)
96     {
97         return util::get<typename MetaType<T>::type>(in_meta.at(idx));
98     }
99
100     template<typename T>
101     typename std::enable_if<is_nongapi_type<T>::value, T>
102     ::type get_in_meta(const GMetaArgs &, const GArgs &in_args, int idx)
103     {
104         return in_args.at(idx).template get<T>();
105     }
106
107     // 4. The MetaHelper itself: an entity which generates outMeta() call
108     //    based on kernel signature, with arguments properly substituted.
109     // 4.1 - case for multiple return values
110     // FIXME: probably can be simplified with std::apply or analogue.
111     template<typename, typename, typename>
112     struct MetaHelper;
113
114     template<typename K, typename... Ins, typename... Outs>
115     struct MetaHelper<K, std::tuple<Ins...>, std::tuple<Outs...> >
116     {
117         template<int... IIs, int... OIs>
118         static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
119                                          const GArgs &in_args,
120                                          detail::Seq<IIs...>,
121                                          detail::Seq<OIs...>)
122         {
123             // FIXME: decay?
124             using R   = std::tuple<typename MetaType<Outs>::type...>;
125             const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
126             return GMetaArgs{ GMetaArg(std::get<OIs>(r))... };
127         }
128         // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
129
130         static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
131                                     const GArgs &in_args)
132         {
133             return getOutMeta_impl(in_meta,
134                                    in_args,
135                                    typename detail::MkSeq<sizeof...(Ins)>::type(),
136                                    typename detail::MkSeq<sizeof...(Outs)>::type());
137         }
138     };
139
140     // 4.1 - case for a single return value
141     // FIXME: How to avoid duplication here?
142     template<typename K, typename... Ins, typename Out>
143     struct MetaHelper<K, std::tuple<Ins...>, Out >
144     {
145         template<int... IIs>
146         static GMetaArgs getOutMeta_impl(const GMetaArgs &in_meta,
147                                          const GArgs &in_args,
148                                          detail::Seq<IIs...>)
149         {
150             // FIXME: decay?
151             using R = typename MetaType<Out>::type;
152             const R r = K::outMeta( get_in_meta<Ins>(in_meta, in_args, IIs)... );
153             return GMetaArgs{ GMetaArg(r) };
154         }
155         // FIXME: help users identify how outMeta must look like (via default impl w/static_assert?)
156
157         static GMetaArgs getOutMeta(const GMetaArgs &in_meta,
158                                     const GArgs &in_args)
159         {
160             return getOutMeta_impl(in_meta,
161                                    in_args,
162                                    typename detail::MkSeq<sizeof...(Ins)>::type());
163         }
164     };
165
166 } // namespace detail
167
168 // GKernelType and GKernelTypeM are base classes which implement typed ::on()
169 // method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels
170 //
171 // G_TYPED_KERNEL and G_TYPED_KERNEK_M macros inherit user classes from GKernelType and
172 // GKernelTypeM respectively.
173
174 template<typename K, typename... R, typename... Args>
175 class GKernelTypeM<K, std::function<std::tuple<R...>(Args...)> >:
176         public detail::MetaHelper<K, std::tuple<Args...>, std::tuple<R...> >
177 {
178     template<int... IIs>
179     static std::tuple<R...> yield(cv::GCall &call, detail::Seq<IIs...>)
180     {
181         return std::make_tuple(detail::Yield<R>::yield(call, IIs)...);
182     }
183
184 public:
185     using InArgs  = std::tuple<Args...>;
186     using OutArgs = std::tuple<R...>;
187
188     static std::tuple<R...> on(Args... args)
189     {
190         cv::GCall call(GKernel{K::id(), &K::getOutMeta, {detail::GTypeTraits<R>::shape...}});
191         call.pass(args...);
192         return yield(call, typename detail::MkSeq<sizeof...(R)>::type());
193     }
194 };
195
196 template<typename, typename> class GKernelType;
197
198 template<typename K, typename R, typename... Args>
199 class GKernelType<K, std::function<R(Args...)> >:
200         public detail::MetaHelper<K, std::tuple<Args...>, R >
201 {
202 public:
203     using InArgs  = std::tuple<Args...>;
204     using OutArgs = std::tuple<R>;
205
206     static R on(Args... args)
207     {
208         cv::GCall call(GKernel{K::id(), &K::getOutMeta, {detail::GTypeTraits<R>::shape}});
209         call.pass(args...);
210         return detail::Yield<R>::yield(call, 0);
211     }
212 };
213
214 } // namespace cv
215
216
217 // FIXME: I don't know a better way so far. Feel free to suggest one
218 // The problem is that every typed kernel should have ::id() but body
219 // of the class is defined by user (with outMeta, other stuff)
220
221 #define G_ID_HELPER_CLASS(Class)  Class##IdHelper
222
223 #define G_ID_HELPER_BODY(Class, Id)                                         \
224     namespace detail                                                        \
225     {                                                                       \
226         struct G_ID_HELPER_CLASS(Class)                                     \
227         {                                                                   \
228             static constexpr const char * id() {return Id;};                \
229         };                                                                  \
230     }
231
232 #define G_TYPED_KERNEL(Class, API, Id)                                      \
233     G_ID_HELPER_BODY(Class, Id)                                             \
234     struct Class final: public cv::GKernelType<Class, std::function API >,  \
235                         public detail::G_ID_HELPER_CLASS(Class)
236 // {body} is to be defined by user
237
238 #define G_TYPED_KERNEL_M(Class, API, Id)                                    \
239     G_ID_HELPER_BODY(Class, Id)                                             \
240     struct Class final: public cv::GKernelTypeM<Class, std::function API >, \
241                         public detail::G_ID_HELPER_CLASS(Class)             \
242 // {body} is to be defined by user
243
244 namespace cv
245 {
246 // Declare <unite> in cv:: namespace
247 enum class unite_policy
248 {
249     REPLACE,
250     KEEP
251 };
252
253 namespace gapi
254 {
255     // Prework: model "Device" API before it gets to G-API headers.
256     // FIXME: Don't mix with internal Backends class!
257     class GAPI_EXPORTS GBackend
258     {
259     public:
260         class Priv;
261
262         // TODO: make it template (call `new` within??)
263         GBackend();
264         explicit GBackend(std::shared_ptr<Priv> &&p);
265
266         Priv& priv();
267         const Priv& priv() const;
268         std::size_t hash() const;
269
270         bool operator== (const GBackend &rhs) const;
271
272     private:
273         std::shared_ptr<Priv> m_priv;
274     };
275
276     inline bool operator != (const GBackend &lhs, const GBackend &rhs)
277     {
278         return !(lhs == rhs);
279     }
280 } // namespace gapi
281 } // namespace cv
282
283 namespace std
284 {
285     template<> struct hash<cv::gapi::GBackend>
286     {
287         std::size_t operator() (const cv::gapi::GBackend &b) const
288         {
289             return b.hash();
290         }
291     };
292 } // namespace std
293
294
295 namespace cv {
296 namespace gapi {
297     /** \addtogroup gapi_compile_args
298      * @{
299      */
300
301     // Lookup order is in fact a vector of Backends to traverse during look-up
302     /**
303      * @brief Priority list of backends to use during kernel
304      *   resolution process.
305      *
306      * Priority is descending -- the first backend in the list has the
307      * top priority, and the last one has the lowest priority.
308      *
309      * If there's multiple implementations available for a kernel at
310      * the moment of graph compilation, a kernel (and thus a backend)
311      * will be selected according to this order (if the parameter is passed).
312      *
313      * Default order is not specified (and by default, only
314      * CPU(OpenCV) backend is involved in graph compilation).
315      */
316     using GLookupOrder = std::vector<GBackend>;
317     /**
318      * @brief Create a backend lookup order -- priority list of
319      * backends to use during graph compilation process.
320      *
321      * @sa GLookupOrder, @ref gapi_std_backends
322      */
323     inline GLookupOrder lookup_order(std::initializer_list<GBackend> &&list)
324     {
325         return GLookupOrder(std::move(list));
326     }
327
328     // FIXME: Hide implementation
329     /**
330      * @brief A container class for heterogeneous kernel
331      * implementation collections.
332      *
333      * GKernelPackage is a special container class which stores kernel
334      * _implementations_. Objects of this class are created and passed
335      * to cv::GComputation::compile() to specify which kernels to use
336      * in the compiled graph. GKernelPackage may contain kernels of
337      * different backends, e.g. be heterogeneous.
338      *
339      * The most easy way to create a kernel package is to use function
340      * cv::gapi::kernels(). This template functions takes kernel
341      * implementations in form of type list (variadic template) and
342      * generates a kernel package atop of that.
343      *
344      * Kernel packages can be also generated programatically, starting
345      * with an empty package (created with the default constructor)
346      * and then by populating it with kernels via call to
347      * GKernelPackage::include(). Note this method is also a template
348      * one since G-API kernel implementations are _types_, not objects.
349      *
350      * Finally, two kernel packages can be combined into a new one
351      * with function cv::gapi::combine(). There are different rules
352      * apply to this process, see also cv::gapi::unite_policy for
353      * details.
354      */
355     class GAPI_EXPORTS GKernelPackage
356     {
357         /// @private
358         using S = std::unordered_map<std::string, GKernelImpl>;
359
360         /// @private
361         using M = std::unordered_map<GBackend, S>;
362
363         /// @private
364         M m_backend_kernels;
365
366     protected:
367         /// @private
368         // Check if package contains ANY implementation of a kernel API
369         // by API textual id.
370         bool includesAPI(const std::string &id) const;
371
372         /// @private
373         // Remove ALL implementations of the given API (identified by ID)
374         void removeAPI(const std::string &id);
375
376     public:
377         /**
378          * @brief Returns total number of kernels in the package
379          * (accross all backends included)
380          *
381          * @return a number of kernels in the package
382          */
383         std::size_t size() const;
384
385         /**
386          * @brief Test if a particular kernel _implementation_ KImpl is
387          * included in this kernel package.
388          *
389          * @sa includesAPI()
390          *
391          * @return true if there is such kernel, false otherwise.
392          */
393         template<typename KImpl>
394         bool includes() const
395         {
396             const auto set_iter = m_backend_kernels.find(KImpl::backend());
397             return (set_iter != m_backend_kernels.end())
398                 ? (set_iter->second.count(KImpl::API::id()) > 0)
399                 : false;
400         }
401
402         /**
403          * @brief Remove all kernels associated with the given backend
404          * from the package.
405          *
406          * Does nothing if there's no kernels of this backend in the package.
407          *
408          * @param backend backend which kernels to remove
409          */
410         void remove(const GBackend& backend);
411
412         /**
413          * @brief Remove all kernels implementing the given API from
414          * the package.
415          *
416          * Does nothing if there's no kernels implementing the given interface.
417          */
418         template<typename KAPI>
419         void remove()
420         {
421             removeAPI(KAPI::id());
422         }
423
424         // FIXME: Rename to includes() and distinguish API/impl case by
425         //     statically?
426         /**
427          * Check if package contains ANY implementation of a kernel API
428          * by API type.
429          */
430         template<typename KAPI>
431         bool includesAPI() const
432         {
433             return includesAPI(KAPI::id());
434         }
435
436         /**
437          * @brief Find a kernel (by its API), given the look-up order.
438          *
439          * If order is empty, returns first suitable implementation.
440          * Throws if nothing found.
441          *
442          * @return Backend which hosts matching kernel implementation.
443          *
444          * @sa cv::gapi::lookup_order
445          */
446         template<typename KAPI>
447         GBackend lookup(const GLookupOrder &order = {}) const
448         {
449             return lookup(KAPI::id(), order).first;
450         }
451
452         /// @private
453         std::pair<cv::gapi::GBackend, cv::GKernelImpl>
454         lookup(const std::string &id, const GLookupOrder &order = {}) const;
455
456         // FIXME: No overwrites allowed?
457         /**
458          * @brief Put a new kernel implementation KImpl into package.
459          *
460          * @param up unite policy to use. If the package has already
461          * implementation for this kernel (probably from another
462          * backend), and cv::unite_policy::KEEP is passed, the
463          * existing implementation remains in package; on
464          * cv::unite_policy::REPLACE all other existing
465          * implementations are first dropped from the package.
466          */
467         template<typename KImpl>
468         void include(const cv::unite_policy up = cv::unite_policy::KEEP)
469         {
470             auto backend     = KImpl::backend();
471             auto kernel_id   = KImpl::API::id();
472             auto kernel_impl = GKernelImpl{KImpl::kernel()};
473             if (up == cv::unite_policy::REPLACE) removeAPI(kernel_id);
474             else GAPI_Assert(up == cv::unite_policy::KEEP);
475
476             // Regardless of the policy, store new impl in its storage slot.
477             m_backend_kernels[backend][kernel_id] = std::move(kernel_impl);
478         }
479
480         /**
481          * @brief Lists all backends which are included into package
482          *
483          * @return vector of backends
484          */
485         std::vector<GBackend> backends() const;
486
487         // TODO: Doxygen bug -- it wants me to place this comment
488         // here, not below.
489         /**
490          * @brief Create a new package based on `lhs` and `rhs`,
491          * with unity policy defined by `policy`.
492          *
493          * @param lhs "Left-hand-side" package in the process
494          * @param rhs "Right-hand-side" package in the process
495          * @param policy Unite policy which is used in case of conflicts
496          * -- when the same kernel API is implemented in both packages by
497          * different backends; cv::unite_policy::KEEP keeps both
498          * implementation in the resulting package, while
499          * cv::unite_policy::REPLACE gives precedence two kernels from
500          * "Right-hand-side".
501          *
502          * @return a new kernel package.
503          */
504         friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage  &lhs,
505                                                    const GKernelPackage  &rhs,
506                                                    const cv::unite_policy policy);
507     };
508
509     /**
510      * @brief Create a kernel package object containing kernels
511      * specified in variadic template argument.
512      *
513      * In G-API, kernel implementations are _types_. Every backend has
514      * its own kernel API (like GAPI_OCV_KERNEL() and
515      * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for
516      * each kernel implementation.
517      *
518      * Use this function to pass kernel implementations (defined in
519      * either way) to the system. Example:
520      *
521      * @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet
522      *
523      * Note that kernels() itself is a function returning object, not
524      * a type, so having `()` at the end is important -- it must be a
525      * function call.
526      */
527     template<typename... KK> GKernelPackage kernels()
528     {
529         GKernelPackage pkg;
530
531         // For those who wonder - below is a trick to call a number of
532         // methods based on parameter pack (zeroes just help hiding these
533         // calls into a sequence which helps to expand this parameter pack).
534         // Just note that `f(),a` always equals to `a` (with f() called!)
535         // and parentheses are used to hide function call in the expanded sequence.
536         // Leading 0 helps to handle case when KK is an empty list (kernels<>()).
537
538         int unused[] = { 0, (pkg.include<KK>(), 0)... };
539         cv::util::suppress_unused_warning(unused);
540         return pkg;
541     };
542
543     /** @} */
544
545     GAPI_EXPORTS GKernelPackage combine(const GKernelPackage  &lhs,
546                                         const GKernelPackage  &rhs,
547                                         const cv::unite_policy policy);
548 } // namespace gapi
549
550 namespace detail
551 {
552     template<> struct CompileArgTag<cv::gapi::GKernelPackage>
553     {
554         static const char* tag() { return "gapi.kernel_package"; }
555     };
556     template<> struct CompileArgTag<cv::gapi::GLookupOrder>
557     {
558         static const char* tag() { return "gapi.lookup_order"; }
559     };
560 } // namespace detail
561 } // namespace cv
562
563 #endif // OPENCV_GAPI_GKERNEL_HPP