kmscon/src/output_context.c
David Herrmann bd09f38a4d log: move log_warning to log_warn
log_warn is much shorter and we already use log_err instead of log_error
so this is more consistent now.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
2012-01-24 15:29:55 +01:00

863 lines
23 KiB
C

/*
* kmscon - Drawing Contexts
*
* Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
* Copyright (c) 2011 University of Tuebingen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Drawing Contexts
* This provides a drwaing context for compositor objects and associated
* framebuffers for output objects. It is implemented with OpenGL as backend.
*/
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#ifdef USE_GLES2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#include "log.h"
#include "output.h"
/* OpenGL extension definitions */
typedef void (*PFNGLGENRENDERBUFFERSPROC)
(GLsizei n, GLuint *renderbuffers);
typedef void (*PFNGLBINDRENDERBUFFERPROC)
(GLenum target, GLuint renderbuffer);
typedef void (*PFNGLDELETERENDERBUFFERSPROC)
(GLsizei n, const GLuint *renderbuffers);
typedef void (*PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target,
GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target);
typedef void (*PFNGLGENFRAMEBUFFERSPROC)
(GLsizei n, GLuint *framebuffers);
typedef void (*PFNGLBINDFRAMEBUFFERPROC)
(GLenum target, GLuint framebuffer);
typedef void (*PFNGLDELETEFRAMEBUFFERSPROC)
(GLsizei n, const GLuint *framebuffers);
typedef GLuint (*PFNGLCREATESHADERPROC) (GLenum type);
typedef void (*PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (*PFNGLSHADERSOURCEPROC) (GLuint shader,
GLsizei count, const GLchar* *string, const GLint *length);
typedef void (*PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef void (*PFNGLGETSHADERIVPROC)
(GLuint shader, GLenum pname, GLint *params);
typedef void (*PFNGLGETSHADERINFOLOGPROC)
(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLuint (*PFNGLCREATEPROGRAMPROC) (void);
typedef void (*PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (*PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (*PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (*PFNGLBINDATTRIBLOCATIONPROC)
(GLuint program, GLuint index, const GLchar *name);
typedef void (*PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (*PFNGLGETPROGRAMIVPROC)
(GLuint program, GLenum pname, GLint *params);
typedef void (*PFNGLGETPROGRAMINFOLOGPROC)
(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (*PFNGLGETUNIFORMLOCATIONPROC)
(GLuint program, const GLchar *name);
typedef void (*PFNGLUNIFORMMATRIX4FVPROC) (GLint location,
GLsizei count, GLboolean transpose, const GLfloat *value);
typedef void (*PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (*PFNGLVERTEXATTRIBPOINTERPROC)
(GLuint index, GLint size, GLenum type, GLboolean normalized,
GLsizei stride, const GLvoid *pointer);
typedef void (*PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (*PFNGLDRAWARRAYSEXTPROC)
(GLenum mode, GLint first, GLsizei count);
struct kmscon_context {
EGLDisplay display;
EGLContext context;
GLuint def_program;
GLuint def_vshader;
GLuint def_fshader;
GLuint def_uni_projection;
GLuint tex_program;
GLuint tex_vshader;
GLuint tex_fshader;
GLuint tex_uni_projection;
GLuint tex_uni_texture;
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC proc_rbuf_storage;
PFNEGLCREATEIMAGEKHRPROC proc_create_image;
PFNEGLDESTROYIMAGEKHRPROC proc_destroy_image;
PFNGLGENRENDERBUFFERSPROC proc_gen_renderbuffers;
PFNGLBINDRENDERBUFFERPROC proc_bind_renderbuffer;
PFNGLDELETERENDERBUFFERSPROC proc_delete_renderbuffers;
PFNGLFRAMEBUFFERRENDERBUFFERPROC proc_framebuffer_renderbuffer;
PFNGLCHECKFRAMEBUFFERSTATUSPROC proc_check_framebuffer_status;
PFNGLGENFRAMEBUFFERSPROC proc_gen_framebuffers;
PFNGLBINDFRAMEBUFFERPROC proc_bind_framebuffer;
PFNGLDELETEFRAMEBUFFERSPROC proc_delete_framebuffers;
PFNGLCREATESHADERPROC proc_create_shader;
PFNGLDELETESHADERPROC proc_delete_shader;
PFNGLSHADERSOURCEPROC proc_shader_source;
PFNGLCOMPILESHADERPROC proc_compile_shader;
PFNGLGETSHADERIVPROC proc_get_shader_iv;
PFNGLGETSHADERINFOLOGPROC proc_get_shader_info_log;
PFNGLCREATEPROGRAMPROC proc_create_program;
PFNGLDELETEPROGRAMPROC proc_delete_program;
PFNGLUSEPROGRAMPROC proc_use_program;
PFNGLATTACHSHADERPROC proc_attach_shader;
PFNGLBINDATTRIBLOCATIONPROC proc_bind_attrib_location;
PFNGLLINKPROGRAMPROC proc_link_program;
PFNGLGETPROGRAMIVPROC proc_get_program_iv;
PFNGLGETPROGRAMINFOLOGPROC proc_get_program_info_log;
PFNGLGETUNIFORMLOCATIONPROC proc_get_uniform_location;
PFNGLUNIFORMMATRIX4FVPROC proc_uniform_matrix_4fv;
PFNGLUNIFORM1IPROC proc_uniform_1i;
PFNGLVERTEXATTRIBPOINTERPROC proc_vertex_attrib_pointer;
PFNGLENABLEVERTEXATTRIBARRAYPROC proc_enable_vertex_attrib_array;
PFNGLDRAWARRAYSEXTPROC proc_draw_arrays;
};
struct renderbuffer {
struct kmscon_context *ctx;
EGLImageKHR image;
GLuint rb;
};
struct kmscon_framebuffer {
struct kmscon_context *ctx;
GLuint fb;
struct renderbuffer *rbs[2];
unsigned int current_rb;
};
/*
* Clear the GL error stack. The standard says that the error value is just a
* single value and no list/stack. However, multiple error fields may be defined
* and glGetError() returns only one of them until all are cleared. Hence, we
* loop until no more error is retrieved.
*/
static void clear_gl_error()
{
GLenum err;
do {
err = glGetError();
} while (err != GL_NO_ERROR);
}
/* return true if there is a pending GL error */
static bool has_gl_error()
{
GLenum err;
err = glGetError();
if (err != GL_NO_ERROR) {
log_err("context: GL error %d\n", err);
return true;
}
return false;
}
/* external shader sources; generated during build */
extern const char *kmscon_vert_def;
extern const char *kmscon_frag_def;
extern const char *kmscon_vert_tex;
extern const char *kmscon_frag_tex;
static int compile_shader(struct kmscon_context *ctx, GLenum type,
const char *source)
{
char msg[512];
GLint status = 1;
GLuint s;
s = ctx->proc_create_shader(type);
if (s == GL_NONE) {
log_warn("context: cannot allocate GL shader\n");
return GL_NONE;
}
ctx->proc_shader_source(s, 1, &source, NULL);
ctx->proc_compile_shader(s);
ctx->proc_get_shader_iv(s, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
msg[0] = 0;
ctx->proc_get_shader_info_log(s, sizeof(msg), NULL, msg);
log_warn("context: cannot compile shader: %s\n", msg);
return GL_NONE;
}
return s;
}
static int init_def_shader(struct kmscon_context *ctx)
{
char msg[512];
GLint status = 1;
int ret;
if (!ctx)
return -EINVAL;
ctx->def_vshader = compile_shader(ctx, GL_VERTEX_SHADER,
kmscon_vert_def);
if (ctx->def_vshader == GL_NONE)
return -EFAULT;
ctx->def_fshader = compile_shader(ctx, GL_FRAGMENT_SHADER,
kmscon_frag_def);
if (ctx->def_fshader == GL_NONE) {
ret = -EFAULT;
goto err_vshader;
}
ctx->def_program = ctx->proc_create_program();
ctx->proc_attach_shader(ctx->def_program, ctx->def_vshader);
ctx->proc_attach_shader(ctx->def_program, ctx->def_fshader);
ctx->proc_bind_attrib_location(ctx->def_program, 0, "position");
ctx->proc_bind_attrib_location(ctx->def_program, 1, "color");
ctx->proc_link_program(ctx->def_program);
ctx->proc_get_program_iv(ctx->def_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
msg[0] = 0;
ctx->proc_get_program_info_log(ctx->def_program, sizeof(msg),
NULL, msg);
log_warn("context: cannot link shader: %s\n", msg);
ret = -EFAULT;
goto err_link;
}
ctx->def_uni_projection =
ctx->proc_get_uniform_location(ctx->def_program, "projection");
return 0;
err_link:
ctx->proc_delete_program(ctx->def_program);
ctx->proc_delete_shader(ctx->def_fshader);
err_vshader:
ctx->proc_delete_shader(ctx->def_vshader);
return ret;
}
static int init_tex_shader(struct kmscon_context *ctx)
{
char msg[512];
GLint status = 1;
int ret;
if (!ctx)
return -EINVAL;
ctx->tex_vshader = compile_shader(ctx, GL_VERTEX_SHADER,
kmscon_vert_tex);
if (ctx->tex_vshader == GL_NONE)
return -EFAULT;
ctx->tex_fshader = compile_shader(ctx, GL_FRAGMENT_SHADER,
kmscon_frag_tex);
if (ctx->tex_fshader == GL_NONE) {
ret = -EFAULT;
goto err_vshader;
}
ctx->tex_program = ctx->proc_create_program();
ctx->proc_attach_shader(ctx->tex_program, ctx->tex_vshader);
ctx->proc_attach_shader(ctx->tex_program, ctx->tex_fshader);
ctx->proc_bind_attrib_location(ctx->tex_program, 0, "position");
ctx->proc_bind_attrib_location(ctx->tex_program, 1, "texture_position");
ctx->proc_link_program(ctx->tex_program);
ctx->proc_get_program_iv(ctx->tex_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
msg[0] = 0;
ctx->proc_get_program_info_log(ctx->tex_program, sizeof(msg),
NULL, msg);
log_warn("context: cannot link shader: %s\n", msg);
ret = -EFAULT;
goto err_link;
}
ctx->tex_uni_projection =
ctx->proc_get_uniform_location(ctx->tex_program, "projection");
ctx->tex_uni_texture =
ctx->proc_get_uniform_location(ctx->tex_program, "texture");
return 0;
err_link:
ctx->proc_delete_program(ctx->tex_program);
ctx->proc_delete_shader(ctx->tex_fshader);
err_vshader:
ctx->proc_delete_shader(ctx->tex_vshader);
return ret;
}
static int init_shader(struct kmscon_context *ctx)
{
int ret;
ret = init_def_shader(ctx);
if (ret)
return ret;
ret = init_tex_shader(ctx);
if (ret) {
ctx->proc_delete_program(ctx->def_program);
ctx->proc_delete_shader(ctx->def_fshader);
ctx->proc_delete_shader(ctx->def_vshader);
return ret;
}
return 0;
}
static void destroy_shader(struct kmscon_context *ctx)
{
if (!ctx)
return;
ctx->proc_delete_program(ctx->tex_program);
ctx->proc_delete_shader(ctx->tex_fshader);
ctx->proc_delete_shader(ctx->tex_vshader);
ctx->proc_delete_program(ctx->def_program);
ctx->proc_delete_shader(ctx->def_fshader);
ctx->proc_delete_shader(ctx->def_vshader);
}
/*
* Create the GL context
* This uses the EGL library for context creation and needs a valid gbm device
* as argument. The caller must provide a valid gbm device as \gbm. We do not
* touch \gbm at all but pass it to EGL. The \gbm object must live as long as we
* do.
*/
int kmscon_context_new(struct kmscon_context **out, void *gbm)
{
struct kmscon_context *ctx;
EGLint major, minor;
int ret;
const char *ext;
EGLenum api;
#ifdef USE_GLES2
static const EGLint ctx_attribs[] =
{ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
#else
static const EGLint *ctx_attribs = NULL;
#endif
if (!out || !gbm)
return -EINVAL;
log_debug("context: new GL context\n");
ctx = malloc(sizeof(*ctx));
if (!ctx)
return -ENOMEM;
memset(ctx, 0, sizeof(*ctx));
ctx->display = eglGetDisplay((EGLNativeDisplayType) gbm);
if (!ctx->display) {
log_warn("context: cannot get EGL display\n");
ret = -EFAULT;
goto err_free;
}
ret = eglInitialize(ctx->display, &major, &minor);
if (!ret) {
log_warn("context: cannot initialize EGL display\n");
ret = -EFAULT;
goto err_free;
}
ext = eglQueryString(ctx->display, EGL_EXTENSIONS);
if (!ext || !strstr(ext, "EGL_KHR_surfaceless_opengl")) {
log_warn("context: surfaceless EGL not supported\n");
ret = -ENOTSUP;
goto err_display;
}
#ifdef USE_GLES2
api = EGL_OPENGL_ES_API;
#else
api = EGL_OPENGL_API;
#endif
if (!eglBindAPI(api)) {
log_warn("context: cannot bind EGL OpenGL API\n");
ret = -EFAULT;
goto err_display;
}
ctx->context = eglCreateContext(ctx->display, NULL, EGL_NO_CONTEXT,
ctx_attribs);
if (!ctx->context) {
log_warn("context: cannot create EGL context\n");
ret = -EFAULT;
goto err_display;
}
if (!eglMakeCurrent(ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
ctx->context)) {
log_warn("context: cannot use EGL context\n");
ret = -EFAULT;
goto err_ctx;
}
ctx->proc_rbuf_storage = (void*)
eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES");
ctx->proc_create_image =
(void*) eglGetProcAddress("eglCreateImageKHR");
ctx->proc_destroy_image =
(void*) eglGetProcAddress("eglDestroyImageKHR");
ctx->proc_gen_renderbuffers =
(void*) eglGetProcAddress("glGenRenderbuffers");
ctx->proc_bind_renderbuffer =
(void*) eglGetProcAddress("glBindRenderbuffer");
ctx->proc_delete_renderbuffers =
(void*) eglGetProcAddress("glDeleteRenderbuffers");
ctx->proc_framebuffer_renderbuffer =
(void*) eglGetProcAddress("glFramebufferRenderbuffer");
ctx->proc_check_framebuffer_status =
(void*) eglGetProcAddress("glCheckFramebufferStatus");
ctx->proc_gen_framebuffers =
(void*) eglGetProcAddress("glGenFramebuffers");
ctx->proc_bind_framebuffer =
(void*) eglGetProcAddress("glBindFramebuffer");
ctx->proc_delete_framebuffers =
(void*) eglGetProcAddress("glDeleteFramebuffers");
ctx->proc_create_shader =
(void*) eglGetProcAddress("glCreateShader");
ctx->proc_delete_shader =
(void*) eglGetProcAddress("glDeleteShader");
ctx->proc_shader_source =
(void*) eglGetProcAddress("glShaderSource");
ctx->proc_compile_shader =
(void*) eglGetProcAddress("glCompileShader");
ctx->proc_get_shader_iv =
(void*) eglGetProcAddress("glGetShaderiv");
ctx->proc_get_shader_info_log =
(void*) eglGetProcAddress("glGetShaderInfoLog");
ctx->proc_create_program =
(void*) eglGetProcAddress("glCreateProgram");
ctx->proc_delete_program =
(void*) eglGetProcAddress("glDeleteProgram");
ctx->proc_use_program =
(void*) eglGetProcAddress("glUseProgram");
ctx->proc_attach_shader =
(void*) eglGetProcAddress("glAttachShader");
ctx->proc_bind_attrib_location =
(void*) eglGetProcAddress("glBindAttribLocation");
ctx->proc_link_program =
(void*) eglGetProcAddress("glLinkProgram");
ctx->proc_get_program_iv =
(void*) eglGetProcAddress("glGetProgramiv");
ctx->proc_get_program_info_log =
(void*) eglGetProcAddress("glGetProgramInfoLog");
ctx->proc_get_uniform_location =
(void*) eglGetProcAddress("glGetUniformLocation");
ctx->proc_uniform_matrix_4fv =
(void*) eglGetProcAddress("glUniformMatrix4fv");
ctx->proc_uniform_1i =
(void*) eglGetProcAddress("glUniform1i");
ctx->proc_vertex_attrib_pointer =
(void*) eglGetProcAddress("glVertexAttribPointer");
ctx->proc_enable_vertex_attrib_array =
(void*) eglGetProcAddress("glEnableVertexAttribArray");
ctx->proc_draw_arrays =
(void*) eglGetProcAddress("glDrawArraysEXT");
if (!ctx->proc_rbuf_storage || !ctx->proc_create_image ||
!ctx->proc_destroy_image) {
log_warn("context: KHR images not supported\n");
ret = -ENOTSUP;
goto err_ctx;
} else if (!ctx->proc_gen_renderbuffers ||
!ctx->proc_bind_renderbuffer ||
!ctx->proc_delete_renderbuffers ||
!ctx->proc_framebuffer_renderbuffer ||
!ctx->proc_check_framebuffer_status) {
log_warn("context: renderbuffers not supported\n");
ret = -ENOTSUP;
goto err_ctx;
} else if (!ctx->proc_create_shader ||
!ctx->proc_delete_shader ||
!ctx->proc_shader_source ||
!ctx->proc_compile_shader ||
!ctx->proc_get_shader_iv ||
!ctx->proc_get_shader_info_log) {
log_warn("context: shaders not supported\n");
ret = -ENOTSUP;
goto err_ctx;
} else if (!ctx->proc_create_program ||
!ctx->proc_delete_program ||
!ctx->proc_use_program ||
!ctx->proc_attach_shader ||
!ctx->proc_bind_attrib_location ||
!ctx->proc_link_program ||
!ctx->proc_get_program_iv ||
!ctx->proc_get_program_info_log ||
!ctx->proc_get_uniform_location ||
!ctx->proc_uniform_matrix_4fv ||
!ctx->proc_uniform_1i ||
!ctx->proc_vertex_attrib_pointer ||
!ctx->proc_enable_vertex_attrib_array ||
!ctx->proc_draw_arrays) {
log_warn("context: shaders not supported\n");
ret = -ENOTSUP;
goto err_ctx;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
ret = init_shader(ctx);
if (ret)
goto err_ctx;
*out = ctx;
return 0;
err_ctx:
eglDestroyContext(ctx->display, ctx->context);
err_display:
eglTerminate(ctx->display);
err_free:
free(ctx);
return ret;
}
void kmscon_context_destroy(struct kmscon_context *ctx)
{
if (!ctx)
return;
destroy_shader(ctx);
eglDestroyContext(ctx->display, ctx->context);
eglTerminate(ctx->display);
free(ctx);
log_debug("context: destroying GL context\n");
}
int kmscon_context_use(struct kmscon_context *ctx)
{
if (!ctx)
return -EINVAL;
if (!eglMakeCurrent(ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
ctx->context)) {
log_warn("context: cannot use EGL context\n");
return -EFAULT;
}
return 0;
}
bool kmscon_context_is_active(struct kmscon_context *ctx)
{
if (!ctx)
return false;
return ctx->context == eglGetCurrentContext();
}
void kmscon_context_flush(struct kmscon_context *ctx)
{
if (!ctx)
return;
glFinish();
}
void kmscon_context_viewport(struct kmscon_context *ctx,
unsigned int width, unsigned int height)
{
if (!ctx)
return;
glViewport(0, 0, width, height);
}
void kmscon_context_clear(struct kmscon_context *ctx)
{
if (!ctx)
return;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
void kmscon_context_draw_def(struct kmscon_context *ctx, float *vertices,
float *colors, size_t num)
{
float m[16] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
if (!ctx)
return;
ctx->proc_use_program(ctx->def_program);
ctx->proc_uniform_matrix_4fv(ctx->def_uni_projection, 1, GL_FALSE, m);
ctx->proc_vertex_attrib_pointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
ctx->proc_vertex_attrib_pointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors);
ctx->proc_enable_vertex_attrib_array(0);
ctx->proc_enable_vertex_attrib_array(1);
ctx->proc_draw_arrays(GL_TRIANGLES, 0, num);
}
void kmscon_context_draw_tex(struct kmscon_context *ctx, const float *vertices,
const float *texcoords, size_t num, unsigned int tex, const float *m)
{
float mat[16];
if (!ctx)
return;
kmscon_m4_transp_dest(mat, m);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
ctx->proc_use_program(ctx->tex_program);
ctx->proc_uniform_matrix_4fv(ctx->tex_uni_projection, 1, GL_FALSE, mat);
ctx->proc_uniform_1i(ctx->tex_uni_texture, 0);
ctx->proc_vertex_attrib_pointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
ctx->proc_vertex_attrib_pointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
ctx->proc_enable_vertex_attrib_array(0);
ctx->proc_enable_vertex_attrib_array(1);
ctx->proc_draw_arrays(GL_TRIANGLES, 0, num);
}
unsigned int kmscon_context_new_tex(struct kmscon_context *ctx)
{
GLuint tex = 0;
if (!ctx)
return tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return tex;
}
void kmscon_context_free_tex(struct kmscon_context *ctx, unsigned int tex)
{
if (!ctx)
return;
glDeleteTextures(1, &tex);
}
void kmscon_context_set_tex(struct kmscon_context *ctx, unsigned int tex,
unsigned int width, unsigned int height, void *buf)
{
if (!ctx || !buf || !width || !height)
return;
glBindTexture(GL_TEXTURE_2D, tex);
#ifdef USE_GLES2
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0, GL_BGRA_EXT,
GL_UNSIGNED_BYTE, buf);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA,
GL_UNSIGNED_BYTE, buf);
#endif
}
int renderbuffer_new(struct renderbuffer **out, struct kmscon_context *ctx,
void *bo)
{
struct renderbuffer *rb;
int ret;
if (!out || !ctx || !bo)
return -EINVAL;
rb = malloc(sizeof(*rb));
if (!rb)
return -ENOMEM;
memset(rb, 0, sizeof(*rb));
rb->ctx = ctx;
clear_gl_error();
rb->image = ctx->proc_create_image(ctx->display, NULL,
EGL_NATIVE_PIXMAP_KHR, bo, NULL);
if (!rb->image) {
log_warn("context: cannot create EGL image\n");
ret = -EFAULT;
goto err_free;
}
ctx->proc_gen_renderbuffers(1, &rb->rb);
ctx->proc_bind_renderbuffer(GL_RENDERBUFFER, rb->rb);
ctx->proc_rbuf_storage(GL_RENDERBUFFER, rb->image);
if (has_gl_error()) {
log_warn("context: cannot create renderbuffers\n");
ret = -EFAULT;
goto err_gl;
}
*out = rb;
return 0;
err_gl:
ctx->proc_bind_renderbuffer(GL_RENDERBUFFER, 0);
ctx->proc_delete_renderbuffers(1, &rb->rb);
ctx->proc_destroy_image(ctx->display, rb->image);
err_free:
free(rb);
return ret;
}
void renderbuffer_destroy(struct renderbuffer *rb)
{
if (!rb)
return;
rb->ctx->proc_bind_renderbuffer(GL_RENDERBUFFER, 0);
rb->ctx->proc_delete_renderbuffers(1, &rb->rb);
rb->ctx->proc_destroy_image(rb->ctx->display, rb->image);
free(rb);
}
int kmscon_framebuffer_new(struct kmscon_framebuffer **out,
struct kmscon_context *ctx, void *bo1, void *bo2)
{
struct kmscon_framebuffer *fb;
int ret;
if (!out || !ctx || !bo1 || !bo2)
return -EINVAL;
fb = malloc(sizeof(*fb));
if (!fb)
return -ENOMEM;
memset(fb, 0, sizeof(*fb));
fb->ctx = ctx;
ret = renderbuffer_new(&fb->rbs[0], ctx, bo1);
if (ret)
goto err_free;
ret = renderbuffer_new(&fb->rbs[1], ctx, bo2);
if (ret)
goto err_rb;
ctx->proc_gen_framebuffers(1, &fb->fb);
ctx->proc_bind_framebuffer(GL_FRAMEBUFFER, fb->fb);
ctx->proc_framebuffer_renderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fb->rbs[0]->rb);
if (ctx->proc_check_framebuffer_status(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE) {
log_warn("context: invalid GL framebuffer state\n");
ret = -EFAULT;
goto err_fb;
}
*out = fb;
return 0;
err_fb:
ctx->proc_bind_framebuffer(GL_FRAMEBUFFER, 0);
ctx->proc_delete_framebuffers(1, &fb->fb);
renderbuffer_destroy(fb->rbs[1]);
err_rb:
renderbuffer_destroy(fb->rbs[0]);
err_free:
free(fb);
return ret;
}
void kmscon_framebuffer_destroy(struct kmscon_framebuffer *fb)
{
if (!fb)
return;
fb->ctx->proc_bind_framebuffer(GL_FRAMEBUFFER, 0);
fb->ctx->proc_delete_framebuffers(1, &fb->fb);
renderbuffer_destroy(fb->rbs[1]);
renderbuffer_destroy(fb->rbs[0]);
free(fb);
}
void kmscon_framebuffer_use(struct kmscon_framebuffer *fb)
{
if (!fb)
return;
fb->ctx->proc_bind_framebuffer(GL_FRAMEBUFFER, fb->fb);
}
int kmscon_framebuffer_swap(struct kmscon_framebuffer *fb)
{
if (!fb)
return -EINVAL;
fb->current_rb ^= 1;
fb->ctx->proc_bind_framebuffer(GL_FRAMEBUFFER, fb->fb);
fb->ctx->proc_framebuffer_renderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
fb->rbs[fb->current_rb]->rb);
if (fb->ctx->proc_check_framebuffer_status(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE)
log_warn("context: invalid GL framebuffer state\n");
return fb->current_rb;
}