gkernel.hpp 20 KB

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