1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 08:13:04 +04:00

Merge pull request #19259 from nglee:dev_gpumatnd1

Minimal implementation of GpuMatND

* GpuMatND - minimal implementation

* GpuMatND - createGpuMatHeader

* GpuMatND - GpuData, offset, getDevicePtr(), license

* reviews

* reviews
This commit is contained in:
Namgoo Lee
2021-02-06 05:30:37 +09:00
committed by GitHub
parent 9d227641c9
commit 7ea21c4b3c
4 changed files with 730 additions and 0 deletions
+269
View File
@@ -0,0 +1,269 @@
// 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.
#include "opencv2/opencv_modules.hpp"
#ifndef HAVE_OPENCV_CUDEV
#error "opencv_cudev is required"
#else
#include "opencv2/core/cuda.hpp"
#include "opencv2/cudev.hpp"
using namespace cv;
using namespace cv::cuda;
GpuData::GpuData(const size_t _size)
: data(nullptr), size(_size)
{
CV_CUDEV_SAFE_CALL(cudaMalloc(&data, _size));
}
GpuData::~GpuData()
{
CV_CUDEV_SAFE_CALL(cudaFree(data));
}
/////////////////////////////////////////////////////
/// create
void GpuMatND::create(SizeArray _size, int _type)
{
{
auto elements_nonzero = [](SizeArray& v)
{
return std::all_of(v.begin(), v.end(),
[](unsigned u){ return u > 0; });
};
CV_Assert(!_size.empty());
CV_Assert(elements_nonzero(_size));
}
_type &= Mat::TYPE_MASK;
if (size == _size && type() == _type && !empty() && !external() && isContinuous() && !isSubmatrix())
return;
release();
setFields(std::move(_size), _type);
data_ = std::make_shared<GpuData>(totalMemSize());
data = data_->data;
offset = 0;
}
/////////////////////////////////////////////////////
/// release
void GpuMatND::release()
{
data = nullptr;
data_.reset();
flags = dims = offset = 0;
size.clear();
step.clear();
}
/////////////////////////////////////////////////////
/// clone
static bool next(uchar*& d, const uchar*& s, std::vector<int>& idx, const int dims, const GpuMatND& dst, const GpuMatND& src)
{
int inc = dims-3;
while (true)
{
if (idx[inc] == src.size[inc] - 1)
{
if (inc == 0)
{
return false;
}
idx[inc] = 0;
d -= (dst.size[inc] - 1) * dst.step[inc];
s -= (src.size[inc] - 1) * src.step[inc];
inc--;
}
else
{
idx[inc]++;
d += dst.step[inc];
s += src.step[inc];
break;
}
}
return true;
}
GpuMatND GpuMatND::clone() const
{
CV_DbgAssert(!empty());
GpuMatND ret(size, type());
if (isContinuous())
{
CV_CUDEV_SAFE_CALL(cudaMemcpy(ret.getDevicePtr(), getDevicePtr(), ret.totalMemSize(), cudaMemcpyDeviceToDevice));
}
else
{
// 1D arrays are always continuous
if (dims == 2)
{
CV_CUDEV_SAFE_CALL(
cudaMemcpy2D(ret.getDevicePtr(), ret.step[0], getDevicePtr(), step[0],
size[1]*step[1], size[0], cudaMemcpyDeviceToDevice)
);
}
else
{
std::vector<int> idx(dims-2, 0);
uchar* d = ret.getDevicePtr();
const uchar* s = getDevicePtr();
// iterate each 2D plane
do
{
CV_CUDEV_SAFE_CALL(
cudaMemcpy2DAsync(
d, ret.step[dims-2], s, step[dims-2],
size[dims-1]*step[dims-1], size[dims-2], cudaMemcpyDeviceToDevice)
);
}
while (next(d, s, idx, dims, ret, *this));
CV_CUDEV_SAFE_CALL(cudaStreamSynchronize(0));
}
}
return ret;
}
GpuMatND GpuMatND::clone(Stream& stream) const
{
CV_DbgAssert(!empty());
GpuMatND ret(size, type());
cudaStream_t _stream = StreamAccessor::getStream(stream);
if (isContinuous())
{
CV_CUDEV_SAFE_CALL(cudaMemcpyAsync(ret.getDevicePtr(), getDevicePtr(), ret.totalMemSize(), cudaMemcpyDeviceToDevice, _stream));
}
else
{
// 1D arrays are always continuous
if (dims == 2)
{
CV_CUDEV_SAFE_CALL(
cudaMemcpy2DAsync(ret.getDevicePtr(), ret.step[0], getDevicePtr(), step[0],
size[1]*step[1], size[0], cudaMemcpyDeviceToDevice, _stream)
);
}
else
{
std::vector<int> idx(dims-2, 0);
uchar* d = ret.getDevicePtr();
const uchar* s = getDevicePtr();
// iterate each 2D plane
do
{
CV_CUDEV_SAFE_CALL(
cudaMemcpy2DAsync(
d, ret.step[dims-2], s, step[dims-2],
size[dims-1]*step[dims-1], size[dims-2], cudaMemcpyDeviceToDevice, _stream)
);
}
while (next(d, s, idx, dims, ret, *this));
}
}
return ret;
}
/////////////////////////////////////////////////////
/// upload
void GpuMatND::upload(InputArray src)
{
Mat mat = src.getMat();
CV_DbgAssert(!mat.empty());
if (!mat.isContinuous())
mat = mat.clone();
SizeArray _size(mat.dims);
std::copy_n(mat.size.p, mat.dims, _size.data());
create(std::move(_size), mat.type());
CV_CUDEV_SAFE_CALL(cudaMemcpy(getDevicePtr(), mat.data, totalMemSize(), cudaMemcpyHostToDevice));
}
void GpuMatND::upload(InputArray src, Stream& stream)
{
Mat mat = src.getMat();
CV_DbgAssert(!mat.empty());
if (!mat.isContinuous())
mat = mat.clone();
SizeArray _size(mat.dims);
std::copy_n(mat.size.p, mat.dims, _size.data());
create(std::move(_size), mat.type());
cudaStream_t _stream = StreamAccessor::getStream(stream);
CV_CUDEV_SAFE_CALL(cudaMemcpyAsync(getDevicePtr(), mat.data, totalMemSize(), cudaMemcpyHostToDevice, _stream));
}
/////////////////////////////////////////////////////
/// download
void GpuMatND::download(OutputArray dst) const
{
CV_DbgAssert(!empty());
dst.create(dims, size.data(), type());
Mat mat = dst.getMat();
GpuMatND gmat = *this;
if (!gmat.isContinuous())
gmat = gmat.clone();
CV_CUDEV_SAFE_CALL(cudaMemcpy(mat.data, gmat.getDevicePtr(), mat.total() * mat.elemSize(), cudaMemcpyDeviceToHost));
}
void GpuMatND::download(OutputArray dst, Stream& stream) const
{
CV_DbgAssert(!empty());
dst.create(dims, size.data(), type());
Mat mat = dst.getMat();
GpuMatND gmat = *this;
if (!gmat.isContinuous())
gmat = gmat.clone(stream);
cudaStream_t _stream = StreamAccessor::getStream(stream);
CV_CUDEV_SAFE_CALL(cudaMemcpyAsync(mat.data, gmat.getDevicePtr(), mat.total() * mat.elemSize(), cudaMemcpyDeviceToHost, _stream));
}
#endif
+180
View File
@@ -0,0 +1,180 @@
// 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.
#include "precomp.hpp"
using namespace cv;
using namespace cv::cuda;
GpuMatND::~GpuMatND() = default;
GpuMatND::GpuMatND(SizeArray _size, int _type, void* _data, StepArray _step) :
flags(0), dims(0), data(static_cast<uchar*>(_data)), offset(0)
{
CV_Assert(_step.empty() || _size.size() == _step.size() + 1);
setFields(std::move(_size), _type, std::move(_step));
}
GpuMatND GpuMatND::operator()(const std::vector<Range>& ranges) const
{
CV_Assert(dims == (int)ranges.size());
for (int i = 0; i < dims; ++i)
{
Range r = ranges[i];
CV_Assert(r == Range::all() || (0 <= r.start && r.start < r.end && r.end <= size[i]));
}
GpuMatND ret = *this;
for (int i = 0; i < dims; ++i)
{
Range r = ranges[i];
if (r != Range::all() && r != Range(0, ret.size[i]))
{
ret.offset += r.start * ret.step[i];
ret.size[i] = r.size();
ret.flags |= Mat::SUBMATRIX_FLAG;
}
}
ret.flags = cv::updateContinuityFlag(ret.flags, dims, ret.size.data(), ret.step.data());
return ret;
}
GpuMat GpuMatND::createGpuMatHeader(IndexArray idx, Range rowRange, Range colRange) const
{
CV_Assert((int)idx.size() == dims - 2);
std::vector<Range> ranges;
for (int i : idx)
ranges.emplace_back(i, i+1);
ranges.push_back(rowRange);
ranges.push_back(colRange);
return (*this)(ranges).createGpuMatHeader();
}
GpuMat GpuMatND::createGpuMatHeader() const
{
auto Effectively2D = [](GpuMatND m)
{
for (int i = 0; i < m.dims - 2; ++i)
if (m.size[i] > 1)
return false;
return true;
};
CV_Assert(Effectively2D(*this));
return GpuMat(size[dims-2], size[dims-1], type(), getDevicePtr(), step[dims-2]);
}
GpuMat GpuMatND::operator()(IndexArray idx, Range rowRange, Range colRange) const
{
return createGpuMatHeader(idx, rowRange, colRange).clone();
}
GpuMatND::operator GpuMat() const
{
return createGpuMatHeader().clone();
}
void GpuMatND::setFields(SizeArray _size, int _type, StepArray _step)
{
_type &= Mat::TYPE_MASK;
flags = Mat::MAGIC_VAL + _type;
dims = static_cast<int>(_size.size());
size = std::move(_size);
if (_step.empty())
{
step = StepArray(dims);
step.back() = elemSize();
for (int _i = dims - 2; _i >= 0; --_i)
{
const size_t i = _i;
step[i] = step[i+1] * size[i+1];
}
flags |= Mat::CONTINUOUS_FLAG;
}
else
{
step = std::move(_step);
step.push_back(elemSize());
flags = cv::updateContinuityFlag(flags, dims, size.data(), step.data());
}
CV_Assert(size.size() == step.size());
CV_Assert(step.back() == elemSize());
}
#ifndef HAVE_CUDA
GpuData::GpuData(const size_t _size)
: data(nullptr), size(0)
{
CV_UNUSED(_size);
throw_no_cuda();
}
GpuData::~GpuData()
{
}
void GpuMatND::create(SizeArray _size, int _type)
{
CV_UNUSED(_size);
CV_UNUSED(_type);
throw_no_cuda();
}
void GpuMatND::release()
{
throw_no_cuda();
}
GpuMatND GpuMatND::clone() const
{
throw_no_cuda();
}
GpuMatND GpuMatND::clone(Stream& stream) const
{
CV_UNUSED(stream);
throw_no_cuda();
}
void GpuMatND::upload(InputArray src)
{
CV_UNUSED(src);
throw_no_cuda();
}
void GpuMatND::upload(InputArray src, Stream& stream)
{
CV_UNUSED(src);
CV_UNUSED(stream);
throw_no_cuda();
}
void GpuMatND::download(OutputArray dst) const
{
CV_UNUSED(dst);
throw_no_cuda();
}
void GpuMatND::download(OutputArray dst, Stream& stream) const
{
CV_UNUSED(dst);
CV_UNUSED(stream);
throw_no_cuda();
}
#endif