mirror of
https://github.com/opencv/opencv.git
synced 2025-06-09 18:43:05 +08:00
Merge pull request #20705 from TolyaTalamanov:at/handle-reshape-in-gexecutor
G-API: Handle reshape for generic case in GExecutor * Handle reshape for generic case for GExecutor * Add initResources * Add tests * Refactor reshape method
This commit is contained in:
parent
54386c82fd
commit
499d8adb75
@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <ade/util/zip_range.hpp>
|
#include <ade/util/zip_range.hpp>
|
||||||
|
|
||||||
#include <opencv2/gapi/opencv_includes.hpp>
|
#include <opencv2/gapi/opencv_includes.hpp>
|
||||||
@ -411,7 +409,8 @@ bool cv::gimpl::GExecutor::canReshape() const
|
|||||||
{
|
{
|
||||||
// FIXME: Introduce proper reshaping support on GExecutor level
|
// FIXME: Introduce proper reshaping support on GExecutor level
|
||||||
// for all cases!
|
// for all cases!
|
||||||
return (m_ops.size() == 1) && m_ops[0].isl_exec->canReshape();
|
return std::all_of(m_ops.begin(), m_ops.end(),
|
||||||
|
[](const OpDesc& op) { return op.isl_exec->canReshape(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
|
void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
|
||||||
@ -421,7 +420,17 @@ void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs&
|
|||||||
ade::passes::PassContext ctx{g};
|
ade::passes::PassContext ctx{g};
|
||||||
passes::initMeta(ctx, inMetas);
|
passes::initMeta(ctx, inMetas);
|
||||||
passes::inferMeta(ctx, true);
|
passes::inferMeta(ctx, true);
|
||||||
m_ops[0].isl_exec->reshape(g, args);
|
|
||||||
|
// NB: Before reshape islands need to re-init resources for every slot.
|
||||||
|
for (auto slot : m_slots)
|
||||||
|
{
|
||||||
|
initResource(slot.slot_nh, slot.data_nh);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& op : m_ops)
|
||||||
|
{
|
||||||
|
op.isl_exec->reshape(g, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cv::gimpl::GExecutor::prepareForNewStream()
|
void cv::gimpl::GExecutor::prepareForNewStream()
|
||||||
|
@ -6,10 +6,158 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "../test_precomp.hpp"
|
#include "../test_precomp.hpp"
|
||||||
|
#include "../gapi_mock_kernels.hpp"
|
||||||
|
|
||||||
namespace opencv_test
|
namespace opencv_test
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class GMockExecutable final: public cv::gimpl::GIslandExecutable
|
||||||
|
{
|
||||||
|
virtual inline bool canReshape() const override {
|
||||||
|
return m_priv->m_can_reshape;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void reshape(ade::Graph&, const GCompileArgs&) override
|
||||||
|
{
|
||||||
|
m_priv->m_reshape_counter++;
|
||||||
|
}
|
||||||
|
virtual void handleNewStream() override { }
|
||||||
|
virtual void run(std::vector<InObj>&&, std::vector<OutObj>&&) { }
|
||||||
|
virtual bool allocatesOutputs() const override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual cv::RMat allocate(const cv::GMatDesc&) const override
|
||||||
|
{
|
||||||
|
m_priv->m_allocate_counter++;
|
||||||
|
return cv::RMat();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: GMockBackendImpl creates new unique_ptr<GMockExecutable>
|
||||||
|
// on every compile call. Need to share counters between instances in order
|
||||||
|
// to validate it in tests.
|
||||||
|
struct Priv
|
||||||
|
{
|
||||||
|
bool m_can_reshape;
|
||||||
|
int m_reshape_counter;
|
||||||
|
int m_allocate_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<Priv> m_priv;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GMockExecutable(bool can_reshape = true)
|
||||||
|
: m_priv(new Priv{can_reshape, 0, 0})
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void setReshape(bool can_reshape) { m_priv->m_can_reshape = can_reshape; }
|
||||||
|
|
||||||
|
int getReshapeCounter() const { return m_priv->m_reshape_counter; }
|
||||||
|
int getAllocateCounter() const { return m_priv->m_allocate_counter; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GMockBackendImpl final: public cv::gapi::GBackend::Priv
|
||||||
|
{
|
||||||
|
virtual void unpackKernel(ade::Graph &,
|
||||||
|
const ade::NodeHandle &,
|
||||||
|
const cv::GKernelImpl &) override { }
|
||||||
|
|
||||||
|
virtual EPtr compile(const ade::Graph &,
|
||||||
|
const cv::GCompileArgs &,
|
||||||
|
const std::vector<ade::NodeHandle> &) const override
|
||||||
|
{
|
||||||
|
++m_compile_counter;
|
||||||
|
return EPtr{new GMockExecutable(m_exec)};
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable int m_compile_counter = 0;
|
||||||
|
GMockExecutable m_exec;
|
||||||
|
|
||||||
|
virtual bool controlsMerge() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &,
|
||||||
|
const ade::NodeHandle &,
|
||||||
|
const ade::NodeHandle &,
|
||||||
|
const ade::NodeHandle &) const override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GMockBackendImpl(const GMockExecutable& exec) : m_exec(exec) { };
|
||||||
|
int getCompileCounter() const { return m_compile_counter; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GMockFunctor : public gapi::cpu::GOCVFunctor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GMockFunctor(cv::gapi::GBackend backend,
|
||||||
|
const char* id,
|
||||||
|
const Meta &meta,
|
||||||
|
const Impl& impl)
|
||||||
|
: gapi::cpu::GOCVFunctor(id, meta, impl), m_backend(backend)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::GBackend backend() const override { return m_backend; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
cv::gapi::GBackend m_backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename K, typename Callable>
|
||||||
|
GMockFunctor mock_kernel(const cv::gapi::GBackend& backend, Callable c)
|
||||||
|
{
|
||||||
|
using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
|
||||||
|
return GMockFunctor{ backend
|
||||||
|
, K::id()
|
||||||
|
, &K::getOutMeta
|
||||||
|
, std::bind(&P::callFunctor, std::placeholders::_1, c)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void dummyFooImpl(const cv::Mat&, cv::Mat&) { };
|
||||||
|
void dummyBarImpl(const cv::Mat&, const cv::Mat&, cv::Mat&) { };
|
||||||
|
|
||||||
|
struct GExecutorReshapeTest: public ::testing::Test
|
||||||
|
{
|
||||||
|
GExecutorReshapeTest()
|
||||||
|
: comp([](){
|
||||||
|
cv::GMat in;
|
||||||
|
cv::GMat out = I::Bar::on(I::Foo::on(in), in);
|
||||||
|
return cv::GComputation(in, out);
|
||||||
|
})
|
||||||
|
{
|
||||||
|
backend_impl1 = std::make_shared<GMockBackendImpl>(island1);
|
||||||
|
backend1 = cv::gapi::GBackend{backend_impl1};
|
||||||
|
backend_impl2 = std::make_shared<GMockBackendImpl>(island2);
|
||||||
|
backend2 = cv::gapi::GBackend{backend_impl2};
|
||||||
|
auto kernel1 = mock_kernel<I::Foo>(backend1, dummyFooImpl);
|
||||||
|
auto kernel2 = mock_kernel<I::Bar>(backend2, dummyBarImpl);
|
||||||
|
pkg = cv::gapi::kernels(kernel1, kernel2);
|
||||||
|
in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
|
||||||
|
in_mat2 = cv::Mat::eye(64, 64, CV_8UC1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::GComputation comp;
|
||||||
|
GMockExecutable island1;
|
||||||
|
std::shared_ptr<GMockBackendImpl> backend_impl1;
|
||||||
|
cv::gapi::GBackend backend1;
|
||||||
|
GMockExecutable island2;
|
||||||
|
std::shared_ptr<GMockBackendImpl> backend_impl2;
|
||||||
|
cv::gapi::GBackend backend2;
|
||||||
|
cv::gapi::GKernelPackage pkg;
|
||||||
|
cv::Mat in_mat1, in_mat2, out_mat;;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
// FIXME: avoid code duplication
|
// FIXME: avoid code duplication
|
||||||
// The below graph and config is taken from ComplexIslands test suite
|
// The below graph and config is taken from ComplexIslands test suite
|
||||||
TEST(GExecutor, SmokeTest)
|
TEST(GExecutor, SmokeTest)
|
||||||
@ -77,6 +225,75 @@ TEST(GExecutor, SmokeTest)
|
|||||||
// with breakdown worked)
|
// with breakdown worked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GExecutorReshapeTest, ReshapeInsteadOfRecompile)
|
||||||
|
{
|
||||||
|
// NB: Initial state
|
||||||
|
EXPECT_EQ(0, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(0, island2.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: First compilation.
|
||||||
|
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(1, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(1, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(0, island2.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: GMockBackendImpl implements "reshape" method,
|
||||||
|
// so it won't be recompiled if the meta is changed.
|
||||||
|
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(1, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(1, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(1, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(1, island2.getReshapeCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GExecutorReshapeTest, OneBackendNotReshapable)
|
||||||
|
{
|
||||||
|
// NB: Make first island not reshapable
|
||||||
|
island1.setReshape(false);
|
||||||
|
|
||||||
|
// NB: Initial state
|
||||||
|
EXPECT_EQ(0, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(0, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island2.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: First compilation.
|
||||||
|
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(1, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(1, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(0, island2.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: Since one of islands isn't reshapable
|
||||||
|
// the entire graph isn't reshapable as well.
|
||||||
|
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(2, backend_impl1->getCompileCounter());
|
||||||
|
EXPECT_EQ(2, backend_impl2->getCompileCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
EXPECT_EQ(0, island2.getReshapeCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GExecutorReshapeTest, ReshapeCallAllocate)
|
||||||
|
{
|
||||||
|
// NB: Initial state
|
||||||
|
EXPECT_EQ(0, island1.getAllocateCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: First compilation.
|
||||||
|
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(1, island1.getAllocateCounter());
|
||||||
|
EXPECT_EQ(0, island1.getReshapeCounter());
|
||||||
|
|
||||||
|
// NB: The entire graph is reshapable, so it won't be recompiled, but reshaped.
|
||||||
|
// Check that reshape call "allocate" to reallocate buffers.
|
||||||
|
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
|
||||||
|
EXPECT_EQ(2, island1.getAllocateCounter());
|
||||||
|
EXPECT_EQ(1, island1.getReshapeCounter());
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
|
// FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
|
||||||
// between executed islands
|
// between executed islands
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user