Repository for OpenCV's extra modules
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

419 lines
15 KiB

// 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 Amir Hassan (kallaballa) <amir@viel-zu.org>
#include "framebuffercontext.hpp"
#include "opencv2/v4d/util.hpp"
#include "opencv2/v4d/v4d.hpp"
namespace cv {
namespace v4d {
namespace detail {
FrameBufferContext::FrameBufferContext(const FrameBufferContext& other) : FrameBufferContext(other.frameBufferSize_, true, other.title_, other.major_, other.minor_, other.compat_, other.samples_, other.debug_, other.glfwWindow_, &other) {
}
FrameBufferContext::FrameBufferContext(const cv::Size& frameBufferSize, bool offscreen,
const string& title, int major, int minor, bool compat, int samples, bool debug, GLFWwindow* sharedWindow, const FrameBufferContext* parent) :
offscreen_(offscreen), title_(title), major_(major), minor_(
minor), compat_(compat), samples_(samples), debug_(debug), frameBufferSize_(frameBufferSize), isShared_(sharedWindow != nullptr), parent_(parent) {
if (glfwInit() != GLFW_TRUE)
assert(false);
#ifndef OPENCV_V4D_USE_ES3
if(parent_ != nullptr)
textureID_ = parent_->textureID_;
#else
isShared_ = false;
#endif
glfwSetErrorCallback(cv::v4d::glfw_error_callback);
if (debug_)
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
if (offscreen_)
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwSetTime(0);
#ifdef __APPLE__
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#elif defined(OPENCV_V4D_USE_ES3)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major_);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor_);
glfwWindowHint(GLFW_OPENGL_PROFILE, compat_ ? GLFW_OPENGL_COMPAT_PROFILE : GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API) ;
#endif
glfwWindowHint(GLFW_SAMPLES, samples_);
glfwWindowHint(GLFW_RED_BITS, 8);
glfwWindowHint(GLFW_GREEN_BITS, 8);
glfwWindowHint(GLFW_BLUE_BITS, 8);
glfwWindowHint(GLFW_ALPHA_BITS, 8);
glfwWindowHint(GLFW_STENCIL_BITS, 8);
glfwWindowHint(GLFW_DEPTH_BITS, 24);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// glfwWindowHint(GLFW_DOUBLEBUFFER, GL_FALSE);
glfwWindow_ = glfwCreateWindow(frameBufferSize.width, frameBufferSize.height, title_.c_str(), nullptr,
sharedWindow);
if (glfwWindow_ == NULL) {
assert(false);
}
glfwMakeContextCurrent(glfwWindow_);
#ifndef OPENCV_V4D_USE_ES3
glewExperimental = true;
glewInit();
try {
if (isClGlSharingSupported())
cv::ogl::ocl::initializeContextFromGL();
else
clglSharing_ = false;
} catch (std::exception& ex) {
cerr << "CL-GL sharing failed: " << ex.what() << endl;
clglSharing_ = false;
} catch (...) {
cerr << "CL-GL sharing failed with unknown error." << endl;
clglSharing_ = false;
}
#else
clglSharing_ = false;
#endif
setup(frameBufferSize_);
#ifndef __EMSCRIPTEN__
context_ = CLExecContext_t::getCurrent();
#endif
}
FrameBufferContext::~FrameBufferContext() {
teardown();
}
void FrameBufferContext::setup(const cv::Size& sz) {
frameBufferSize_ = sz;
glfwMakeContextCurrent(getGLFWWindow());
if(!isShared_) {
GL_CHECK(glGenFramebuffers(1, &frameBufferID_));
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID_));
GL_CHECK(glGenRenderbuffers(1, &renderBufferID_));
GL_CHECK(glGenTextures(1, &textureID_));
GL_CHECK(glBindTexture(GL_TEXTURE_2D, textureID_));
texture_ = new cv::ogl::Texture2D(sz, cv::ogl::Texture2D::RGBA, textureID_);
GL_CHECK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
GL_CHECK(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sz.width, sz.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, renderBufferID_));
GL_CHECK(
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, sz.width, sz.height));
GL_CHECK(
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBufferID_));
#ifndef OPENCV_V4D_USE_ES3
GL_CHECK(
glNamedFramebufferTexture(frameBufferID_, GL_COLOR_ATTACHMENT0, textureID_, 0));
#else
GL_CHECK(
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID_, 0));
#endif
assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
} else {
assert(parent_ != nullptr);
textureID_ = parent_->textureID_;
GL_CHECK(glGenFramebuffers(1, &frameBufferID_));
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID_));
GL_CHECK(glBindTexture(GL_TEXTURE_2D, textureID_));
texture_ = new cv::ogl::Texture2D(sz, cv::ogl::Texture2D::RGBA, textureID_);
GL_CHECK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
GL_CHECK(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sz.width, sz.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
GL_CHECK(glGenRenderbuffers(1, &renderBufferID_));
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, renderBufferID_));
GL_CHECK(
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, sz.width, sz.height));
GL_CHECK(
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBufferID_));
#ifndef OPENCV_V4D_USE_ES3
GL_CHECK(
glNamedFramebufferTexture(frameBufferID_, GL_COLOR_ATTACHMENT0, textureID_, 0));
#else
GL_CHECK(
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID_, 0));
#endif
assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
}
}
void FrameBufferContext::teardown() {
using namespace cv::ocl;
glfwMakeContextCurrent(getGLFWWindow());
#ifndef __EMSCRIPTEN__
if(clImage_ != nullptr) {
CLExecScope_t clExecScope(getCLExecContext());
cl_int status = 0;
cl_command_queue q = (cl_command_queue) Queue::getDefault().ptr();
status = clEnqueueReleaseGLObjects(q, 1, &clImage_, 0, NULL, NULL);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clEnqueueReleaseGLObjects failed: %d", status));
status = clFinish(q); // TODO Use events
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clFinish failed: %d", status));
status = clReleaseMemObject(clImage_); // TODO RAII
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError, ("OpenCL: clReleaseMemObject failed: %d", status));
clImage_ = nullptr;
}
#endif
GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
assert(texture_ != nullptr);
delete texture_;
GL_CHECK(glDeleteTextures(1, &textureID_));
GL_CHECK(glDeleteRenderbuffers(1, &renderBufferID_));
GL_CHECK(glDeleteFramebuffers(1, &frameBufferID_));
}
void FrameBufferContext::toGLTexture2D(cv::UMat& u, cv::ogl::Texture2D& texture) {
#ifdef __EMSCRIPTEN__
CV_UNUSED(u);
CV_UNUSED(texture);
#else
using namespace cv::ocl;
cl_int status = 0;
cl_command_queue q = (cl_command_queue) Queue::getDefault().ptr();
if (clImage_ == nullptr) {
Context& ctx = Context::getDefault();
cl_context context = (cl_context) ctx.ptr();
clImage_ = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY, 0x0DE1, 0, texture.texId(),
&status);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clCreateFromGLTexture failed: %d", status));
status = clEnqueueAcquireGLObjects(q, 1, &clImage_, 0, NULL, NULL);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clEnqueueAcquireGLObjects failed: %d", status));
}
cl_mem clBuffer = (cl_mem) u.handle(ACCESS_READ);
size_t offset = 0;
size_t dst_origin[3] = { 0, 0, 0 };
size_t region[3] = { (size_t) u.cols, (size_t) u.rows, 1 };
status = clEnqueueCopyBufferToImage(q, clBuffer, clImage_, offset, dst_origin, region, 0, NULL,
NULL);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clEnqueueCopyBufferToImage failed: %d", status));
#endif
}
void FrameBufferContext::fromGLTexture2D(const cv::ogl::Texture2D& texture, cv::UMat& u) {
#ifdef __EMSCRIPTEN__
CV_UNUSED(u);
CV_UNUSED(texture);
#else
using namespace cv::ocl;
const int dtype = CV_8UC4;
int textureType = dtype;
if (u.size() != texture.size() || u.type() != textureType) {
u.create(texture.size(), textureType);
}
cl_command_queue q = (cl_command_queue) Queue::getDefault().ptr();
cl_int status = 0;
if (clImage_ == nullptr) {
Context& ctx = Context::getDefault();
cl_context context = (cl_context) ctx.ptr();
clImage_ = clCreateFromGLTexture(context, CL_MEM_READ_ONLY, 0x0DE1, 0, texture.texId(),
&status);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clCreateFromGLTexture failed: %d", status));
status = clEnqueueAcquireGLObjects(q, 1, &clImage_, 0, NULL, NULL);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clEnqueueAcquireGLObjects failed: %d", status));
}
cl_mem clBuffer = (cl_mem) u.handle(ACCESS_WRITE);
size_t offset = 0;
size_t src_origin[3] = { 0, 0, 0 };
size_t region[3] = { (size_t) u.cols, (size_t) u.rows, 1 };
status = clEnqueueCopyImageToBuffer(q, clImage_, clBuffer, src_origin, region, offset, 0, NULL,
NULL);
if (status != CL_SUCCESS)
CV_Error_(cv::Error::OpenCLApiCallError,
("OpenCL: clEnqueueCopyImageToBuffer failed: %d", status));
#endif
}
cv::Size FrameBufferContext::getSize() {
return frameBufferSize_;
}
void FrameBufferContext::execute(std::function<void(cv::UMat&)> fn) {
if(frameBuffer_.empty())
frameBuffer_.create(getSize(), CV_8UC4);
cv::resize(frameBuffer_,frameBuffer_, getSize());
#ifndef __EMSCRIPTEN__
CLExecScope_t clExecScope(getCLExecContext());
#endif
FrameBufferContext::GLScope glScope(*this);
FrameBufferContext::FrameBufferScope fbScope(*this, frameBuffer_);
fn(frameBuffer_);
}
cv::ogl::Texture2D& FrameBufferContext::getTexture2D() {
return *texture_;
}
GLFWwindow* FrameBufferContext::getGLFWWindow() {
return glfwWindow_;
}
#ifndef __EMSCRIPTEN__
CLExecContext_t& FrameBufferContext::getCLExecContext() {
return context_;
}
#endif
void FrameBufferContext::blitFrameBufferToScreen(const cv::Rect& viewport,
const cv::Size& windowSize, bool stretch) {
GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferID_));
if(!isShared_) {
GL_CHECK(glReadBuffer(GL_COLOR_ATTACHMENT0));
} else {
#ifndef OPENCV_V4D_USE_ES3
GL_CHECK(glReadBuffer(GL_COLOR_ATTACHMENT0));
#else
GL_CHECK(glReadBuffer(GL_COLOR_ATTACHMENT0));
#endif
}
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
GL_CHECK(
glBlitFramebuffer( viewport.x, viewport.y, viewport.x + viewport.width, viewport.y + viewport.height, stretch ? 0 : windowSize.width - frameBufferSize_.width, stretch ? 0 : windowSize.height - frameBufferSize_.height, stretch ? windowSize.width : frameBufferSize_.width, stretch ? windowSize.height : frameBufferSize_.height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
}
void FrameBufferContext::begin() {
glfwMakeContextCurrent(getGLFWWindow());
GL_CHECK(glGetIntegerv( GL_VIEWPORT, viewport_ ));
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID_));
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, renderBufferID_));
GL_CHECK(
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBufferID_));
GL_CHECK(glBindTexture(GL_TEXTURE_2D, textureID_));
GL_CHECK(
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID_, 0));
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
GL_CHECK(glViewport(0, 0, frameBufferSize_.width, frameBufferSize_.height));
}
void FrameBufferContext::end() {
GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GL_CHECK(glViewport(viewport_[0], viewport_[1], viewport_[2], viewport_[3]));
GL_CHECK(glFlush());
GL_CHECK(glFinish());
glfwMakeContextCurrent(nullptr);
}
void FrameBufferContext::download(cv::UMat& m) {
cv::Mat tmp = m.getMat(cv::ACCESS_WRITE);
assert(tmp.data != nullptr);
//this should use a PBO for the pixel transfer, but i couldn't get it to work for both opengl and webgl at the same time
GL_CHECK(glReadPixels(0, 0, tmp.cols, tmp.rows, GL_RGBA, GL_UNSIGNED_BYTE, tmp.data));
tmp.release();
}
void FrameBufferContext::upload(const cv::UMat& m) {
cv::Mat tmp = m.getMat(cv::ACCESS_READ);
assert(tmp.data != nullptr);
GL_CHECK(
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, tmp.cols, tmp.rows, GL_RGBA, GL_UNSIGNED_BYTE, tmp.data));
tmp.release();
}
void FrameBufferContext::acquireFromGL(cv::UMat& m) {
if (clglSharing_) {
GL_CHECK(fromGLTexture2D(getTexture2D(), m));
} else {
download(m);
GL_CHECK(glFlush());
GL_CHECK(glFinish());
}
//FIXME
cv::flip(m, m, 0);
}
void FrameBufferContext::releaseToGL(cv::UMat& m) {
//FIXME
cv::flip(m, m, 0);
if (clglSharing_) {
GL_CHECK(toGLTexture2D(m, getTexture2D()));
} else {
upload(m);
GL_CHECK(glFlush());
GL_CHECK(glFinish());
}
}
float FrameBufferContext::getXPixelRatio() {
makeCurrent();
#ifdef __EMSCRIPTEN__
return emscripten_get_device_pixel_ratio();
#else
float xscale, yscale;
glfwGetWindowContentScale(getGLFWWindow(), &xscale, &yscale);
return xscale;
#endif
}
float FrameBufferContext::getYPixelRatio() {
makeCurrent();
#ifdef __EMSCRIPTEN__
return emscripten_get_device_pixel_ratio();
#else
float xscale, yscale;
glfwGetWindowContentScale(getGLFWWindow(), &xscale, &yscale);
return yscale;
#endif
}
void FrameBufferContext::makeCurrent() {
glfwMakeContextCurrent(getGLFWWindow());
}
void FrameBufferContext::makeNoneCurrent() {
glfwMakeContextCurrent(nullptr);
}
}
}
}