Hi, I managed to simulate a decoded image through
NvBufferCreateEx
as you suggested. When the EGL context is created with
egl_surface = eglCreatePbufferSurface(egl_display, egl_config, pbuffer_attrib_list);
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attrib_list);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
the call
glEGLImageTargetTexture2DOES
works.
When it’s created with
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attrib_list);
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context);
then
glEGLImageTargetTexture2DOES
does nothing, but all the rest work.
The problem with this is that
-
I don’t think I should need a pbuffer surface (and I’ve read that there are limitations to pbuffer that there are not present in frame buffer)
-
I cannot integrate this code with GTK, because somehow, GTK automatically creates an Open GL context when the signal_render signal is called and I don’t think I can turn it off. This automatic Open GL context seems to override the egl_surface or something related to it, because glEGLImageTargetTexture2DOESwill not work when called from GTK code, even if I try to call eglMakeCurrent inside every render, and even if I use pBuffer surface at the EGL creation
#include <epoxy/gl.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdio.h>
#include <stdlib.h>
#include <nvbuf_utils.h>
#include <string>
#include <stdexcept>
#include <iostream>
#include <sstream>
const char *vertex_shader = R"glsl(
#version 100
attribute vec2 position;
attribute vec2 tex_coord;
varying vec2 tex_coord_var;
uniform mat4 transform_matrix;
void main()
{
tex_coord_var = tex_coord;
gl_Position = vec4(position, 0.0, 1.0);
}
)glsl";
const char *fragment_shader = R"glsl(
#version 100
precision mediump float;
varying vec2 tex_coord_var;
uniform sampler2D tex;
void main()
{
vec4 tex_color = texture2D(tex, tex_coord_var);
gl_FragColor = vec4(tex_color.r, tex_color.g, tex_color.b, 1.0);
}
)glsl";
static void assertOpenGLError(const std::string &msg)
{
GLenum error = glGetError();
if (error != GL_NO_ERROR)
{
std::stringstream s;
s << "OpenGL error 0x" << std::hex << error << " at " << msg;
throw std::runtime_error(s.str());
}
}
int main()
{
int width = 64;
int height = 64;
unsigned char *input_image_data = (unsigned char *)malloc(width * height * 4 * sizeof(char));
unsigned char *frameBufferData = (unsigned char *)malloc(width * height * 4 * sizeof(char));
unsigned char *test = (unsigned char *)malloc(width * height * 4 * sizeof(char));
unsigned char *t2 = (unsigned char *)malloc(width * height * 4 * sizeof(char));
unsigned char *t3 = (unsigned char *)malloc(width * height * 4 * sizeof(char));
/* 4 color checkers */
for (int i = 0; i < width * height * 4; i++)
{
input_image_data[i] = 1;
}
GLuint tex[2];
GLuint fbo, vbo, ebo, v_shader, f_shader, shader_program;
GLuint externalTexture;
GLuint frameBufferTexture;
EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;
/* EGL context creation */
EGLConfig egl_config;
EGLint matching_configs;
const EGLint config_attrib_list[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE};
const EGLint pbuffer_attrib_list[] = {
EGL_WIDTH,
width,
EGL_HEIGHT,
height,
EGL_NONE,
};
const EGLint context_attrib_list[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};
egl_display = eglGetDisplay((EGLNativeDisplayType)0);
eglInitialize(egl_display, 0, 0);
eglBindAPI(EGL_OPENGL_ES_API);
eglChooseConfig(egl_display, config_attrib_list, &egl_config, 1, &matching_configs);
#define USE_SURFACE
#if defined USE_SURFACE
printf("using surface, glEGLImageTargetTexture2DOES will work!\n");
egl_surface = eglCreatePbufferSurface(egl_display, egl_config, pbuffer_attrib_list);
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attrib_list);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
#else
printf("NOT using surface, glEGLImageTargetTexture2DOES will NOT work\n");
//egl_surface = eglCreatePbufferSurface (egl_display, egl_config, pbuffer_attrib_list);
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attrib_list);
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context);
#endif
glGenTextures(1, &externalTexture);
glGenTextures(1, &frameBufferTexture);
shader_program = glCreateProgram();
v_shader = glCreateShader(GL_VERTEX_SHADER);
f_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v_shader, 1, &vertex_shader, NULL);
glShaderSource(f_shader, 1, &fragment_shader, NULL);
glCompileShader(v_shader);
glCompileShader(f_shader);
glAttachShader(shader_program, v_shader);
glAttachShader(shader_program, f_shader);
GLuint vertex_position, texture_position;
GLint transform_matrix;
const GLfloat vertices_textures[20] = {
//vertices //positions
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f};
//Fill with data
for (int i = 0; i < width * height * 4; ++i)
{
t2[i] = 112;
}
for (int i = 0; i < width * height * 4; ++i)
{
t3[i] = 50;
}
glLinkProgram(shader_program);
glUseProgram(shader_program);
unsigned int vertexBufferObject, vertexArrayObject, TBO, EBO;
GLint vextexInLocation;
GLint textureInLocation;
GLuint vertexbuffer;
GLuint frameBuffer;
vextexInLocation = glGetAttribLocation(shader_program, "position");
textureInLocation = glGetAttribLocation(shader_program, "tex_coord");
glGenVertexArrays(1, &vertexArrayObject);
glGenBuffers(1, &vertexBufferObject);
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_textures), vertices_textures, GL_STATIC_DRAW);
glVertexAttribPointer(vextexInLocation, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)0);
glEnableVertexAttribArray(vextexInLocation);
glVertexAttribPointer(textureInLocation, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(textureInLocation);
int texLocation = glGetUniformLocation(shader_program, "tex");
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glGenTextures(1, &externalTexture);
glGenTextures(1, &frameBufferTexture);
glBindTexture(GL_TEXTURE_2D, frameBufferTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, t2);
glUniform1i(texLocation, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frameBufferTexture, 0);
int dmabuf_fd;
EGLImageKHR egl_image;
char *virtual_addr;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC EGLImageTargetTexture2DOES;
//----------------------Simulates creation of nv egl image decoded from hardware
NvBufferCreateParams input_params;
input_params.payloadType = NvBufferPayload_SurfArray;
input_params.width = width;
input_params.height = height;
input_params.layout = NvBufferLayout_Pitch;
input_params.colorFormat = NvBufferColorFormat_ABGR32;
input_params.nvbuf_tag = NvBufferTag_VIDEO_DEC;
int ret = NvBufferCreateEx (&dmabuf_fd, &input_params);
if (ret<0)
printf("NvBufferCreateEx error!\n");
//NvBufferCreate(&dmabuf_fd, width, height, NvBufferLayout_BlockLinear, NvBufferColorFormat_ABGR32);
NvBufferMemMap(dmabuf_fd, 0, NvBufferMem_Read_Write, (void **)&virtual_addr);
NvBufferMemSyncForCpu(dmabuf_fd, 0, (void **)&virtual_addr);
for (int i = 0; i < width * height * 4; ++i)
{
virtual_addr[i] = input_image_data[i];
}
NvBufferMemSyncForDevice(dmabuf_fd, 0, (void **)&virtual_addr);
egl_image = NvEGLImageFromFd(egl_display, dmabuf_fd);
EGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
/*
NvBuffer2Raw(dmabuf_fd, 0, width, height, test);
for (int i = 0; i < 150; ++i)
{
printf("%i ", test[i]);
}
printf("\n\n");
*/
//--------------------------------------------------------------------------------
EGLImageKHR hEglImage;
hEglImage = NvEGLImageFromFd(egl_display, dmabuf_fd);
glBindTexture(GL_TEXTURE_2D, externalTexture);
//-----------------------------------------------------
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, t3);
//-----------------------------------------------------
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) hEglImage);
glBindVertexArray(vertexArrayObject);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, frameBufferData);
printf("frameBufferData:\n");
for (int i = 0; i < 200; ++i)
{
printf("%i ", frameBufferData[i]);
}
printf("\n");
glFinish();
NvDestroyEGLImage(egl_display, egl_image);
NvBufferMemUnMap(dmabuf_fd, 0, (void **)&virtual_addr);
NvBufferDestroy(dmabuf_fd);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(2, tex);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
glDeleteShader(v_shader);
glDeleteShader(f_shader);
glDeleteProgram(shader_program);
eglDestroyContext(egl_display, egl_context);
eglDestroySurface(egl_display, egl_surface);
eglTerminate(egl_display);
free(input_image_data);
return 0;
}
Please compile with and without
#define USE_SURFACE
to see the difference.
Compile with
gcc -o lab4 lab4.cpp -lEGL -lGLESv2 -lepoxy -lnvbuf_utils -lstdc++ -L/usr/lib/aarch64-linux-gnu/tegra/ -I/usr/src/nvidia/tegra_multimedia_api/include/
(do sudo apt install -y libepoxy-dev if needed)