From 725d83b0e544fb5b56b935a6fb37a417850bc0b1 Mon Sep 17 00:00:00 2001 From: Alexey Spizhevoy Date: Tue, 15 Feb 2011 10:03:26 +0000 Subject: [PATCH] added init() function into MultiGpuMgr, added samples --- modules/gpu/include/opencv2/gpu/gpu.hpp | 7 +- modules/gpu/src/multi_gpu_mgr.cpp | 17 +++- samples/gpu/driver_api_stereo_multi.cpp | 8 +- samples/gpu/multi.cpp | 102 ++++++++++++++++++++++ samples/gpu/stereo_multi.cpp | 148 ++++++++++++++++++++++++++++++++ 5 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 samples/gpu/multi.cpp create mode 100644 samples/gpu/stereo_multi.cpp diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 173e45d..76ae149 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -131,15 +131,16 @@ namespace cv /////////////////////////// Multi GPU Manager ////////////////////////////// - // Provides functionality for working with many GPUs. Object of this - // class must be created before any OpenCV GPU call and no call must - // be done after its destruction. + // Provides functionality for working with many GPUs class CV_EXPORTS MultiGpuMgr { public: MultiGpuMgr(); ~MultiGpuMgr(); + // Must be called before any other GPU calls + void init(); + // Makes the given GPU active void gpuOn(int gpu_id); diff --git a/modules/gpu/src/multi_gpu_mgr.cpp b/modules/gpu/src/multi_gpu_mgr.cpp index c435073..b251366 100644 --- a/modules/gpu/src/multi_gpu_mgr.cpp +++ b/modules/gpu/src/multi_gpu_mgr.cpp @@ -48,6 +48,7 @@ namespace cv { namespace gpu { class MultiGpuMgr::Impl {}; MultiGpuMgr::MultiGpuMgr() { throw_nogpu(); } +void MultiGpuMgr::init() { throw_nogpu(); } void MultiGpuMgr::gpuOn(int) { throw_nogpu(); } void MultiGpuMgr::gpuOff() { throw_nogpu(); } @@ -79,6 +80,8 @@ public: void gpuOn(int gpu_id) { + if (gpu_id < 0 || gpu_id >= num_devices_) + CV_Error(CV_StsBadArg, "MultiGpuMgr::gpuOn: GPU ID is out of range"); cuSafeCall(cuCtxPushCurrent(contexts_[gpu_id])); } @@ -105,6 +108,8 @@ MultiGpuMgr::Impl::Impl(): num_devices_(0) num_devices_ = getCudaEnabledDeviceCount(); contexts_.resize(num_devices_); + cuSafeCall(cuInit(0)); + CUdevice device; CUcontext prev_context; for (int i = 0; i < num_devices_; ++i) @@ -116,18 +121,28 @@ MultiGpuMgr::Impl::Impl(): num_devices_(0) } -MultiGpuMgr::MultiGpuMgr(): impl_(new Impl()) {} +MultiGpuMgr::MultiGpuMgr() {} MultiGpuMgr::~MultiGpuMgr() {} +void MultiGpuMgr::init() +{ + impl_ = Ptr(new Impl()); +} + + void MultiGpuMgr::gpuOn(int gpu_id) { + if (impl_.empty()) + CV_Error(CV_StsNullPtr, "MultiGpuMgr::gpuOn: must be initialized before any calls"); impl_->gpuOn(gpu_id); } void MultiGpuMgr::gpuOff() { + if (impl_.empty()) + CV_Error(CV_StsNullPtr, "MultiGpuMgr::gpuOff: must be initialized before any calls"); impl_->gpuOff(); } diff --git a/samples/gpu/driver_api_stereo_multi.cpp b/samples/gpu/driver_api_stereo_multi.cpp index d18ad1c..5c39f5a 100644 --- a/samples/gpu/driver_api_stereo_multi.cpp +++ b/samples/gpu/driver_api_stereo_multi.cpp @@ -120,25 +120,25 @@ int main(int argc, char** argv) // Init CUDA Driver API safeCall(cuInit(0)); - // Create context for the first GPU + // Create context for GPU #0 CUdevice device; safeCall(cuDeviceGet(&device, 0)); safeCall(cuCtxCreate(&contexts[0], 0, device)); contextOff(); - // Create context for the second GPU + // Create context for GPU #1 safeCall(cuDeviceGet(&device, 1)); safeCall(cuCtxCreate(&contexts[1], 0, device)); contextOff(); - // Split source images for processing on the first GPU + // Split source images for processing on GPU #0 contextOn(0); d_left[0].upload(left.rowRange(0, left.rows / 2)); d_right[0].upload(right.rowRange(0, right.rows / 2)); bm[0] = new StereoBM_GPU(); contextOff(); - // Split source images for processing on the second GPU + // Split source images for processing on the GPU #1 contextOn(1); d_left[1].upload(left.rowRange(left.rows / 2, left.rows)); d_right[1].upload(right.rowRange(right.rows / 2, right.rows)); diff --git a/samples/gpu/multi.cpp b/samples/gpu/multi.cpp new file mode 100644 index 0000000..447816d --- /dev/null +++ b/samples/gpu/multi.cpp @@ -0,0 +1,102 @@ +/* This sample demonstrates the way you can perform independed tasks + on the different GPUs */ + +// Disable some warnings which are caused with CUDA headers +#if defined(_MSC_VER) +#pragma warning(disable: 4201 4408 4100) +#endif + +#include +#include +#include +#include + +#if !defined(HAVE_CUDA) || !defined(HAVE_TBB) + +int main() +{ +#if !defined(HAVE_CUDA) + cout << "CUDA support is required (CMake key 'WITH_CUDA' must be true).\n"; +#endif + +#if !defined(HAVE_TBB) + cout << "TBB support is required (CMake key 'WITH_TBB' must be true).\n"; +#endif + + return 0; +} + +#else + +#include "opencv2/core/internal.hpp" // For TBB wrappers + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +struct Worker { void operator()(int device_id) const; }; + +MultiGpuMgr multi_gpu_mgr; + +int main() +{ + int num_devices = getCudaEnabledDeviceCount(); + if (num_devices < 2) + { + cout << "Two or more GPUs are required\n"; + return -1; + } + for (int i = 0; i < num_devices; ++i) + { + DeviceInfo dev_info(i); + if (!dev_info.isCompatible()) + { + cout << "GPU module isn't built for GPU #" << i << " (" + << dev_info.name() << ", CC " << dev_info.majorVersion() + << dev_info.minorVersion() << "\n"; + return -1; + } + } + + multi_gpu_mgr.init(); + + // Execute calculation in two threads using two GPUs + int devices[] = {0, 2}; + parallel_do(devices, devices + 2, Worker()); + + return 0; +} + + +void Worker::operator()(int device_id) const +{ + multi_gpu_mgr.gpuOn(device_id); + + Mat src(1000, 1000, CV_32F); + Mat dst; + + RNG rng(0); + rng.fill(src, RNG::UNIFORM, 0, 1); + + // CPU works + transpose(src, dst); + + // GPU works + GpuMat d_src(src); + GpuMat d_dst; + transpose(d_src, d_dst); + + // Check results + bool passed = norm(dst - Mat(d_dst), NORM_INF) < 1e-3; + cout << "GPU #" << device_id << " (" << DeviceInfo().name() << "): " + << (passed ? "passed" : "FAILED") << endl; + + // Deallocate data here, otherwise deallocation will be performed + // after context is extracted from the stack + d_src.release(); + d_dst.release(); + + multi_gpu_mgr.gpuOff(); +} + +#endif diff --git a/samples/gpu/stereo_multi.cpp b/samples/gpu/stereo_multi.cpp new file mode 100644 index 0000000..6e6cfe5 --- /dev/null +++ b/samples/gpu/stereo_multi.cpp @@ -0,0 +1,148 @@ +/* This sample demonstrates working on one piece of data using two GPUs. + It splits input into two parts and processes them separately on different + GPUs. */ + +// Disable some warnings which are caused with CUDA headers +#if defined(_MSC_VER) +#pragma warning(disable: 4201 4408 4100) +#endif + +#include +#include +#include +#include +#include + +#if !defined(HAVE_CUDA) || !defined(HAVE_TBB) + +int main() +{ +#if !defined(HAVE_CUDA) + cout << "CUDA support is required (CMake key 'WITH_CUDA' must be true).\n"; +#endif + +#if !defined(HAVE_TBB) + cout << "TBB support is required (CMake key 'WITH_TBB' must be true).\n"; +#endif + + return 0; +} + +#else + +#include "opencv2/core/internal.hpp" // For TBB wrappers + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +struct Worker { void operator()(int device_id) const; }; + +MultiGpuMgr multi_gpu_mgr; + +// GPUs data +GpuMat d_left[2]; +GpuMat d_right[2]; +StereoBM_GPU* bm[2]; +GpuMat d_result[2]; + +// CPU result +Mat result; + +int main(int argc, char** argv) +{ + if (argc < 3) + { + cout << "Usage: stereo_multi_gpu \n"; + return -1; + } + + int num_devices = getCudaEnabledDeviceCount(); + if (num_devices < 2) + { + cout << "Two or more GPUs are required\n"; + return -1; + } + for (int i = 0; i < num_devices; ++i) + { + DeviceInfo dev_info(i); + if (!dev_info.isCompatible()) + { + cout << "GPU module isn't built for GPU #" << i << " (" + << dev_info.name() << ", CC " << dev_info.majorVersion() + << dev_info.minorVersion() << "\n"; + return -1; + } + } + + // Load input data + Mat left = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); + Mat right = imread(argv[2], CV_LOAD_IMAGE_GRAYSCALE); + if (left.empty()) + { + cout << "Cannot open '" << argv[1] << "'\n"; + return -1; + } + if (right.empty()) + { + cout << "Cannot open '" << argv[2] << "'\n"; + return -1; + } + + multi_gpu_mgr.init(); + + // Split source images for processing on the GPU #0 + multi_gpu_mgr.gpuOn(0); + d_left[0].upload(left.rowRange(0, left.rows / 2)); + d_right[0].upload(right.rowRange(0, right.rows / 2)); + bm[0] = new StereoBM_GPU(); + multi_gpu_mgr.gpuOff(); + + // Split source images for processing on the GPU #1 + multi_gpu_mgr.gpuOn(1); + d_left[1].upload(left.rowRange(left.rows / 2, left.rows)); + d_right[1].upload(right.rowRange(right.rows / 2, right.rows)); + bm[1] = new StereoBM_GPU(); + multi_gpu_mgr.gpuOff(); + + // Execute calculation in two threads using two GPUs + int devices[] = {0, 1}; + parallel_do(devices, devices + 2, Worker()); + + // Release the first GPU resources + multi_gpu_mgr.gpuOn(0); + imshow("GPU #0 result", Mat(d_result[0])); + d_left[0].release(); + d_right[0].release(); + d_result[0].release(); + delete bm[0]; + multi_gpu_mgr.gpuOff(); + + // Release the second GPU resources + multi_gpu_mgr.gpuOn(1); + imshow("GPU #1 result", Mat(d_result[1])); + d_left[1].release(); + d_right[1].release(); + d_result[1].release(); + delete bm[1]; + multi_gpu_mgr.gpuOff(); + + waitKey(); + return 0; +} + + +void Worker::operator()(int device_id) const +{ + multi_gpu_mgr.gpuOn(device_id); + + bm[device_id]->operator()(d_left[device_id], d_right[device_id], + d_result[device_id]); + + cout << "GPU #" << device_id << " (" << DeviceInfo().name() + << "): finished\n"; + + multi_gpu_mgr.gpuOff(); +} + +#endif -- 2.7.4