2017-06-26 18:35:51 +08:00
|
|
|
// This file is part of OpenCV project.
|
|
|
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
|
|
// of this distribution and at http://opencv.org/license.html.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
|
|
|
// Third party copyrights are property of their respective owners.
|
|
|
|
|
2017-06-27 14:04:50 +08:00
|
|
|
#include "precomp.hpp"
|
2017-06-26 18:35:51 +08:00
|
|
|
#include "op_halide.hpp"
|
|
|
|
|
|
|
|
#ifdef HAVE_HALIDE
|
|
|
|
#include <HalideRuntimeOpenCL.h>
|
|
|
|
#endif // HAVE_HALIDE
|
|
|
|
|
|
|
|
namespace cv
|
|
|
|
{
|
|
|
|
namespace dnn
|
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef HAVE_HALIDE
|
2017-09-06 15:34:07 +08:00
|
|
|
static MatShape getBufferShape(const MatShape& shape)
|
|
|
|
{
|
|
|
|
if (shape.size() == 2 || shape.size() == 4)
|
|
|
|
{
|
|
|
|
int w, h, c, n;
|
|
|
|
getCanonicalSize(shape, &w, &h, &c, &n);
|
|
|
|
return {w, h, c, n};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MatShape bufferShape(shape);
|
|
|
|
std::reverse(bufferShape.begin(), bufferShape.end());
|
|
|
|
return bufferShape;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static MatShape getBufferShape(const MatSize& size)
|
|
|
|
{
|
|
|
|
return getBufferShape(MatShape(size.p, size.p + size[-1]));
|
|
|
|
}
|
|
|
|
|
2017-06-26 18:35:51 +08:00
|
|
|
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat)
|
|
|
|
{
|
2017-09-06 15:34:07 +08:00
|
|
|
return wrapToHalideBuffer(mat, getBufferShape(mat.size));
|
2017-06-26 18:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat,
|
|
|
|
const std::vector<int>& sizes)
|
|
|
|
{
|
|
|
|
Halide::Buffer<float> buffer((float*)mat.data, sizes);
|
|
|
|
buffer.set_host_dirty(); // Indicate that data is on CPU.
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
Halide::Buffer<> halideBuffer(const Ptr<BackendWrapper>& ptr)
|
|
|
|
{
|
|
|
|
CV_Assert(!ptr.empty());
|
|
|
|
return ptr.dynamicCast<HalideBackendWrapper>()->buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Halide::Buffer<> > halideBuffers(const std::vector<Ptr<BackendWrapper> >& ptrs)
|
|
|
|
{
|
|
|
|
std::vector<Halide::Buffer<> > vec;
|
|
|
|
vec.reserve(ptrs.size());
|
|
|
|
for (const Ptr<BackendWrapper>& ptr : ptrs)
|
|
|
|
{
|
|
|
|
vec.push_back(halideBuffer(ptr));
|
|
|
|
}
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
void getCanonicalSize(const Halide::Buffer<>& buffer, int* width, int* height,
|
|
|
|
int* channels, int* batch)
|
|
|
|
{
|
|
|
|
CV_Assert(buffer.dimensions() == 4);
|
|
|
|
*width = buffer.extent(0);
|
|
|
|
*height = buffer.extent(1);
|
|
|
|
*channels = buffer.extent(2);
|
|
|
|
*batch = buffer.extent(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
HalideBackendNode::HalideBackendNode(const Halide::Func& func)
|
|
|
|
: BackendNode(DNN_BACKEND_HALIDE), funcs(1, func) {}
|
|
|
|
|
|
|
|
HalideBackendNode::HalideBackendNode(const std::vector<Halide::Func>& funcs)
|
|
|
|
: BackendNode(DNN_BACKEND_HALIDE), funcs(funcs) {}
|
|
|
|
|
|
|
|
HalideBackendNode::HalideBackendNode(const Ptr<HalideBackendNode>& base,
|
|
|
|
const Halide::Func& top)
|
|
|
|
: BackendNode(DNN_BACKEND_HALIDE), funcs(base->funcs)
|
|
|
|
{
|
|
|
|
funcs.back() = top;
|
|
|
|
}
|
|
|
|
|
|
|
|
HalideBackendWrapper::HalideBackendWrapper(int targetId, const cv::Mat& m)
|
|
|
|
: BackendWrapper(DNN_BACKEND_HALIDE, targetId)
|
|
|
|
{
|
2017-08-17 17:48:02 +08:00
|
|
|
managesDevMemory = true;
|
2017-06-26 18:35:51 +08:00
|
|
|
buffer = wrapToHalideBuffer(m);
|
|
|
|
if (targetId == DNN_TARGET_CPU)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (targetId == DNN_TARGET_OPENCL)
|
|
|
|
{
|
2017-08-03 01:38:30 +08:00
|
|
|
Halide::Target t = Halide::get_host_target();
|
|
|
|
t.set_feature(Halide::Target::OpenCL);
|
2017-11-01 19:01:54 +08:00
|
|
|
buffer.copy_to_device(t);
|
2017-06-26 18:35:51 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
CV_Error(Error::StsNotImplemented, "Unknown target identifier");
|
|
|
|
}
|
|
|
|
|
|
|
|
HalideBackendWrapper::HalideBackendWrapper(const Ptr<BackendWrapper>& base,
|
|
|
|
const MatShape& shape)
|
|
|
|
: BackendWrapper(DNN_BACKEND_HALIDE, base->targetId)
|
|
|
|
{
|
2017-08-17 17:48:02 +08:00
|
|
|
managesDevMemory = false;
|
2017-06-26 18:35:51 +08:00
|
|
|
Halide::Buffer<float> baseBuffer = halideBuffer(base);
|
|
|
|
buffer = Halide::Buffer<float>((float*)baseBuffer.raw_buffer()->host,
|
2017-09-06 15:34:07 +08:00
|
|
|
getBufferShape(shape));
|
2017-06-26 18:35:51 +08:00
|
|
|
if (baseBuffer.has_device_allocation())
|
|
|
|
{
|
|
|
|
buffer.raw_buffer()->device = baseBuffer.raw_buffer()->device;
|
|
|
|
buffer.raw_buffer()->device_interface = baseBuffer.raw_buffer()->device_interface;
|
|
|
|
buffer.set_device_dirty();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffer.set_host_dirty(); // Indicate that data is on CPU.
|
|
|
|
CV_Assert(targetId == DNN_TARGET_CPU);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-17 17:48:02 +08:00
|
|
|
HalideBackendWrapper::~HalideBackendWrapper()
|
|
|
|
{
|
|
|
|
if (buffer.has_device_allocation() && !managesDevMemory)
|
|
|
|
{
|
|
|
|
buffer.raw_buffer()->device = 0;
|
|
|
|
buffer.raw_buffer()->device_interface = 0;
|
|
|
|
buffer.set_device_dirty(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-26 18:35:51 +08:00
|
|
|
void HalideBackendWrapper::copyToHost()
|
|
|
|
{
|
|
|
|
if (buffer.device_dirty())
|
|
|
|
{
|
|
|
|
buffer.device_sync();
|
|
|
|
buffer.copy_to_host();
|
|
|
|
}
|
|
|
|
}
|
2017-09-06 15:34:07 +08:00
|
|
|
|
|
|
|
void HalideBackendWrapper::setHostDirty()
|
|
|
|
{
|
|
|
|
buffer.set_device_dirty(false);
|
|
|
|
buffer.set_host_dirty();
|
|
|
|
}
|
2017-06-26 18:35:51 +08:00
|
|
|
#endif // HAVE_HALIDE
|
|
|
|
|
2017-09-06 15:34:07 +08:00
|
|
|
void getCanonicalSize(const MatSize& size, int* w, int* h, int* c, int* n)
|
2017-06-26 18:35:51 +08:00
|
|
|
{
|
2017-09-06 15:34:07 +08:00
|
|
|
getCanonicalSize(MatShape(size.p, size.p + size[-1]), w, h, c, n);
|
2017-06-26 18:35:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void getCanonicalSize(const MatShape& shape, int* width, int* height,
|
|
|
|
int* channels, int* batch)
|
|
|
|
{
|
|
|
|
const int dims = shape.size();
|
|
|
|
CV_Assert(dims == 2 || dims == 4);
|
|
|
|
*batch = shape[0];
|
|
|
|
*channels = shape[1];
|
|
|
|
if (dims == 4)
|
|
|
|
{
|
|
|
|
*width = shape[3];
|
|
|
|
*height = shape[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*width = 1;
|
|
|
|
*height = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-06 15:34:07 +08:00
|
|
|
void compileHalide(const std::vector<Mat> &outputs, Ptr<BackendNode>& node, int targetId)
|
2017-06-26 18:35:51 +08:00
|
|
|
{
|
|
|
|
#ifdef HAVE_HALIDE
|
|
|
|
CV_Assert(!node.empty());
|
|
|
|
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs.back();
|
|
|
|
|
|
|
|
int outW, outH, outC, outN;
|
|
|
|
Halide::Var x("x"), y("y"), c("c"), n("n");
|
|
|
|
getCanonicalSize(outputs[0].size, &outW, &outH, &outC, &outN);
|
|
|
|
top.bound(x, 0, outW).bound(y, 0, outH)
|
|
|
|
.bound(c, 0, outC).bound(n, 0, outN);
|
|
|
|
|
|
|
|
Halide::Target target = Halide::get_host_target();
|
|
|
|
target.set_feature(Halide::Target::NoAsserts);
|
|
|
|
if (targetId == DNN_TARGET_OPENCL)
|
|
|
|
{
|
|
|
|
target.set_feature(Halide::Target::OpenCL);
|
|
|
|
}
|
|
|
|
CV_Assert(target.supported());
|
|
|
|
top.compile_jit(target);
|
|
|
|
#endif // HAVE_HALIDE
|
|
|
|
}
|
|
|
|
|
|
|
|
void forwardHalide(std::vector<Ptr<BackendWrapper> > &outputs,
|
|
|
|
const Ptr<BackendNode>& node)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_HALIDE
|
|
|
|
CV_Assert(!node.empty());
|
|
|
|
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs.back();
|
|
|
|
auto outputBuffers = halideBuffers(outputs);
|
|
|
|
top.realize(Halide::Realization(outputBuffers));
|
|
|
|
#endif // HAVE_HALIDE
|
|
|
|
}
|
|
|
|
|
|
|
|
bool haveHalide()
|
|
|
|
{
|
|
|
|
#ifdef HAVE_HALIDE
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif // HAVE_HALIDE
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dnn
|
|
|
|
} // namespace cv
|