From 89392b2a6d28a323f7a4c5f5ea9798237d01605f Mon Sep 17 00:00:00 2001 From: Alexey Ershov Date: Mon, 6 Jul 2015 17:46:18 +0300 Subject: [PATCH] added convertToGLBuffer() & convertFromGLBuffer() functions; added OpenGL interop sample comment rewrite & change convertFromGLBuffer() & convertToGLBuffer() into acquireGLBuffer() & releaseGLBuffer(), respectively opengl sample: added buffer support tested and fixed buffer support on Windows change glFlush() call to glFinish() added UMat::release() call; fixed functions' names adopted & implemented API suggestion(s) from Alexander fixed unreachable code warning added more info to the mapGLBuffer/unmapGLBuffer description --- modules/core/include/opencv2/core/opengl.hpp | 21 ++++ modules/core/src/opengl.cpp | 80 +++++++++++++ samples/opengl/opengl_interop.cpp | 113 ++++++++++++++++--- 3 files changed, 198 insertions(+), 16 deletions(-) diff --git a/modules/core/include/opencv2/core/opengl.hpp b/modules/core/include/opencv2/core/opengl.hpp index 8c3235f5a..fd47c520e 100644 --- a/modules/core/include/opencv2/core/opengl.hpp +++ b/modules/core/include/opencv2/core/opengl.hpp @@ -537,6 +537,27 @@ CV_EXPORTS void convertToGLTexture2D(InputArray src, Texture2D& texture); */ CV_EXPORTS void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst); +/** @brief Maps Buffer object to process on CL side (convert to UMat). + +Function creates CL buffer from GL one, and then constructs UMat that can be used +to process buffer data with OpenCV functions. Note that in current implementation +UMat constructed this way doesn't own corresponding GL buffer object, so it is +the user responsibility to close down CL/GL buffers relationships by explicitly +calling unmapGLBuffer() function. +@param buffer - source Buffer object. +@param accessFlags - data access flags (ACCESS_READ|ACCESS_WRITE). +@return Returns UMat object + */ +CV_EXPORTS UMat mapGLBuffer(const Buffer& buffer, int accessFlags = ACCESS_READ|ACCESS_WRITE); + +/** @brief Unmaps Buffer object (releases UMat, previously mapped from Buffer). + +Function must be called explicitly by the user for each UMat previously constructed +by the call to mapGLBuffer() function. +@param u - source UMat, created by mapGLBuffer(). + */ +CV_EXPORTS void unmapGLBuffer(UMat& u); + }} // namespace cv::ogl namespace cv { namespace cuda { diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index 3c3c6d2ab..3bbc0f8e9 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -1804,4 +1804,84 @@ void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst) #endif } +//void mapGLBuffer(const Buffer& buffer, UMat& dst, int accessFlags) +UMat mapGLBuffer(const Buffer& buffer, int accessFlags) +{ + (void)buffer; (void)accessFlags; +#if !defined(HAVE_OPENGL) + NO_OPENGL_SUPPORT_ERROR; +#elif !defined(HAVE_OPENCL) + NO_OPENCL_SUPPORT_ERROR; +#else + using namespace cv::ocl; + Context& ctx = Context::getDefault(); + cl_context context = (cl_context)ctx.ptr(); + cl_command_queue clQueue = (cl_command_queue)Queue::getDefault().ptr(); + + int clAccessFlags = 0; + switch (accessFlags & (ACCESS_READ|ACCESS_WRITE)) + { + default: + case ACCESS_READ|ACCESS_WRITE: + clAccessFlags = CL_MEM_READ_WRITE; + break; + case ACCESS_READ: + clAccessFlags = CL_MEM_READ_ONLY; + break; + case ACCESS_WRITE: + clAccessFlags = CL_MEM_WRITE_ONLY; + break; + } + + cl_int status = 0; + cl_mem clBuffer = clCreateFromGLBuffer(context, clAccessFlags, buffer.bufId(), &status); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLBuffer failed"); + + gl::Finish(); + + status = clEnqueueAcquireGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + + size_t step = buffer.cols() * buffer.elemSize(); + int rows = buffer.rows(); + int cols = buffer.cols(); + int type = buffer.type(); + + UMat u; + convertFromBuffer(clBuffer, step, rows, cols, type, u); + return u; +#endif +} + +void unmapGLBuffer(UMat& u) +{ + (void)u; +#if !defined(HAVE_OPENGL) + NO_OPENGL_SUPPORT_ERROR; +#elif !defined(HAVE_OPENCL) + NO_OPENCL_SUPPORT_ERROR; +#else + using namespace cv::ocl; + cl_command_queue clQueue = (cl_command_queue)Queue::getDefault().ptr(); + + cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); + + u.release(); + + cl_int status = clEnqueueReleaseGLObjects(clQueue, 1, &clBuffer, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + + status = clFinish(clQueue); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + + status = clReleaseMemObject(clBuffer); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); +#endif +} + }} // namespace cv::ogl diff --git a/samples/opengl/opengl_interop.cpp b/samples/opengl/opengl_interop.cpp index 00a8a2684..4900d1c5d 100644 --- a/samples/opengl/opengl_interop.cpp +++ b/samples/opengl/opengl_interop.cpp @@ -1,3 +1,10 @@ +/* +// Sample demonstrating interoperability of OpenCV UMat with OpenGL texture. +// At first, the data obtained from video file or camera and placed onto +// OpenGL texture, following mapping of this OpenGL texture to OpenCV UMat +// and call cv::Blur function. The result is mapped back to OpenGL texture +// and rendered through OpenGL API. +*/ #if defined(WIN32) || defined(_WIN32) # define WIN32_LEAN_AND_MEAN # include @@ -25,6 +32,16 @@ # pragma comment(lib, "glu32.lib") #endif +/* +// Press key to +// 0 no processing +// 1 processing on CPU +// 2 processing on GPU +// 9 toggle texture/buffer +// space toggle processing on/off, preserve mode +// esc quit +*/ + class GLWinApp : public WinApp { public: @@ -33,9 +50,12 @@ public: { m_shutdown = false; m_mode = 0; - m_modeStr[0] = cv::String("No processing"); - m_modeStr[1] = cv::String("Processing on CPU"); - m_modeStr[2] = cv::String("Processing on GPU"); + m_modeStr[0] = cv::String("Texture/No processing"); + m_modeStr[1] = cv::String("Texture/Processing on CPU"); + m_modeStr[2] = cv::String("Texture/Processing on GPU"); + m_modeStr[3] = cv::String("Buffer/No processing"); + m_modeStr[4] = cv::String("Buffer/Processing on CPU"); + m_modeStr[5] = cv::String("Buffer/Processing on GPU"); m_disableProcessing = false; m_cap = cap; } @@ -60,7 +80,12 @@ public: case WM_CHAR: if (wParam >= '0' && wParam <= '2') { - m_mode = (char)wParam - '0'; + set_mode((char)wParam - '0'); + return 0; + } + else if (wParam == '9') + { + toggle_buffer(); return 0; } else if (wParam == VK_SPACE) @@ -131,13 +156,16 @@ public: m_disableProcessing = !m_disableProcessing; break; case XK_0: - m_mode = 0; + set_mode(0); break; case XK_1: - m_mode = 1; + set_mode(1); break; case XK_2: - m_mode = 2; + set_mode(2); + break; + case XK_9: + toggle_buffer(); break; case XK_Escape: m_end_loop = true; @@ -187,14 +215,17 @@ public: return 0; } // init() - int get_texture(cv::ogl::Texture2D& texture) + int get_frame(cv::ogl::Texture2D& texture, cv::ogl::Buffer& buffer) { if (!m_cap.read(m_frame_bgr)) return -1; cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2RGBA); - texture.copyFrom(m_frame_rgba); + if (use_buffer()) + buffer.copyFrom(m_frame_rgba); + else + texture.copyFrom(m_frame_rgba); return 0; } @@ -254,14 +285,16 @@ public: int r; cv::ogl::Texture2D texture; + cv::ogl::Buffer buffer; - r = get_texture(texture); + r = get_frame(texture, buffer); if (r != 0) { return -1; } - switch (m_mode) + bool do_buffer = use_buffer(); + switch (get_mode()) { case 0: // no processing @@ -272,13 +305,21 @@ public: // process video frame on CPU cv::Mat m(m_height, m_width, CV_8UC4); - texture.copyTo(m); + if (do_buffer) + buffer.copyTo(m); + else + texture.copyTo(m); + if (!m_disableProcessing) { // blur texture image with OpenCV on CPU cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); } - texture.copyFrom(m); + + if (do_buffer) + buffer.copyFrom(m); + else + texture.copyFrom(m); break; } @@ -288,19 +329,34 @@ public: // process video frame on GPU cv::UMat u; - cv::ogl::convertFromGLTexture2D(texture, u); + if (do_buffer) + u = cv::ogl::mapGLBuffer(buffer); + else + cv::ogl::convertFromGLTexture2D(texture, u); + if (!m_disableProcessing) { // blur texture image with OpenCV on GPU with OpenCL cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); } - cv::ogl::convertToGLTexture2D(u, texture); + + if (do_buffer) + cv::ogl::unmapGLBuffer(u); + else + cv::ogl::convertToGLTexture2D(u, texture); break; } } // switch + if (do_buffer) // buffer -> texture + { + cv::Mat m(m_height, m_width, CV_8UC4); + buffer.copyTo(m); + texture.copyFrom(m); + } + #if defined(__linux__) XWindowAttributes window_attributes; XGetWindowAttributes(m_display, m_window, &window_attributes); @@ -393,10 +449,35 @@ protected: } #endif + // modes: 0,1,2 - use texture + // 3,4,5 - use buffer + bool use_buffer() + { + return bool(m_mode >= 3); + } + void toggle_buffer() + { + if (m_mode < 3) + m_mode += 3; + else + m_mode -= 3; + } + int get_mode() + { + return (m_mode % 3); + } + void set_mode(int mode) + { + bool do_buffer = bool(m_mode >= 3); + m_mode = (mode % 3); + if (do_buffer) + m_mode += 3; + } + private: bool m_shutdown; int m_mode; - cv::String m_modeStr[3]; + cv::String m_modeStr[3*2]; int m_disableProcessing; #if defined(WIN32) || defined(_WIN32) HDC m_hDC;