2019-10-06 13:10:46 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <GLES2/gl2.h>
|
|
|
|
#include <GLES2/gl2ext.h>
|
|
|
|
#include <EGL/egl.h>
|
|
|
|
#include <EGL/eglext.h>
|
|
|
|
|
2019-10-06 17:07:33 +00:00
|
|
|
#include "dmabuf.h"
|
|
|
|
|
2019-10-06 13:10:46 +00:00
|
|
|
#define MAYBE_UNUSED __attribute__((unused))
|
|
|
|
|
|
|
|
#ifdef NDEBUG
|
|
|
|
#define log_debug(...)
|
|
|
|
#else
|
|
|
|
#define log_debug(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define XSTR(s) STR(s)
|
|
|
|
#define STR(s) #s
|
|
|
|
|
|
|
|
#define X_GL_EARLY_EXTENSIONS \
|
|
|
|
X(PFNEGLGETPLATFORMDISPLAYEXTPROC, eglGetPlatformDisplayEXT) \
|
|
|
|
X(PFNEGLDEBUGMESSAGECONTROLKHRPROC, eglDebugMessageControlKHR) \
|
|
|
|
X(PFNGLDEBUGMESSAGECALLBACKKHRPROC, glDebugMessageCallbackKHR) \
|
|
|
|
|
|
|
|
#define X_GL_LATE_EXTENSIONS \
|
|
|
|
X(PFNEGLCREATEIMAGEKHRPROC, eglCreateImageKHR) \
|
|
|
|
X(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR) \
|
|
|
|
X(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC, glEGLImageTargetTexture2DOES) \
|
|
|
|
|
|
|
|
#define X_GL_EXTENSIONS \
|
|
|
|
X_GL_EARLY_EXTENSIONS \
|
|
|
|
X_GL_LATE_EXTENSIONS \
|
|
|
|
|
|
|
|
#define X(type, name) type name;
|
|
|
|
X_GL_EXTENSIONS
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
struct renderer {
|
|
|
|
EGLDisplay display;
|
|
|
|
EGLSurface surface;
|
|
|
|
EGLContext context;
|
|
|
|
GLuint shader_program;
|
2019-10-06 14:08:52 +00:00
|
|
|
uint32_t width;
|
|
|
|
uint32_t height;
|
|
|
|
GLint read_format;
|
|
|
|
GLint read_type;
|
|
|
|
};
|
|
|
|
|
2019-10-06 13:10:46 +00:00
|
|
|
static inline void* gl_load_single_extension(const char* name)
|
|
|
|
{
|
|
|
|
void* ext = eglGetProcAddress(name);
|
|
|
|
if (!ext)
|
|
|
|
log_debug("GL: Failed to load procedure: %s\n", name);
|
|
|
|
|
|
|
|
return ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gl_load_early_extensions(void)
|
|
|
|
{
|
|
|
|
#define X(type, name) \
|
|
|
|
name = gl_load_single_extension(XSTR(name)); \
|
|
|
|
if (!name) \
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
X_GL_EARLY_EXTENSIONS
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gl_load_late_extensions(void)
|
|
|
|
{
|
|
|
|
#define X(type, name) \
|
|
|
|
name = gl_load_single_extension(XSTR(name)); \
|
|
|
|
if (!name) \
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
X_GL_LATE_EXTENSIONS
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
MAYBE_UNUSED
|
|
|
|
static void egl_log(EGLenum error, const char* command, EGLint msg_type,
|
|
|
|
EGLLabelKHR thread, EGLLabelKHR obj, const char *msg)
|
|
|
|
{
|
|
|
|
(void)error;
|
|
|
|
(void)msg_type;
|
|
|
|
(void)thread;
|
|
|
|
(void)obj;
|
|
|
|
|
|
|
|
log_debug("EGL: %s: %s\n", command, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
MAYBE_UNUSED
|
|
|
|
static void gles2_log(GLenum src, GLenum type, GLuint id, GLenum severity,
|
|
|
|
GLsizei len, const GLchar *msg, const void *user)
|
|
|
|
{
|
|
|
|
(void)src;
|
|
|
|
(void)type;
|
|
|
|
(void)id;
|
|
|
|
(void)severity;
|
|
|
|
(void)len;
|
|
|
|
(void)user;
|
|
|
|
|
|
|
|
log_debug("GLES2: %s\n", msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gl_debug_init()
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
static const EGLAttrib debug_attribs[] = {
|
|
|
|
EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE,
|
|
|
|
EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE,
|
|
|
|
EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE,
|
|
|
|
EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE,
|
|
|
|
EGL_NONE,
|
|
|
|
};
|
|
|
|
eglDebugMessageControlKHR(egl_log, debug_attribs);
|
|
|
|
|
|
|
|
glEnable(GL_DEBUG_OUTPUT_KHR);
|
|
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
|
|
|
glDebugMessageCallbackKHR(gles2_log, NULL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gl_load_shader(GLuint* dst, const char* source, GLenum type)
|
|
|
|
{
|
|
|
|
GLuint shader = glCreateShader(type);
|
|
|
|
|
|
|
|
glShaderSource(shader, 1, &source, NULL);
|
|
|
|
glCompileShader(shader);
|
|
|
|
|
|
|
|
if (glGetError() != GL_NO_ERROR) {
|
|
|
|
glDeleteShader(shader);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst = shader;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gl_compile_shader_program(GLuint* dst)
|
|
|
|
{
|
|
|
|
static const char vertex_src[] =
|
|
|
|
"attribute vec2 pos;\n"
|
|
|
|
"attribute vec2 texture;\n"
|
|
|
|
"varying vec2 v_texture;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" v_texture = vec2(texture.s, 1.0 - texture.t);\n"
|
|
|
|
" gl_Position = vec4(pos, 0, 1);\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
static const char fragment_src[] =
|
|
|
|
"#extension GL_OES_EGL_image_external: require\n\n"
|
|
|
|
"precision mediump float;\n"
|
|
|
|
"uniform samplerExternalOES u_tex;\n"
|
|
|
|
"varying vec2 v_texture;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" gl_FragColor = texture2D(u_tex, v_texture);\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
int rc = -1;
|
|
|
|
GLuint vertex, fragment;
|
|
|
|
|
|
|
|
if (gl_load_shader(&vertex, vertex_src, GL_VERTEX_SHADER) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (gl_load_shader(&fragment, fragment_src, GL_FRAGMENT_SHADER) < 0)
|
|
|
|
goto fragment_failure;
|
|
|
|
|
|
|
|
GLuint program = glCreateProgram();
|
|
|
|
|
|
|
|
glAttachShader(program, vertex);
|
|
|
|
glAttachShader(program, fragment);
|
|
|
|
|
|
|
|
glBindAttribLocation(program, 0, "pos");
|
|
|
|
glBindAttribLocation(program, 1, "texture");
|
|
|
|
|
|
|
|
glLinkProgram(program);
|
|
|
|
|
|
|
|
glDeleteShader(vertex);
|
|
|
|
glDeleteShader(fragment);
|
|
|
|
|
|
|
|
if (glGetError() != GL_NO_ERROR) {
|
|
|
|
glDeleteProgram(program);
|
|
|
|
goto program_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst = program;
|
|
|
|
rc = 0;
|
|
|
|
program_failure:
|
|
|
|
glDeleteShader(fragment);
|
|
|
|
fragment_failure:
|
|
|
|
glDeleteShader(vertex);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_clear(void)
|
|
|
|
{
|
|
|
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_render(void)
|
|
|
|
{
|
|
|
|
static const GLfloat s_vertices[4][2] = {
|
|
|
|
{ -1.0, 1.0 },
|
|
|
|
{ 1.0, 1.0 },
|
|
|
|
{ -1.0, -1.0 },
|
|
|
|
{ 1.0, -1.0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const GLfloat s_positions[4][2] = {
|
|
|
|
{ 0, 0 },
|
|
|
|
{ 1, 0 },
|
|
|
|
{ 0, 1 },
|
|
|
|
{ 1, 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
gl_clear();
|
|
|
|
|
|
|
|
glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, 0, s_vertices);
|
|
|
|
glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, s_positions);
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glEnableVertexAttribArray(1);
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
|
|
|
|
glDisableVertexAttribArray(0);
|
|
|
|
glDisableVertexAttribArray(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void renderer_destroy(struct renderer* self)
|
|
|
|
{
|
|
|
|
eglDestroySurface(self->display, self->surface);
|
|
|
|
eglDestroyContext(self->display, self->context);
|
|
|
|
glDeleteProgram(self->shader_program);
|
|
|
|
}
|
|
|
|
|
2019-10-06 14:08:52 +00:00
|
|
|
int renderer_init(struct renderer* self, uint32_t width, uint32_t height)
|
2019-10-06 13:10:46 +00:00
|
|
|
{
|
|
|
|
if (!eglBindAPI(EGL_OPENGL_ES_API))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (gl_load_early_extensions() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
gl_debug_init();
|
|
|
|
|
|
|
|
self->display =
|
|
|
|
eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
|
|
|
|
EGL_DEFAULT_DISPLAY, NULL);
|
|
|
|
if (!self->display)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!eglInitialize(self->display, NULL, NULL))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
static const EGLint cfg_attr[] = {
|
|
|
|
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
|
|
|
EGL_ALPHA_SIZE, 0,
|
|
|
|
EGL_BLUE_SIZE, 1,
|
|
|
|
EGL_GREEN_SIZE, 1,
|
|
|
|
EGL_RED_SIZE, 1,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
EGLConfig cfg;
|
|
|
|
EGLint cfg_count;
|
|
|
|
|
|
|
|
if (!eglChooseConfig(self->display, cfg_attr, cfg, 1, &cfg_count))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
static const EGLint ctx_attr[] = {
|
|
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
self->context = eglCreateContext(self->display, cfg, EGL_NO_CONTEXT,
|
|
|
|
ctx_attr);
|
|
|
|
if (!self->context)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
EGLint surf_attr[] = {
|
|
|
|
EGL_WIDTH, width,
|
|
|
|
EGL_HEIGHT, height,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
self->surface = eglCreatePbufferSurface(self->display, cfg, surf_attr);
|
|
|
|
if (!self->surface)
|
|
|
|
goto surface_failure;
|
|
|
|
|
|
|
|
if (!eglMakeCurrent(self->display, self->surface, self->surface,
|
|
|
|
self->context))
|
|
|
|
goto make_current_failure;
|
|
|
|
|
|
|
|
log_debug("%s\n", glGetString(GL_VERSION));
|
|
|
|
|
|
|
|
if (gl_load_late_extensions() < 0)
|
|
|
|
goto late_extension_failure;
|
|
|
|
|
|
|
|
if (gl_compile_shader_program(&self->shader_program) < 0)
|
|
|
|
goto shader_failure;
|
|
|
|
|
|
|
|
glUseProgram(self->shader_program);
|
|
|
|
|
2019-10-06 14:08:52 +00:00
|
|
|
self->width = width;
|
|
|
|
self->height = height;
|
|
|
|
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &self->read_format);
|
|
|
|
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &self->read_type);
|
|
|
|
|
2019-10-06 13:10:46 +00:00
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
gl_clear();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
shader_failure:
|
|
|
|
late_extension_failure:
|
|
|
|
make_current_failure:
|
|
|
|
eglDestroySurface(self->display, self->surface);
|
|
|
|
surface_failure:
|
|
|
|
eglDestroyContext(self->display, self->context);
|
|
|
|
return -1;
|
|
|
|
}
|
2019-10-06 14:08:52 +00:00
|
|
|
|
|
|
|
static inline void append_attr(EGLint* dst, int* i, EGLint name, EGLint value)
|
|
|
|
{
|
|
|
|
dst[*i] = name;
|
|
|
|
i[0] += 1;
|
|
|
|
dst[*i] = value;
|
|
|
|
i[0] += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dmabuf_attr_append_planes(EGLint* dst, int* i,
|
|
|
|
struct dmabuf_frame* frame)
|
|
|
|
{
|
|
|
|
#define APPEND_PLANE_ATTR(n) \
|
|
|
|
if (frame->n_planes <= n) \
|
|
|
|
return; \
|
|
|
|
\
|
|
|
|
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_FD_EXT, frame->plane[n].fd); \
|
|
|
|
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_OFFSET_EXT, frame->plane[n].offset); \
|
|
|
|
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_PITCH_EXT, frame->plane[n].pitch); \
|
|
|
|
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_MODIFIER_LO_EXT, frame->plane[n].modifier); \
|
|
|
|
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_MODIFIER_HI_EXT, frame->plane[n].modifier >> 32); \
|
|
|
|
|
|
|
|
APPEND_PLANE_ATTR(0);
|
|
|
|
APPEND_PLANE_ATTR(1);
|
|
|
|
APPEND_PLANE_ATTR(2);
|
|
|
|
APPEND_PLANE_ATTR(3);
|
|
|
|
#undef APPEND_PLANE_ATTR
|
|
|
|
}
|
|
|
|
|
|
|
|
int render_dmabuf_frame(struct renderer* self, struct dmabuf_frame* frame)
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
EGLint attr[6 + 10 * 4 + 1];
|
|
|
|
|
|
|
|
if (frame->n_planes == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
append_attr(attr, &index, EGL_WIDTH, frame->width);
|
|
|
|
append_attr(attr, &index, EGL_HEIGHT, frame->height);
|
|
|
|
append_attr(attr, &index, EGL_LINUX_DRM_FOURCC_EXT, frame->format);
|
|
|
|
dmabuf_attr_append_planes(attr, &index, frame);
|
|
|
|
attr[index++] = EGL_NONE;
|
|
|
|
|
|
|
|
EGLImageKHR image =
|
|
|
|
eglCreateImageKHR(self->display, EGL_NO_CONTEXT,
|
|
|
|
EGL_LINUX_DMA_BUF_EXT, NULL, attr);
|
|
|
|
if (!image)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
GLuint tex;
|
|
|
|
glGenTextures(1, &tex);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
|
|
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
|
|
|
|
|
|
|
|
gl_render();
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glDeleteTextures(1, &tex);
|
|
|
|
eglDestroyImageKHR(self->display, image);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy a horizontal stripe from the GL frame into a pixel buffer */
|
|
|
|
void render_copy_pixels(struct renderer* self, void* dst, uint32_t y, uint32_t height)
|
|
|
|
{
|
|
|
|
assert(y + height <= self->height);
|
|
|
|
|
|
|
|
glReadPixels(0, y, self->width, height, self->read_format,
|
|
|
|
self->read_type, dst);
|
|
|
|
}
|