mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 19:20:28 +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 <iostream>
|
||||
|
||||
#include <ade/util/zip_range.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
|
||||
// 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)
|
||||
@ -421,7 +420,17 @@ void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs&
|
||||
ade::passes::PassContext ctx{g};
|
||||
passes::initMeta(ctx, inMetas);
|
||||
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()
|
||||
|
@ -6,10 +6,158 @@
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "../gapi_mock_kernels.hpp"
|
||||
|
||||
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
|
||||
// The below graph and config is taken from ComplexIslands test suite
|
||||
TEST(GExecutor, SmokeTest)
|
||||
@ -77,6 +225,75 @@ TEST(GExecutor, SmokeTest)
|
||||
// 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
|
||||
// between executed islands
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user