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.
360 lines
11 KiB
360 lines
11 KiB
#include "viz2d.hpp" |
|
|
|
#include "util.hpp" |
|
|
|
namespace kb { |
|
void gl_check_error(const std::filesystem::path &file, unsigned int line, const char *expression) { |
|
int errorCode = glGetError(); |
|
|
|
if (errorCode != 0) { |
|
std::cerr << "GL failed in " << file.filename() << " (" << line << ") : " << "\nExpression:\n " << expression << "\nError code:\n " << errorCode << "\n " << std::endl; |
|
assert(false); |
|
} |
|
} |
|
|
|
Viz2D::Viz2D(const cv::Size &size, const cv::Size& frameBufferSize, bool offscreen, const string &title, int major, int minor, int samples, bool debug) : |
|
size_(size), frameBufferSize_(frameBufferSize), offscreen_(offscreen), title_(title), major_(major), minor_(minor), samples_(samples), debug_(debug) { |
|
assert(frameBufferSize_.width >= size_.width && frameBufferSize_.height >= size_.height); |
|
} |
|
|
|
Viz2D::~Viz2D() { |
|
//don't delete form_. it is autmatically cleaned up by screen_ |
|
if (screen_) |
|
delete screen_; |
|
if (writer_) |
|
delete writer_; |
|
if (capture_) |
|
delete capture_; |
|
if (nvgContext_) |
|
delete nvgContext_; |
|
if (clvaContext_) |
|
delete clvaContext_; |
|
if (clglContext_) |
|
delete clglContext_; |
|
glfwDestroyWindow(getGLFWWindow()); |
|
glfwTerminate(); |
|
} |
|
|
|
void Viz2D::initialize() { |
|
assert(glfwInit() == GLFW_TRUE); |
|
glfwSetErrorCallback(kb::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); |
|
#else |
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major_); |
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor_); |
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); |
|
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_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_TRUE); |
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); |
|
/* I figure we don't need double buffering because the texture is our backbuffer |
|
* But EGL/X11 anyway doesn't support rendering to the front buffer, yet. But on wayland it should work. |
|
* And I am not sure about vsync on other platforms. |
|
*/ |
|
// glfwWindowHint(GLFW_DOUBLEBUFFER, GL_FALSE); |
|
|
|
glfwWindow_ = glfwCreateWindow(size_.width, size_.height, title_.c_str(), nullptr, nullptr); |
|
if (glfwWindow_ == NULL) { |
|
std::cout << "Failed to create GLFW window" << std::endl; |
|
glfwTerminate(); |
|
exit(-1); |
|
} |
|
glfwMakeContextCurrent(getGLFWWindow()); |
|
|
|
screen_ = new nanogui::Screen(); |
|
screen_->initialize(getGLFWWindow(), false); |
|
form_ = new nanogui::FormHelper(screen_); |
|
this->setSize(size_); |
|
|
|
glfwSetWindowUserPointer(getGLFWWindow(), this); |
|
|
|
glfwSetCursorPosCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, double x, double y) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->cursor_pos_callback_event(x, y); |
|
} |
|
); |
|
glfwSetMouseButtonCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, int button, int action, int modifiers) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->mouse_button_callback_event(button, action, modifiers); |
|
} |
|
); |
|
glfwSetKeyCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, int key, int scancode, int action, int mods) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->key_callback_event(key, scancode, action, mods); |
|
} |
|
); |
|
glfwSetCharCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, unsigned int codepoint) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->char_callback_event(codepoint); |
|
} |
|
); |
|
glfwSetDropCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, int count, const char **filenames) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->drop_callback_event(count, filenames); |
|
} |
|
); |
|
glfwSetScrollCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, double x, double y) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->scroll_callback_event(x, y); |
|
} |
|
); |
|
|
|
|
|
//FIXME resize internal buffers? |
|
// glfwSetWindowContentScaleCallback(getGLFWWindow(), |
|
// [](GLFWwindow* glfwWin, float xscale, float yscale) { |
|
// } |
|
// ); |
|
|
|
glfwSetFramebufferSizeCallback(getGLFWWindow(), [](GLFWwindow *glfwWin, int width, int height) { |
|
Viz2D *v2d = (Viz2D*) glfwGetWindowUserPointer(glfwWin); |
|
v2d->screen_->resize_callback_event(width, height); |
|
} |
|
); |
|
|
|
clglContext_ = new CLGLContext(this->getFrameBufferSize()); |
|
clvaContext_ = new CLVAContext(*clglContext_); |
|
nvgContext_ = new NanoVGContext(*this, getNVGcontext(), *clglContext_); |
|
} |
|
|
|
cv::ogl::Texture2D& Viz2D::texture() { |
|
return clglContext_->getTexture2D(); |
|
} |
|
|
|
nanogui::FormHelper* Viz2D::form() { |
|
return form_; |
|
} |
|
|
|
CLGLContext& Viz2D::clgl() { |
|
return *clglContext_; |
|
} |
|
|
|
CLVAContext& Viz2D::clva() { |
|
return *clvaContext_; |
|
} |
|
|
|
NanoVGContext& Viz2D::nvg() { |
|
return *nvgContext_; |
|
} |
|
|
|
cv::Size Viz2D::getVideoFrameSize() { |
|
return clva().getVideoFrameSize(); |
|
} |
|
|
|
void Viz2D::setVideoFrameSize(const cv::Size& sz) { |
|
clva().setVideoFrameSize(sz); |
|
} |
|
|
|
void Viz2D::opengl(std::function<void(const cv::Size&)> fn) { |
|
CLExecScope_t scope(clglContext_->getCLExecContext()); |
|
CLGLContext::GLScope glScope(*clglContext_); |
|
fn(getFrameBufferSize()); |
|
} |
|
|
|
void Viz2D::opencl(std::function<void(cv::UMat&)> fn) { |
|
clgl().opencl(fn); |
|
} |
|
|
|
void Viz2D::nanovg(std::function<void(NVGcontext*, const cv::Size&)> fn) { |
|
nvg().render(fn); |
|
} |
|
|
|
bool Viz2D::captureVA() { |
|
return clva().capture([=, this](cv::UMat &videoFrame) { |
|
*(this->capture_) >> videoFrame; |
|
}); |
|
} |
|
|
|
void Viz2D::writeVA() { |
|
clva().write([=, this](const cv::UMat &videoFrame) { |
|
*(this->writer_) << videoFrame; |
|
}); |
|
} |
|
|
|
void Viz2D::makeGLFWContextCurrent() { |
|
glfwMakeContextCurrent(getGLFWWindow()); |
|
} |
|
|
|
cv::VideoWriter& Viz2D::makeVAWriter(const string &outputFilename, const int fourcc, const float fps, const cv::Size &frameSize, const int vaDeviceIndex) { |
|
writer_ = new cv::VideoWriter(outputFilename, cv::CAP_FFMPEG, cv::VideoWriter::fourcc('V', 'P', '9', '0'), fps, frameSize, { cv::VIDEOWRITER_PROP_HW_DEVICE, vaDeviceIndex, cv::VIDEOWRITER_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_VAAPI, cv::VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL, 1 }); |
|
setVideoFrameSize(frameSize); |
|
|
|
if (!clva().hasContext()) { |
|
clva().copyContext(); |
|
} |
|
return *writer_; |
|
} |
|
|
|
cv::VideoCapture& Viz2D::makeVACapture(const string &intputFilename, const int vaDeviceIndex) { |
|
//Initialize MJPEG HW decoding using VAAPI |
|
capture_ = new cv::VideoCapture(intputFilename, cv::CAP_FFMPEG, { cv::CAP_PROP_HW_DEVICE, vaDeviceIndex, cv::CAP_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_VAAPI, cv::CAP_PROP_HW_ACCELERATION_USE_OPENCL, 1 }); |
|
float w = capture_->get(cv::CAP_PROP_FRAME_WIDTH); |
|
float h = capture_->get(cv::CAP_PROP_FRAME_HEIGHT); |
|
setVideoFrameSize(cv::Size(w,h)); |
|
|
|
if (!clva().hasContext()) { |
|
clva().copyContext(); |
|
} |
|
|
|
return *capture_; |
|
} |
|
|
|
void Viz2D::clear(const cv::Scalar &rgba) { |
|
const float &r = rgba[0] / 255.0f; |
|
const float &g = rgba[1] / 255.0f; |
|
const float &b = rgba[2] / 255.0f; |
|
const float &a = rgba[3] / 255.0f; |
|
GL_CHECK(glClearColor(r, g, b, a)); |
|
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); |
|
} |
|
|
|
cv::Size Viz2D::getNativeFrameBufferSize() { |
|
int w, h; |
|
glfwGetFramebufferSize(getGLFWWindow(), &w, &h); |
|
return {w, h}; |
|
} |
|
|
|
cv::Size Viz2D::getFrameBufferSize() { |
|
return frameBufferSize_; |
|
} |
|
|
|
cv::Size Viz2D::getSize() { |
|
int w, h; |
|
glfwGetWindowSize(getGLFWWindow(), &w, &h); |
|
return {w, h}; |
|
} |
|
|
|
float Viz2D::getXPixelRatio() { |
|
#if defined(EMSCRIPTEN) |
|
return emscripten_get_device_pixel_ratio(); |
|
#else |
|
float xscale, yscale; |
|
glfwGetWindowContentScale(getGLFWWindow(), &xscale, &yscale); |
|
return xscale; |
|
#endif |
|
} |
|
|
|
float Viz2D::getYPixelRatio() { |
|
#if defined(EMSCRIPTEN) |
|
return emscripten_get_device_pixel_ratio(); |
|
#else |
|
float xscale, yscale; |
|
glfwGetWindowContentScale(getGLFWWindow(), &xscale, &yscale); |
|
return yscale; |
|
#endif |
|
} |
|
|
|
void Viz2D::setSize(const cv::Size &sz) { |
|
screen_->set_size(nanogui::Vector2i(sz.width / getXPixelRatio(), sz.height / getYPixelRatio())); |
|
} |
|
|
|
bool Viz2D::isFullscreen() { |
|
return glfwGetWindowMonitor(getGLFWWindow()) != nullptr; |
|
} |
|
|
|
void Viz2D::setFullscreen(bool f) { |
|
auto monitor = glfwGetPrimaryMonitor(); |
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor); |
|
if (f) { |
|
glfwSetWindowMonitor(getGLFWWindow(), monitor, 0, 0, mode->width, mode->height, mode->refreshRate); |
|
} else { |
|
glfwSetWindowMonitor(getGLFWWindow(), nullptr, 0, 0, getNativeFrameBufferSize().width, getNativeFrameBufferSize().height, mode->refreshRate); |
|
} |
|
setSize(size_); |
|
} |
|
|
|
bool Viz2D::isResizable() { |
|
return glfwGetWindowAttrib(getGLFWWindow(), GLFW_RESIZABLE) == GLFW_TRUE; |
|
} |
|
|
|
void Viz2D::setResizable(bool r) { |
|
glfwWindowHint(GLFW_RESIZABLE, r ? GLFW_TRUE : GLFW_FALSE); |
|
} |
|
|
|
bool Viz2D::isVisible() { |
|
return glfwGetWindowAttrib(getGLFWWindow(), GLFW_VISIBLE) == GLFW_TRUE; |
|
} |
|
|
|
void Viz2D::setVisible(bool v) { |
|
screen_->perform_layout(); |
|
glfwWindowHint(GLFW_VISIBLE, v ? GLFW_TRUE : GLFW_FALSE); |
|
screen_->set_visible(v); |
|
setSize(size_); |
|
} |
|
|
|
bool Viz2D::isOffscreen() { |
|
return offscreen_; |
|
} |
|
|
|
nanogui::Window* Viz2D::makeWindow(int x, int y, const string &title) { |
|
return form()->add_window(nanogui::Vector2i(x, y), title); |
|
} |
|
|
|
nanogui::Label* Viz2D::makeGroup(const string &label) { |
|
return form()->add_group(label); |
|
} |
|
|
|
nanogui::detail::FormWidget<bool>* Viz2D::makeFormVariable(const string &name, bool &v, const string &tooltip) { |
|
auto var = form()->add_variable(name, v); |
|
if (!tooltip.empty()) |
|
var->set_tooltip(tooltip); |
|
return var; |
|
} |
|
|
|
void Viz2D::setUseOpenCL(bool u) { |
|
clglContext_->getCLExecContext().setUseOpenCL(u); |
|
clvaContext_->getCLExecContext().setUseOpenCL(u); |
|
cv::ocl::setUseOpenCL(u); |
|
} |
|
|
|
bool Viz2D::display() { |
|
bool result = true; |
|
if (!offscreen_) { |
|
glfwPollEvents(); |
|
screen_->draw_contents(); |
|
clglContext_->blitFrameBufferToScreen(getSize()); |
|
screen_->draw_widgets(); |
|
glfwSwapBuffers(glfwWindow_); |
|
result = !glfwWindowShouldClose(glfwWindow_); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
bool Viz2D::isClosed() { |
|
return closed_; |
|
|
|
} |
|
void Viz2D::close() { |
|
setVisible(false); |
|
closed_ = true; |
|
} |
|
|
|
GLFWwindow* Viz2D::getGLFWWindow() { |
|
return glfwWindow_; |
|
} |
|
|
|
NVGcontext* Viz2D::getNVGcontext() { |
|
return screen_->nvg_context(); |
|
} |
|
} /* namespace kb */
|
|
|