opencv/modules/dnn/src/op_vkcom.cpp
WuZhiwen 6e3ea8b49d Merge pull request #12703 from wzw-intel:vkcom
* dnn: Add a Vulkan based backend

This commit adds a new backend "DNN_BACKEND_VKCOM" and a
new target "DNN_TARGET_VULKAN". VKCOM means vulkan based
computation library.

This backend uses Vulkan API and SPIR-V shaders to do
the inference computation for layers. The layer types
that implemented in DNN_BACKEND_VKCOM include:
Conv, Concat, ReLU, LRN, PriorBox, Softmax, MaxPooling,
AvePooling, Permute

This is just a beginning work for Vulkan in OpenCV DNN,
more layer types will be supported and performance
tuning is on the way.

Signed-off-by: Wu Zhiwen <zhiwen.wu@intel.com>

* dnn/vulkan: Add FindVulkan.cmake to detect Vulkan SDK

In order to build dnn with Vulkan support, need installing
Vulkan SDK and setting environment variable "VULKAN_SDK" and
add "-DWITH_VULKAN=ON" to cmake command.

You can download Vulkan SDK from:
https://vulkan.lunarg.com/sdk/home#linux

For how to install, see
https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html
https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html
https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html
respectively for linux, windows and mac.

To run the vulkan backend, also need installing mesa driver.
On Ubuntu, use this command 'sudo apt-get install mesa-vulkan-drivers'

To test, use command '$BUILD_DIR/bin/opencv_test_dnn --gtest_filter=*VkCom*'

Signed-off-by: Wu Zhiwen <zhiwen.wu@intel.com>

* dnn/Vulkan: dynamically load Vulkan runtime

No compile-time dependency on Vulkan library.
If Vulkan runtime is unavailable, fallback to CPU path.

Use environment "OPENCL_VULKAN_RUNTIME" to specify path to your
own vulkan runtime library.

Signed-off-by: Wu Zhiwen <zhiwen.wu@intel.com>

* dnn/Vulkan: Add a python script to compile GLSL shaders to SPIR-V shaders

The SPIR-V shaders are in format of text-based 32-bit hexadecimal
numbers, and inserted into .cpp files as unsigned int32 array.

* dnn/Vulkan: Put Vulkan headers into 3rdparty directory and some other fixes

Vulkan header files are copied from
https://github.com/KhronosGroup/Vulkan-Docs/tree/master/include/vulkan
to 3rdparty/include

Fix the Copyright declaration issue.

Refine OpenCVDetectVulkan.cmake

* dnn/Vulkan: Add vulkan backend tests into existing ones.

Also fixed some test failures.

- Don't use bool variable as uniform for shader
- Fix dispathed group number beyond max issue
- Bypass "group > 1" convolution. This should be support in future.

* dnn/Vulkan: Fix multiple initialization in one thread.
2018-10-29 17:51:26 +03:00

163 lines
4.3 KiB
C++

// 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) 2018, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "precomp.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include "op_vkcom.hpp"
namespace cv
{
namespace dnn
{
#ifdef HAVE_VULKAN
void copyToTensor(vkcom::Tensor &dst, const Mat &src)
{
CV_Assert(src.isContinuous() && src.type() == CV_32F);
std::vector<int> mat_shape = shape(src);
dst.reshape((const char*)src.data, mat_shape);
}
void copyToMat(Mat &dst, vkcom::Tensor &src)
{
CV_Assert(dst.type() == CV_32F);
std::vector<int> shape = src.getShape();
void *data = src.map();
Mat tmp(shape, CV_32F, data);
tmp.copyTo(dst);
src.unMap();
}
vkcom::Tensor VkComTensor(const Ptr<BackendWrapper>& ptr)
{
CV_Assert(!ptr.empty());
return ptr.dynamicCast<VkComBackendWrapper>()->getTensor();
}
void setDirty(std::vector<Ptr<BackendWrapper> >& ptrs)
{
for (const Ptr<BackendWrapper>& ptr : ptrs)
{
ptr.dynamicCast<VkComBackendWrapper>()->setDeviceDirty();
}
}
std::vector<vkcom::Tensor> VkComTensors(const std::vector<Ptr<BackendWrapper> >& ptrs)
{
std::vector<vkcom::Tensor> vec;
vec.reserve(ptrs.size());
for (const Ptr<BackendWrapper>& ptr : ptrs)
{
vec.push_back(VkComTensor(ptr));
}
return vec;
}
VkComBackendNode::VkComBackendNode(const std::vector<Ptr<BackendWrapper> >& inputsWrapper,
const std::shared_ptr<vkcom::OpBase>& op,
const std::vector<Ptr<BackendWrapper> >& blobsWrapper)
: BackendNode(DNN_BACKEND_VKCOM)
{
operation = op;
inputsWrapper_ = inputsWrapper;
ins = VkComTensors(inputsWrapper_);
if (!blobsWrapper.empty())
{
blobs = VkComTensors(blobsWrapper);
}
}
bool VkComBackendNode::forward(std::vector<vkcom::Tensor>& outs)
{
for (int i = 0, n = inputsWrapper_.size(); i < n; ++i)
{
inputsWrapper_[i].dynamicCast<VkComBackendWrapper>()->copyToDevice();
}
return operation->forward(ins, blobs, outs);
}
VkComBackendWrapper::VkComBackendWrapper(Mat& m) : BackendWrapper(DNN_BACKEND_VKCOM, DNN_TARGET_VULKAN)
{
copyToTensor(tensor, m);
host = &m;
hostDirty = false;
deviceDirty = false;
}
VkComBackendWrapper::VkComBackendWrapper(const Ptr<BackendWrapper>& baseBuffer, Mat& m)
: BackendWrapper(DNN_BACKEND_VKCOM, DNN_TARGET_VULKAN)
{
Ptr<VkComBackendWrapper> base = baseBuffer.dynamicCast<VkComBackendWrapper>();
CV_Assert(!base.empty());
host = &m;
tensor = base->tensor;
CV_Assert(tensor.count() >= m.total());
tensor.reshape(0, shape(m));
hostDirty = false;
deviceDirty = false;
}
void VkComBackendWrapper::copyToHost()
{
if (deviceDirty)
copyToMat(*host, tensor);
}
void VkComBackendWrapper::setHostDirty()
{
hostDirty = true;
};
void VkComBackendWrapper::setDeviceDirty()
{
deviceDirty = true;
};
void VkComBackendWrapper::copyToDevice()
{
if (hostDirty)
{
copyToTensor(tensor, *host);
hostDirty = false;
}
}
vkcom::Tensor VkComBackendWrapper::getTensor()
{
return tensor;
}
#endif
void forwardVkCom(std::vector<Ptr<BackendWrapper> > &outputs,
const Ptr<BackendNode>& node)
{
#ifdef HAVE_VULKAN
CV_Assert(!node.empty());
Ptr<VkComBackendNode> node_ = node.dynamicCast<VkComBackendNode>();
std::vector<vkcom::Tensor> outs = VkComTensors(outputs);
node_->forward(outs);
setDirty(outputs);
#endif
}
bool haveVulkan()
{
#ifdef HAVE_VULKAN
return vkcom::isAvailable();
#else
return false;
#endif // HAVE_VULKAN
}
} // namespace dnn
} // namespace cv