initial files of the OpenGL to OpenCL to VAAPI interop demo

pull/3471/head
kallaballa 3 years ago
parent 0cc99b1309
commit 6e105f15ac
  1. 9
      .gitignore
  2. 49
      Makefile
  3. 19
      debug-env.sh
  4. 46
      src/tetra/Makefile
  5. 517
      src/tetra/tetra-demo.cpp

9
.gitignore vendored

@ -0,0 +1,9 @@
.cproject
.project
.settings/
src/*.o
src/*.dep
src/*.d
src/tetra/tetra-demo

@ -0,0 +1,49 @@
CXX := g++
CXXFLAGS := -I/usr/local/include/ -std=c++20 -pthread -fno-strict-aliasing -pedantic -Wall -march=native -flto
LDFLAGS := -L/usr/local/lib64/ -L/opt/local/lib -flto
.PHONY: all release debian-release info debug asan clean debian-clean distclean
DESTDIR := /
PREFIX := /usr/local
all: release
release: CXXFLAGS += -g0 -O3
release: dirs
info: CXXFLAGS += -g3 -O0
info: dirs
debug: CXXFLAGS += -g3 -O0 -rdynamic
debug: dirs
profile: CXXFLAGS += -g3 -O1
profile: dirs
unsafe: CXXFLAGS += -g0 -Ofast -DNDEBUG -ffast-math -ftree-vectorizer-verbose=1 -funroll-loops -ftree-vectorize -fno-signed-zeros -fno-trapping-math -frename-registers
unsafe: dirs
asan: CXXFLAGS += -g3 -O0 -fno-omit-frame-pointer -fsanitize=address
asan: LDFLAGS += -fsanitize=address
asan: LIBS+= -lbfd -ldw
asan: dirs
clean: dirs
export LDFLAGS
export CXXFLAGS
dirs:
${MAKE} -C src/tetra/ ${MAKEFLAGS} CXX=${CXX} ${MAKECMDGOALS}
debian-release:
${MAKE} -C src/tetra/ ${MAKEFLAGS} CXX=${CXX} release
debian-clean:
${MAKE} -C src/tetra/ ${MAKEFLAGS} CXX=${CXX} clean
install: ${TARGET}
true
distclean:
true

@ -0,0 +1,19 @@
export OPENCV_LOG_LEVEL=DEBUG
export OPENCV_FFMPEG_LOGLEVEL=trace
export OPENCV_VIDEOIO_DEBUG=1
export OPENCV_VIDEOWRITER_DEBUG=1
export OPENCV_FFMPEG_DEBUG=1
export OPENCV_OPENCL_RAISE_ERROR=1
export OPENCV_OPENCL_ABORT_ON_BUILD_ERROR=1
#export OPENCV_OPENCL_ENABLE_MEM_USE_HOST_PTR=1
#export OPENCV_OPENCL_ALIGNMENT_MEM_USE_HOST_PTR=1
#export OPENCV_OPENCL_RUNTIME=
#export OPENCV_OPENCL_DEVICE=
#export OPENCV_OPENCL_SVM_DISABLE=1
export OPENCV_DUMP_ERRORS=1
export OPENCV_DUMP_CONFIG=1
#export OPENCV_CPU_DISABLE=1
export OPENCV_TRACE=1
export OPENCV_TRACE_SYNC_OPENCL=1
#export OPENCV_FFMPEG_CAPTURE_OPTIONS=
#export OPENCV_FFMPEG_WRITER_OPTIONS=

@ -0,0 +1,46 @@
TARGET := tetra-demo
SRCS := tetra-demo.cpp
#precompiled headers
HEADERS :=
OBJS := ${SRCS:.cpp=.o}
DEPS := ${SRCS:.cpp=.dep}
CXXFLAGS += -fpic `pkg-config --cflags egl opencv4 libva-drm glew`
LDFLAGS +=
LIBS += -lm `pkg-config --libs egl opencv4 libva-drm glew`
.PHONY: all release debug clean distclean
all: release
release: ${TARGET}
debug: ${TARGET}
info: ${TARGET}
profile: ${TARGET}
hardcore: ${TARGET}
asan: ${TARGET}
${TARGET}: ${OBJS}
${CXX} ${LDFLAGS} -o $@ $^ ${LIBS}
${OBJS}: %.o: %.cpp %.dep ${GCH}
${CXX} ${CXXFLAGS} -o $@ -c $<
${DEPS}: %.dep: %.cpp Makefile
${CXX} ${CXXFLAGS} -MM $< > $@
${GCH}: %.gch: ${HEADERS}
${CXX} ${CXXFLAGS} -o $@ -c ${@:.gch=.hpp}
install:
mkdir -p ${DESTDIR}/${PREFIX}
cp ${TARGET} ${DESTDIR}/${PREFIX}
uninstall:
rm ${DESTDIR}/${PREFIX}/${TARGET}
clean:
rm -f *~ ${DEPS} ${OBJS} ${CUO} ${GCH} ${TARGET}
distclean: clean

@ -0,0 +1,517 @@
#define CL_TARGET_OPENCL_VERSION 300
#include <iostream>
#include <filesystem>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <va/va.h>
#include <va/va_drm.h>
#include <opencv2/opencv.hpp>
#include "opencv2/core/va_intel.hpp"
#include <opencv2/videoio.hpp>
#include <GL/glew.h>
#include <GL/gl.h>
#include <EGL/egl.h>
#include <CL/cl.h>
#include <CL/cl_gl.h>
#include <opencv2/core/ocl.hpp>
#include <opencv2/core/opengl.hpp>
constexpr off_t WIDTH = 3840;
constexpr off_t HEIGHT = 2160;
constexpr double FPS = 30;
using std::cout;
using std::cerr;
using std::endl;
namespace kb {
namespace va {
//code in the kb::va namespace adapted from https://github.com/opencv/opencv/blob/4.x/samples/va_intel/display.cpp.inc
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
static unsigned read_id(const char *devName, const char *idName);
static int find_adapter(unsigned desiredVendorId);
int drmfd = -1;
class Directory
{
typedef int (*fsort)(const struct dirent**, const struct dirent**);
public:
Directory(const char *path)
{
dirEntries_ = 0;
numEntries_ = scandir(path, &dirEntries_, filterFunc, (fsort) alphasort);
}
~Directory()
{
if (numEntries_ && dirEntries_)
{
for (int i = 0; i < numEntries_; ++i)
free(dirEntries_[i]);
free(dirEntries_);
}
}
int count() const
{
return numEntries_;
}
const struct dirent* operator[](int index) const
{
return ((dirEntries_ != 0) && (index >= 0) && (index < numEntries_)) ? dirEntries_[index] : 0;
}
protected:
static int filterFunc(const struct dirent *dir)
{
if (!dir)
return 0;
if (!strcmp(dir->d_name, "."))
return 0;
if (!strcmp(dir->d_name, ".."))
return 0;
return 1;
}
private:
int numEntries_;
struct dirent **dirEntries_;
};
static unsigned read_id(const char *devName, const char *idName)
{
long int id = 0;
std::string fileName = cv::format("%s/%s/%s", VA_INTEL_PCI_DIR, devName, idName);
FILE *file = fopen(fileName.c_str(), "r");
if (file)
{
char str[16] = "";
if (fgets(str, sizeof(str), file))
id = strtol(str, NULL, 16);
fclose(file);
}
return (unsigned) id;
}
static int find_adapter(unsigned desiredVendorId)
{
int adapterIndex = -1;
Directory dir(VA_INTEL_PCI_DIR);
for (int i = 0; i < dir.count(); ++i)
{
const char *name = dir[i]->d_name;
unsigned classId = read_id(name, "class");
if ((classId >> 16) == VA_INTEL_PCI_DISPLAY_CONTROLLER_CLASS)
{
unsigned vendorId = read_id(name, "vendor");
if (vendorId == desiredVendorId)
{
std::string subdirName = cv::format("%s/%s/%s", VA_INTEL_PCI_DIR, name, "drm");
Directory subdir(subdirName.c_str());
for (int j = 0; j < subdir.count(); ++j)
{
if (!strncmp(subdir[j]->d_name, "card", 4))
{
adapterIndex = strtoul(subdir[j]->d_name + 4, NULL, 10);
}
}
break;
}
}
}
return adapterIndex;
}
class NodeInfo
{
enum {
NUM_NODES = 2
};
public:
NodeInfo(int adapterIndex)
{
const char *names[NUM_NODES] = { "renderD", "card" };
int numbers[NUM_NODES];
numbers[0] = adapterIndex + 128;
numbers[1] = adapterIndex;
for (int i = 0; i < NUM_NODES; ++i)
{
paths_[i] = cv::format("%s%s%d", VA_INTEL_DRI_DIR, names[i], numbers[i]);
}
}
~NodeInfo()
{
// nothing
}
int count() const
{
return NUM_NODES;
}
const char* path(int index) const
{
return ((index >= 0) && (index < NUM_NODES)) ? paths_[index].c_str() : 0;
}
private:
std::string paths_[NUM_NODES];
};
static bool open_device_intel();
static bool open_device_generic();
static bool open_device_intel()
{
const unsigned IntelVendorID = 0x8086;
int adapterIndex = find_adapter(IntelVendorID);
if (adapterIndex >= 0)
{
NodeInfo nodes(adapterIndex);
for (int i = 0; i < nodes.count(); ++i)
{
drmfd = open(nodes.path(i), O_RDWR);
if (drmfd >= 0)
{
display = vaGetDisplayDRM(drmfd);
if (display)
return true;
close(drmfd);
drmfd = -1;
}
}
}
return false;
}
static bool open_device_generic()
{
static const char *device_paths[] = { "/dev/dri/renderD128", "/dev/dri/card0" };
static const int num_devices = sizeof(device_paths) / sizeof(device_paths[0]);
for (int i = 0; i < num_devices; ++i)
{
drmfd = open(device_paths[i], O_RDWR);
if (drmfd >= 0)
{
display = vaGetDisplayDRM(drmfd);
if (display)
return true;
close(drmfd);
drmfd = -1;
}
}
return false;
}
bool open_display()
{
if (!initialized)
{
drmfd = -1;
display = 0;
if (open_device_intel() || open_device_generic())
{
int majorVersion = 0, minorVersion = 0;
if (vaInitialize(display, &majorVersion, &minorVersion) == VA_STATUS_SUCCESS)
{
initialized = true;
return true;
}
close(drmfd);
display = 0;
drmfd = -1;
}
return false; // Can't open VA display
}
return true;
}
void close_display()
{
if (initialized)
{
if (display)
vaTerminate(display);
if (drmfd >= 0)
close(drmfd);
display = 0;
drmfd = -1;
initialized = false;
}
}
void check_if_YUV420_available() {
VAEntrypoint entrypoints[5];
int num_entrypoints, vld_entrypoint;
VAConfigAttrib attrib;
VAStatus status;
status = vaQueryConfigEntrypoints(va::display, VAProfileVP9Profile0, entrypoints, &num_entrypoints);
assert(status == VA_STATUS_SUCCESS);
for (vld_entrypoint = 0; vld_entrypoint < num_entrypoints; ++vld_entrypoint)
{
if (entrypoints[vld_entrypoint] == VAEntrypointVLD)
break;
}
if (vld_entrypoint == num_entrypoints)
throw std::runtime_error("Failed to find VLD entry point");
attrib.type = VAConfigAttribRTFormat;
vaGetConfigAttributes(va::display, VAProfileVP9Profile0, VAEntrypointVLD, &attrib, 1);
if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
throw std::runtime_error("Desired YUV444 RT format not found");
}
} // namespace va
}
namespace kb {
namespace egl {
//code in the kb::egl namespace deals with setting up EGL
EGLDisplay display;
EGLSurface surface;
EGLContext context;
void eglCheckError(const std::filesystem::path &file, unsigned int line, const char *expression)
{
EGLint errorCode = eglGetError();
if (errorCode != EGL_SUCCESS) {
cerr << "EGL failed in " << file.filename() << " (" << line << ") : "
<< "\nExpression:\n " << expression << "\nError code:\n " << errorCode << "\n "
<< endl;
assert(false);
}
}
#define eglCheck(expr) \
expr; \
egl::eglCheckError(__FILE__, __LINE__, #expr);
void init_egl() {
eglCheck(eglBindAPI(EGL_OPENGL_API));
eglCheck(display = eglGetDisplay(EGL_DEFAULT_DISPLAY));
eglCheck(eglInitialize(display, nullptr, nullptr));
const EGLint attributes[] =
{ EGL_BUFFER_SIZE,
static_cast<EGLint>(24),
EGL_DEPTH_SIZE,
static_cast<EGLint>(24),
EGL_STENCIL_SIZE,
static_cast<EGLint>(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 };
EGLint configCount;
EGLConfig configs[1];
eglCheck(eglChooseConfig(display, attributes, configs, 1, &configCount));
EGLint attrib_list[] = { EGL_WIDTH, WIDTH, EGL_HEIGHT, HEIGHT, EGL_NONE };
eglCheck(surface = eglCreatePbufferSurface(display, configs[0], attrib_list));
const EGLint contextVersion[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_CONTEXT_OPENGL_DEBUG, EGL_FALSE, EGL_NONE };
eglCheck(context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, contextVersion));
eglCheck(eglMakeCurrent(display, surface, surface, context));
cerr << "EGL Version: " << eglQueryString(display, EGL_VERSION) << endl;
cerr << "OpenGL version: " << glGetString(GL_VERSION) << endl;
}
EGLBoolean swapBuffers() {
return eglSwapBuffers(display, surface);
}
}
}
namespace kb {
namespace gl {
//code in the kb::gl namespace deals with OpenGL (and OpenCV/GL) internals
cv::ogl::Texture2D *frame_buf_tex;
GLuint frame_buf_tex_name;
void glCheckError(const std::filesystem::path &file, unsigned int line, const char *expression)
{
GLint errorCode = glGetError();
if (errorCode != GL_NO_ERROR) {
cerr << "GL failed in " << file.filename() << " (" << line << ") : "
<< "\nExpression:\n " << expression << "\nError code:\n " << errorCode << "\n "
<< endl;
assert(false);
}
}
#define glCheck(expr) \
expr; \
gl::glCheckError(__FILE__, __LINE__, #expr);
void init_gl() {
glewInit();
cv::ogl::ocl::initializeContextFromGL();
frame_buf_tex_name = 0;
glCheck(glGenFramebuffers(1, &frame_buf_tex_name));
glCheck(glBindFramebuffer(GL_FRAMEBUFFER, frame_buf_tex_name));
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));
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glCheck(glDrawBuffers(1, drawBuffers));
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glCheck(glBindFramebuffer(GL_FRAMEBUFFER, frame_buf_tex_name));
glCheck(glClearColor(0.1, 0.39, 0.88, 1.0));
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));
glCheck(glMatrixMode(GL_MODELVIEW));
glCheck(glLoadIdentity());
glCheck(glTranslatef(0, 0, -3));
glCheck(glRotatef(50, 1, 0, 0));
glCheck(glRotatef(70, 0, 1, 0));
}
void swapBuffers() {
kb::egl::swapBuffers();
}
void fetch_frame_buffer(cv::UMat &m) {
cv::ogl::convertFromGLTexture2D(*frame_buf_tex, m);
assert(glGetError() == GL_NO_ERROR);
}
void return_frame_buffer(cv::UMat &m) {
cv::ogl::convertToGLTexture2D(m, *frame_buf_tex);
assert(glGetError() == GL_NO_ERROR);
}
}
}
int main(int argc, char **argv) {
using namespace kb;
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);
cv::ocl::OpenCLExecutionContext vaContext = cv::ocl::OpenCLExecutionContext::getCurrent();
cv::VideoWriter video("out.mkv", cv::CAP_FFMPEG, cv::VideoWriter::fourcc('V', 'P', '9', '0'), FPS, cv::Size(WIDTH, HEIGHT), {
cv::VIDEOWRITER_PROP_HW_DEVICE, 0,
cv::VIDEOWRITER_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_VAAPI,
cv::VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL, 1
});
egl::init_egl();
gl::init_gl();
cv::ocl::OpenCLExecutionContext glContext = cv::ocl::OpenCLExecutionContext::getCurrent();
cv::UMat frameBuffer(HEIGHT, WIDTH, CV_8UC4, cv::Scalar::all(0));
cv::UMat mask;
cv::UMat bgr;
while (true) {
int64 start = cv::getTickCount();
//draw a rotating tetrahedron
glContext.bind();
glCheck(glRotatef(1, 0, 1, 0));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
for (GLfloat i = -2.5; i <= 2.5; i += 0.25) {
glVertex3f(i, 0, 2.5);
glVertex3f(i, 0, -2.5);
glVertex3f(2.5, 0, i);
glVertex3f(-2.5, 0, i);
}
glEnd();
glBegin(GL_TRIANGLE_STRIP);
glColor3f(1, 1, 1);
glVertex3f(0, 2, 0);
glColor3f(1, 0, 0);
glVertex3f(-1, 0, 1);
glColor3f(0, 1, 0);
glVertex3f(1, 0, 1);
glColor3f(0, 0, 1);
glVertex3f(0, 0, -1.4);
glColor3f(1, 1, 1);
glVertex3f(0, 2, 0);
glColor3f(1, 0, 0);
glVertex3f(-1, 0, 1);
glEnd();
glFlush();
gl::swapBuffers();
gl::fetch_frame_buffer(frameBuffer); //hand over the data (GPU 2 GPU) to OpenCV/OpenCL
//Using OpenCL in the background
cv::flip(frameBuffer, frameBuffer, 0); // flip the image in the y-axis
//do a glow effect using blur
{
cv::blur(frameBuffer, mask, cv::Size(50,50));
cv::bitwise_not(mask, mask);
cv::bitwise_not(frameBuffer, frameBuffer);
mask.assignTo(mask, CV_16U);
frameBuffer.assignTo(frameBuffer, CV_16U);
multiply(mask,frameBuffer, mask);
cv::divide(mask, cv::Scalar::all(255.0), mask);
mask.assignTo(mask, CV_8U);
cv::bitwise_not(mask, frameBuffer);
}
cv::cvtColor(frameBuffer, bgr, cv::COLOR_BGRA2BGR); //convert to bgr
vaContext.bind();
//encode the frame using VAAPI on the GPU.
video.write(bgr);
int64 tick = cv::getTickCount();
if(tick % int64(FPS) == 0)
cerr << "FPS : " << cv::getTickFrequency() / (cv::getTickCount() - start) << '\r';
}
return 0;
}
Loading…
Cancel
Save