diff --git a/Makefile b/Makefile index c69fb4f99..999986ddd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ CXX := g++ CXXFLAGS := -std=c++20 -pthread -fno-strict-aliasing -pedantic -Wall -march=native -flto LDFLAGS := -L/opt/local/lib -flto +LIBS := -lnanovg .PHONY: all release debian-release info debug asan clean debian-clean distclean DESTDIR := / PREFIX := /usr/local @@ -31,12 +32,12 @@ clean: dirs export LDFLAGS export CXXFLAGS +export LIBS dirs: ${MAKE} -C src/tetra/ ${MAKEFLAGS} CXX=${CXX} ${MAKECMDGOALS} ${MAKE} -C src/camera/ ${MAKEFLAGS} CXX=${CXX} ${MAKECMDGOALS} - debian-release: ${MAKE} -C src/tetra/ ${MAKEFLAGS} CXX=${CXX} release ${MAKE} -C src/camera/ ${MAKEFLAGS} CXX=${CXX} release diff --git a/src/camera/camera-demo.cpp b/src/camera/camera-demo.cpp index 01dcbf214..a9bb77b8c 100644 --- a/src/camera/camera-demo.cpp +++ b/src/camera/camera-demo.cpp @@ -30,10 +30,25 @@ const string CAPTURE_OPTIONS = "framerate;" + std::to_string(INPUT_FPS) const string WRITER_OPTIONS = ""; constexpr const int VA_HW_DEVICE_INDEX = 0; -constexpr bool OFFSCREEN = true; +constexpr bool OFFSCREEN = false; -cv::ocl::OpenCLExecutionContext VA_CONTEXT; -cv::ocl::OpenCLExecutionContext GL_CONTEXT; +void init_render() { + glViewport(0, 0, WIDTH, HEIGHT); + glColor3f(1.0, 1.0, 1.0); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-2, 2, -1.5, 1.5, 1, 40); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -3); + glRotatef(50, 1, 0, 0); + glRotatef(70, 0, 1, 0); +} void render() { //Render a tetrahedron using immediate mode because the code is more concise for a demo @@ -69,7 +84,7 @@ void render() { glFlush(); } -void glow(cv::UMat &src, int ksize = WIDTH / 85 % 2 == 0 ? WIDTH / 85 + 1 : WIDTH / 85) { +void glow_effect(cv::UMat &src, int ksize = WIDTH / 85 % 2 == 0 ? WIDTH / 85 + 1 : WIDTH / 85) { static cv::UMat resize; static cv::UMat blur; static cv::UMat src16; @@ -99,12 +114,7 @@ int main(int argc, char **argv) { using namespace kb; //Initialize OpenCL Context for VAAPI - va::init_va(); - /* - * The OpenCLExecutionContext for VAAPI needs to be copied right after init_va(). - * Now everytime you want to do VAAPI interop first bind the context. - */ - VA_CONTEXT = cv::ocl::OpenCLExecutionContext::getCurrent(); + va::init(); //Initialize MJPEG HW decoding using VAAPI cv::VideoCapture cap(INPUT_FILENAME, cv::CAP_FFMPEG, { @@ -126,17 +136,12 @@ int main(int argc, char **argv) { //If we are rendering offscreen we don't need x11 if(!OFFSCREEN) - x11::init_x11(); + x11::init(); //Passing true to init_egl will create a OpenGL debug context - egl::init_egl(); + egl::init(); //Initialize OpenCL Context for OpenGL - gl::init_gl(); - /* - * The OpenCLExecutionContext for OpenGL needs to be copied right after init_gl(). - * Now everytime you want to do OpenGL interop first bind the context. - */ - GL_CONTEXT = cv::ocl::OpenCLExecutionContext::getCurrent(); + gl::init(); cerr << "VA Version: " << va::get_info() << endl; cerr << "EGL Version: " << egl::get_info() << endl; @@ -152,14 +157,16 @@ int main(int argc, char **argv) { double tickFreq = cv::getTickFrequency(); double lastFps = INPUT_FPS; + init_render(); + while (true) { //Activate the OpenCL context for OpenGL - GL_CONTEXT.bind(); + gl::bind(); //Initially aquire the framebuffer so we can write the video frame to it - gl::acquire_frame_buffer(frameBuffer); + gl::acquire_from_gl(frameBuffer); //Activate the OpenCL context for VAAPI - VA_CONTEXT.bind(); + va::bind(); //Decode a frame on the GPU using VAAPI cap >> videoFrame; if (videoFrame.empty()) { @@ -174,31 +181,31 @@ int main(int argc, char **argv) { //Resize the frame. (OpenCL) cv::resize(videoFrameRGBA, frameBuffer, cv::Size(WIDTH, HEIGHT)); - GL_CONTEXT.bind(); + gl::bind(); //Release the frame buffer for use by OpenGL - gl::release_frame_buffer(frameBuffer); + gl::release_to_gl(frameBuffer); //Render using OpenGL render(); //Aquire the frame buffer for use by OpenCL - gl::acquire_frame_buffer(frameBuffer); + gl::acquire_from_gl(frameBuffer); //Glow effect (OpenCL) - glow(frameBuffer); + glow_effect(frameBuffer); //Color-conversion from BGRA to RGB. (OpenCL) cv::cvtColor(frameBuffer, videoFrame, cv::COLOR_BGRA2RGB); //Video frame is upside down -> flip it (OpenCL) cv::flip(videoFrame, videoFrame, 0); //Activate the OpenCL context for VAAPI - VA_CONTEXT.bind(); + va::bind(); //Encode the frame using VAAPI on the GPU. video.write(videoFrame); if(x11::is_initialized()) { //Yet again activate the OpenCL context for OpenGL - GL_CONTEXT.bind(); + gl::bind(); //Release the frame buffer for use by OpenGL - gl::release_frame_buffer(frameBuffer); + gl::release_to_gl(frameBuffer); //Blit the framebuffer we have been working on to the screen gl::blit_frame_buffer_to_screen(); @@ -207,7 +214,7 @@ int main(int argc, char **argv) { break; //Transfer the back buffer (which we have been using as frame buffer) to the native window - gl::swapBuffers(); + gl::swap_buffers(); } //Measure FPS diff --git a/src/common/subsystems.hpp b/src/common/subsystems.hpp index 60e16f04a..9a8e001c3 100644 --- a/src/common/subsystems.hpp +++ b/src/common/subsystems.hpp @@ -1,5 +1,5 @@ -#ifndef SRC_TETRA_SUBSYSTEMS_HPP_ -#define SRC_TETRA_SUBSYSTEMS_HPP_ +#ifndef SRC_SUBSYSTEMS_HPP_ +#define SRC_SUBSYSTEMS_HPP_ #include #include @@ -16,11 +16,17 @@ #include #include #include +#include +#include +#include "nanovg.h" +#define NANOVG_GL3_IMPLEMENTATION +#include "nanovg_gl.h" #include #include #include #include + using std::cout; using std::cerr; using std::endl; @@ -53,13 +59,13 @@ void eglCheckError(const std::filesystem::path &file, unsigned int line, const c namespace va { //code in the kb::va namespace adapted from https://github.com/opencv/opencv/blob/4.x/samples/va_intel/display.cpp.inc +cv::ocl::OpenCLExecutionContext context; +VADisplay display = NULL; +bool initialized = false; bool open_display(); void close_display(); -VADisplay display = NULL; -bool initialized = false; - #define VA_INTEL_PCI_DIR "/sys/bus/pci/devices" #define VA_INTEL_DRI_DIR "/dev/dri/" #define VA_INTEL_PCI_DISPLAY_CONTROLLER_CLASS 0x03 @@ -187,7 +193,7 @@ static bool open_device_intel() { drmfd = open(nodes.path(i), O_RDWR); if (drmfd >= 0) { display = vaGetDisplayDRM(drmfd); - vaSetInfoCallback(display,nullptr,nullptr); + vaSetInfoCallback(display, nullptr, nullptr); if (display) return true; close(drmfd); @@ -206,7 +212,7 @@ static bool open_device_generic() { drmfd = open(device_paths[i], O_RDWR); if (drmfd >= 0) { display = vaGetDisplayDRM(drmfd); - vaSetInfoCallback(display,nullptr,nullptr); + vaSetInfoCallback(display, nullptr, nullptr); if (display) return true; close(drmfd); @@ -221,7 +227,6 @@ bool open_display() { drmfd = -1; display = 0; - if (open_device_intel() || open_device_generic()) { int majorVersion = 0, minorVersion = 0; if (vaInitialize(display, &majorVersion, &minorVersion) == VA_STATUS_SUCCESS) { @@ -271,13 +276,15 @@ void check_if_YUV420_available() { throw std::runtime_error("Desired YUV420 RT format not found"); } -void init_va() { +void init() { if (!va::open_display()) throw std::runtime_error("Failed to open VA display for CL-VA interoperability"); va::check_if_YUV420_available(); cv::va_intel::ocl::initializeContextFromVA(va::display, true); + + va::context = cv::ocl::OpenCLExecutionContext::getCurrent(); } std::string get_info() { @@ -285,16 +292,28 @@ std::string get_info() { ss << VA_VERSION_S << " (" << vaQueryVendorString(display) << ")"; return ss.str(); } + +void bind() { + context.bind(); +} } // namespace va namespace x11 { -Display* xdisplay; +Display *xdisplay; Window xroot; Window xwin; Atom wmDeleteMessage; bool initialized = false; +std::pair get_window_size() { + std::pair ret; + int x, y; + unsigned int border, depth; + XGetGeometry(xdisplay, xwin, &xroot, &x, &y, &ret.first, &ret.second, &border, &depth); + return ret; +} + Display* get_x11_display() { return xdisplay; } @@ -308,27 +327,25 @@ bool is_initialized() { } bool window_closed() { - if(XPending(xdisplay) == 0) + if (XPending(xdisplay) == 0) return false; XEvent event; XNextEvent(xdisplay, &event); - switch (event.type) - { - case ClientMessage: - if (event.xclient.data.l[0] == static_cast(wmDeleteMessage)) - return true; - break; + switch (event.type) { + case ClientMessage: + if (event.xclient.data.l[0] == static_cast(wmDeleteMessage)) + return true; + break; - default: - break; + default: + break; } return false; } - -void init_x11() { +void init() { xdisplay = XOpenDisplay(nullptr); if (xdisplay == nullptr) { cerr << "Unable to open X11 display" << endl; @@ -346,24 +363,55 @@ void init_x11() { XChangeWindowAttributes(xdisplay, xwin, CWOverrideRedirect, &xattr); int one = 1; - XChangeProperty( - xdisplay, xwin, - XInternAtom ( xdisplay, "_HILDON_NON_COMPOSITED_WINDOW", False ), - XA_INTEGER, 32, PropModeReplace, - (unsigned char*) &one, 1); + XChangeProperty(xdisplay, xwin, XInternAtom(xdisplay, "_HILDON_NON_COMPOSITED_WINDOW", False), + XA_INTEGER, 32, PropModeReplace, (unsigned char*) &one, 1); - XWMHints hints; - hints.input = True; - hints.flags = InputHint; - XSetWMHints(xdisplay, xwin, &hints); + XWMHints hints; + hints.input = True; + hints.flags = InputHint; + XSetWMHints(xdisplay, xwin, &hints); XMapWindow(xdisplay, xwin); - XStoreName(xdisplay, xwin, "tetra-demo"); + XStoreName(xdisplay, xwin, "nanovg-demo"); wmDeleteMessage = XInternAtom(xdisplay, "WM_DELETE_WINDOW", False); XSetWMProtocols(xdisplay, xwin, &wmDeleteMessage, 1); + XSelectInput(xdisplay, xwin, ButtonPressMask | Button1MotionMask ); initialized = true; } + +enum EventState { + NONE, + PRESS, + RELEASE, + MOTION +}; + +std::pair consume_event() { + XEvent event; + EventState state; + int revert_to; + XGetInputFocus(xdisplay, &xwin, &revert_to); + int x = 0, y = 0; + if (XEventsQueued(xdisplay, QueuedAlready) > 0) { + XNextEvent(xdisplay, &event); + switch (event.type) { + + case ButtonPress: + x = event.xmotion.x; + y = event.xmotion.y; + state = PRESS; + break; + case MotionNotify: + x = event.xmotion.x; + y = event.xmotion.y; + state = MOTION; + break; + } + } + + return {state,{x,y}}; +} } namespace egl { @@ -372,112 +420,112 @@ EGLDisplay display; EGLSurface surface; EGLContext context; -void debugMessageCallback(GLenum source, GLenum type, GLuint id, - GLenum severity, GLsizei length, - const GLchar *msg, const void *data) -{ +void debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *msg, const void *data) { std::string _source; std::string _type; std::string _severity; switch (source) { - case GL_DEBUG_SOURCE_API: + case GL_DEBUG_SOURCE_API: _source = "API"; break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: _source = "WINDOW SYSTEM"; break; - case GL_DEBUG_SOURCE_SHADER_COMPILER: + case GL_DEBUG_SOURCE_SHADER_COMPILER: _source = "SHADER COMPILER"; break; - case GL_DEBUG_SOURCE_THIRD_PARTY: + case GL_DEBUG_SOURCE_THIRD_PARTY: _source = "THIRD PARTY"; break; - case GL_DEBUG_SOURCE_APPLICATION: + case GL_DEBUG_SOURCE_APPLICATION: _source = "APPLICATION"; break; - case GL_DEBUG_SOURCE_OTHER: + case GL_DEBUG_SOURCE_OTHER: _source = "UNKNOWN"; break; - default: + default: _source = "UNKNOWN"; break; } switch (type) { - case GL_DEBUG_TYPE_ERROR: + case GL_DEBUG_TYPE_ERROR: _type = "ERROR"; break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: _type = "DEPRECATED BEHAVIOR"; break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: _type = "UDEFINED BEHAVIOR"; break; - case GL_DEBUG_TYPE_PORTABILITY: + case GL_DEBUG_TYPE_PORTABILITY: _type = "PORTABILITY"; break; - case GL_DEBUG_TYPE_PERFORMANCE: + case GL_DEBUG_TYPE_PERFORMANCE: _type = "PERFORMANCE"; break; - case GL_DEBUG_TYPE_OTHER: + case GL_DEBUG_TYPE_OTHER: _type = "OTHER"; break; - case GL_DEBUG_TYPE_MARKER: + case GL_DEBUG_TYPE_MARKER: _type = "MARKER"; break; - default: + default: _type = "UNKNOWN"; break; } switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: + case GL_DEBUG_SEVERITY_HIGH: _severity = "HIGH"; break; - case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_MEDIUM: _severity = "MEDIUM"; break; - case GL_DEBUG_SEVERITY_LOW: + case GL_DEBUG_SEVERITY_LOW: _severity = "LOW"; break; - case GL_DEBUG_SEVERITY_NOTIFICATION: + case GL_DEBUG_SEVERITY_NOTIFICATION: _severity = "NOTIFICATION"; break; - default: + default: _severity = "UNKNOWN"; break; } - fprintf(stderr, "%d: %s of %s severity, raised from %s: %s\n", - id, _type.c_str(), _severity.c_str(), _source.c_str(), msg); + fprintf(stderr, "%d: %s of %s severity, raised from %s: %s\n", id, _type.c_str(), _severity.c_str(), _source.c_str(), msg); - if(type == GL_DEBUG_TYPE_ERROR) + if (type == GL_DEBUG_TYPE_ERROR) exit(2); } -void init_egl(bool debug = false) { +EGLBoolean swap_buffers() { + return eglCheck(eglSwapBuffers(display, surface)); +} + +void init(bool debug = false) { bool offscreen = !x11::is_initialized(); eglCheck(eglBindAPI(EGL_OPENGL_API)); - if(offscreen) { + if (offscreen) { eglCheck(display = eglGetDisplay(EGL_DEFAULT_DISPLAY)); } else { eglCheck(display = eglGetDisplay(x11::get_x11_display())); @@ -485,52 +533,57 @@ void init_egl(bool debug = false) { eglCheck(eglInitialize(display, nullptr, nullptr)); const EGLint egl_config_constraints[] = { - EGL_STENCIL_SIZE, static_cast(0), - EGL_SAMPLE_BUFFERS, - EGL_FALSE, - EGL_SAMPLES, 0, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT | EGL_PBUFFER_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_BIT, - EGL_NONE - }; + EGL_STENCIL_SIZE, static_cast(8), + EGL_DEPTH_SIZE, static_cast(16), + EGL_BUFFER_SIZE, static_cast(32), + EGL_RED_SIZE, static_cast(8), + EGL_GREEN_SIZE, static_cast(8), + EGL_BLUE_SIZE, static_cast(8), + EGL_ALPHA_SIZE, static_cast(8), + EGL_SAMPLE_BUFFERS, EGL_TRUE, + EGL_SAMPLES, 16, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_NONE }; EGLint configCount; EGLConfig configs[1]; eglCheck(eglChooseConfig(display, egl_config_constraints, configs, 1, &configCount)); - if(!offscreen) { + EGLint stencilSize; + eglGetConfigAttrib(display, configs[0], + EGL_STENCIL_SIZE, &stencilSize); + + if (!offscreen) { eglCheck(surface = eglCreateWindowSurface(display, configs[0], x11::get_x11_window(), nullptr)); } else { EGLint pbuffer_attrib_list[] = { - EGL_WIDTH, WIDTH, - EGL_HEIGHT, HEIGHT, - EGL_NONE - }; + EGL_WIDTH, WIDTH, + EGL_HEIGHT, HEIGHT, + EGL_NONE }; eglCheck(surface = eglCreatePbufferSurface(display, configs[0], pbuffer_attrib_list)); } const EGLint contextVersion[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_CONTEXT_OPENGL_DEBUG, debug ? EGL_TRUE : EGL_FALSE, - EGL_NONE - }; + EGL_CONTEXT_MAJOR_VERSION, 4, + EGL_CONTEXT_MINOR_VERSION, 6, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_CONTEXT_OPENGL_DEBUG, debug ? EGL_TRUE : EGL_FALSE, + EGL_NONE }; eglCheck(context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextVersion)); eglCheck(eglMakeCurrent(display, surface, surface, context)); eglCheck(eglSwapInterval(display, 1)); - if(debug) { + + if (debug) { glCheck(glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)); - auto glDebugMessageCallback = (void (*)(void *, void *))eglGetProcAddress("glDebugMessageCallback"); + auto glDebugMessageCallback = (void (*)(void*, void*)) eglGetProcAddress("glDebugMessageCallback"); assert(glDebugMessageCallback); glCheck(glDebugMessageCallback(reinterpret_cast(debugMessageCallback), nullptr)); } } -EGLBoolean swapBuffers() { - return eglCheck(eglSwapBuffers(display, surface)); -} - std::string get_info() { return eglQueryString(display, EGL_VERSION); } @@ -540,52 +593,57 @@ namespace gl { //code in the kb::gl namespace deals with OpenGL (and OpenCV/GL) internals cv::ogl::Texture2D *frame_buf_tex; GLuint frame_buf; +cv::ocl::OpenCLExecutionContext context; +bool initialized = false; +void bind() { + context.bind(); +} -void init_gl() { +void init() { + glewExperimental = true; glewInit(); cv::ogl::ocl::initializeContextFromGL(); frame_buf = 0; glCheck(glGenFramebuffers(1, &frame_buf)); - glCheck(glBindFramebuffer(GL_FRAMEBUFFER, frame_buf)); + glCheck(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_buf)); + + GLuint sb; + glGenRenderbuffers(1, &sb); + glBindRenderbuffer(GL_RENDERBUFFER, sb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT); + + glCheck(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, sb)); + frame_buf_tex = new cv::ogl::Texture2D(cv::Size(WIDTH, HEIGHT), cv::ogl::Texture2D::RGBA, false); frame_buf_tex->bind(); - glCheck(glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, frame_buf_tex->texId(), 0)); + glCheck(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frame_buf_tex->texId(), 0)); assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - glCheck(glViewport(0, 0, WIDTH, HEIGHT)); - glCheck(glColor3f(1.0, 1.0, 1.0)); - - glCheck(glEnable(GL_CULL_FACE)); - glCheck(glCullFace(GL_BACK)); - - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glLoadIdentity()); - glCheck(glFrustum(-2, 2, -1.5, 1.5, 1, 40)); + gl::context = cv::ocl::OpenCLExecutionContext::getCurrent(); + initialized = true; +} - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glLoadIdentity()); - glCheck(glTranslatef(0, 0, -3)); - glCheck(glRotatef(50, 1, 0, 0)); - glCheck(glRotatef(70, 0, 1, 0)); +bool is_initialized() { + return initialized; } -void swapBuffers() { - kb::egl::swapBuffers(); +void swap_buffers() { + kb::egl::swap_buffers(); } std::string get_info() { return reinterpret_cast(glGetString(GL_VERSION)); } -void acquire_frame_buffer(cv::UMat &m) { +void acquire_from_gl(cv::UMat &m) { glCheck(cv::ogl::convertFromGLTexture2D(*gl::frame_buf_tex, m)); } -void release_frame_buffer(cv::UMat &m) { +void release_to_gl(cv::UMat &m) { glCheck(cv::ogl::convertToGLTexture2D(m, *gl::frame_buf_tex)); } @@ -594,6 +652,7 @@ void blit_frame_buffer_to_screen() { glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } } // namespace gl @@ -603,17 +662,85 @@ std::string get_info() { std::stringstream ss; std::vector plt_info; cv::ocl::getPlatfomsInfo(plt_info); - const cv::ocl::Device& device = cv::ocl::Device::getDefault(); + const cv::ocl::Device &device = cv::ocl::Device::getDefault(); for (const auto &info : plt_info) { ss << "\t* " << info.version() << " = " << info.name() << endl; } ss << "\t GL sharing: " << (device.isExtensionSupported("cl_khr_gl_sharing") ? "true" : "false") << endl; - ss << "\t GL MSAA sharing: " << (device.isExtensionSupported("cl_khr_gl_msaa_sharing") ? "true" : "false") << endl; - ss << "\t VAAPI media sharing: " << (device.isExtensionSupported("cl_intel_va_api_media_sharing") ? "true" : "false") << endl; + ss << "\t GL MSAA sharing: " << (device.isExtensionSupported("cl_khr_gl_msaa_sharing") ? "true" : "false") << endl; + ss << "\t VAAPI media sharing: " << (device.isExtensionSupported("cl_intel_va_api_media_sharing") ? "true" : "false") << endl; return ss.str(); } +} //namespace cl + +namespace nvg { +NVGcontext *vg; + +void clear() { + glCheck(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + glCheck(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); +} + +void push() { + glCheck(glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS)); + glCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPushMatrix()); +} + +void pop() { + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPopMatrix()); + glCheck(glPopClientAttrib()); + glCheck(glPopAttrib()); +} + +void begin(int w, int h, double pxRatio = 1) { + push(); + glCheck(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, kb::gl::frame_buf)); + nvgSave(vg); + nvgBeginFrame(vg, w, h, pxRatio); +} + +void end() { + nvgEndFrame(vg); + nvgRestore(vg); + pop(); + glCheck(glFlush()); + glCheck(glFinish()); +} + +void init(bool debug = false) { + push(); + + glCheck(glViewport(0, 0, WIDTH, HEIGHT)); + glCheck(glEnable(GL_STENCIL_TEST)); + glCheck(glStencilMask(~0)); + glCheck(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + glCheck(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); + + vg = nvgCreateGL3(NVG_STENCIL_STROKES | debug ? NVG_DEBUG : 0); + if (vg == NULL) { + cerr << "Couldn't init nanovg." << endl; + exit(24); + } + + nvgCreateFont(vg, "icons", "fonts/entypo.ttf"); + nvgCreateFont(vg, "sans-bold", "fonts/DejaVuSans-Bold.ttf"); + nvgCreateFont(vg, "sans", "fonts/DejaVuSans.ttf"); + + pop(); } +} //namespace nvg } -#endif /* SRC_TETRA_SUBSYSTEMS_HPP_ */ +#endif /* SRC_SUBSYSTEMS_HPP_ */ diff --git a/src/tetra/tetra-demo.cpp b/src/tetra/tetra-demo.cpp index 697529360..3bd119cc7 100644 --- a/src/tetra/tetra-demo.cpp +++ b/src/tetra/tetra-demo.cpp @@ -3,7 +3,7 @@ constexpr long unsigned int WIDTH = 1920; constexpr long unsigned int HEIGHT = 1080; constexpr double FPS = 60; -constexpr bool OFFSCREEN = true; +constexpr bool OFFSCREEN = false; constexpr const char* OUTPUT_FILENAME = "tetra-demo.mkv"; constexpr const int VA_HW_DEVICE_INDEX = 0; @@ -12,8 +12,23 @@ constexpr const int VA_HW_DEVICE_INDEX = 0; using std::cerr; using std::endl; -cv::ocl::OpenCLExecutionContext VA_CONTEXT; -cv::ocl::OpenCLExecutionContext GL_CONTEXT; +void init_render() { + glViewport(0, 0, WIDTH, HEIGHT); + glColor3f(1.0, 1.0, 1.0); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-2, 2, -1.5, 1.5, 1, 40); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -3); + glRotatef(50, 1, 0, 0); + glRotatef(70, 0, 1, 0); +} void render() { //Render a tetrahedron using immediate mode because the code is more concise for a demo @@ -50,7 +65,7 @@ void render() { glFlush(); } -void glow(cv::UMat &src, int ksize = WIDTH / 85 % 2 == 0 ? WIDTH / 85 + 1 : WIDTH / 85) { +void glow_effect(cv::UMat &src, int ksize = WIDTH / 85 % 2 == 0 ? WIDTH / 85 + 1 : WIDTH / 85) { static cv::UMat resize; static cv::UMat blur; static cv::UMat src16; @@ -75,12 +90,7 @@ void glow(cv::UMat &src, int ksize = WIDTH / 85 % 2 == 0 ? WIDTH / 85 + 1 : WID int main(int argc, char **argv) { using namespace kb; //Initialize OpenCL Context for VAAPI - va::init_va(); - /* - * The OpenCLExecutionContext for VAAPI needs to be copied right after init_va(). - * Now everytime you want to do VAAPI interop first bind the context. - */ - VA_CONTEXT = cv::ocl::OpenCLExecutionContext::getCurrent(); + va::init(); //Initialize VP9 HW encoding using VAAPI cv::VideoWriter encoder(OUTPUT_FILENAME, cv::CAP_FFMPEG, cv::VideoWriter::fourcc('V', 'P', '9', '0'), FPS, cv::Size(WIDTH, HEIGHT), { @@ -91,17 +101,12 @@ int main(int argc, char **argv) { //If we are rendering offscreen we don't need x11 if(!OFFSCREEN) - x11::init_x11(); + x11::init(); //Passing true to init_egl will create a OpenGL debug context - egl::init_egl(); + egl::init(); //Initialize OpenCL Context for OpenGL - gl::init_gl(); - /* - * The OpenCLExecutionContext for OpenGL needs to be copied right after init_gl(). - * Now everytime you want to do OpenGL interop first bind the context. - */ - GL_CONTEXT = cv::ocl::OpenCLExecutionContext::getCurrent(); + gl::init(); cerr << "VA Version: " << va::get_info() << endl; cerr << "EGL Version: " << egl::get_info() << endl; @@ -116,31 +121,33 @@ int main(int argc, char **argv) { double tickFreq = cv::getTickFrequency(); double lastFps = FPS; + init_render(); + while (true) { //Activate the OpenCL context for OpenGL - GL_CONTEXT.bind(); + gl::bind(); //Render using OpenGL render(); //Aquire the frame buffer for use by OpenCL - gl::acquire_frame_buffer(frameBuffer); + gl::acquire_from_gl(frameBuffer); //Glow effect (OpenCL) - glow(frameBuffer); + glow_effect(frameBuffer); //Color-conversion from BGRA to RGB. OpenCV/OpenCL. cv::cvtColor(frameBuffer, videoFrame, cv::COLOR_BGRA2RGB); //Video frame is upside down -> flip it (OpenCL) cv::flip(videoFrame, videoFrame, 0); //Activate the OpenCL context for VAAPI - VA_CONTEXT.bind(); + va::bind(); //Encode the frame using VAAPI on the GPU. encoder.write(videoFrame); if(x11::is_initialized()) { //Yet again activate the OpenCL context for OpenGL - GL_CONTEXT.bind(); + gl::bind(); //Release the frame buffer for use by OpenGL - gl::release_frame_buffer(frameBuffer); + gl::release_to_gl(frameBuffer); //Blit the framebuffer we have been working on to the screen gl::blit_frame_buffer_to_screen(); @@ -149,7 +156,7 @@ int main(int argc, char **argv) { break; //Transfer the back buffer (which we have been using as frame buffer) to the native window - gl::swapBuffers(); + gl::swap_buffers(); } //Measure FPS