output: move EGL/GL calls into separate header

This splits off all EGL/GL calls into output_context.c. This way we can
replace the backend of the drawing functions very easily.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-01-16 17:37:42 +01:00
parent a8dcede8c7
commit 7c7a4e32f9
4 changed files with 494 additions and 172 deletions

View File

@ -22,6 +22,7 @@ endif
libkmscon_core_la_SOURCES = \
src/console.c src/console.h \
src/output.c src/output.h \
src/output_context.c \
src/console_cell.c \
src/unicode.c src/unicode.h \
src/log.c src/log.h \

View File

@ -30,13 +30,6 @@
* contexts available for drawing directly to the graphics framebuffer.
*/
/*
* TODO: Avoid using this hack and instead retrieve EGL and GL extension
* pointers dynamically on initialization.
*/
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@ -46,11 +39,7 @@
#include <string.h>
#include <unistd.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gbm.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@ -66,9 +55,7 @@ struct kmscon_mode {
};
struct render_buffer {
GLuint rb;
struct gbm_bo *bo;
EGLImageKHR image;
uint32_t fb;
};
@ -92,9 +79,8 @@ struct kmscon_output {
uint32_t conn_id;
uint32_t crtc_id;
unsigned int cur_rb;
struct render_buffer rb[2];
GLuint fb;
struct kmscon_framebuffer *fb;
drmModeCrtcPtr saved_crtc;
};
@ -113,8 +99,7 @@ struct kmscon_compositor {
int drm_fd;
struct gbm_device *gbm;
EGLDisplay display;
EGLContext context;
struct kmscon_context *ctx;
};
/*
@ -574,18 +559,6 @@ static int init_rb(struct render_buffer *rb, struct kmscon_compositor *comp,
return -EFAULT;
}
rb->image = eglCreateImageKHR(comp->display, NULL,
EGL_NATIVE_PIXMAP_KHR, rb->bo, NULL);
if (!rb->image) {
log_warning("output: cannot create EGL image\n");
ret = -EFAULT;
goto err_bo;
}
glGenRenderbuffers(1, &rb->rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb->rb);
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, rb->image);
stride = gbm_bo_get_pitch(rb->bo);
handle = gbm_bo_get_handle(rb->bo).u32;
@ -598,15 +571,11 @@ static int init_rb(struct render_buffer *rb, struct kmscon_compositor *comp,
if (ret) {
log_warning("output: cannot add DRM framebuffer object\n");
ret = -EFAULT;
goto err_rb;
goto err_bo;
}
return 0;
err_rb:
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glDeleteRenderbuffers(1, &rb->rb);
eglDestroyImageKHR(comp->display, rb->image);
err_bo:
gbm_bo_destroy(rb->bo);
return ret;
@ -616,9 +585,6 @@ static void destroy_rb(struct render_buffer *rb,
struct kmscon_compositor *comp)
{
drmModeRmFB(comp->drm_fd, rb->fb);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glDeleteRenderbuffers(1, &rb->rb);
eglDestroyImageKHR(comp->display, rb->image);
gbm_bo_destroy(rb->bo);
}
@ -667,22 +633,15 @@ int kmscon_output_activate(struct kmscon_output *output,
output->current = mode;
output->active = 1;
output->cur_rb = 0;
glGenFramebuffers(1, &output->fb);
glBindFramebuffer(GL_FRAMEBUFFER, output->fb);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, output->rb[0].rb);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE) {
log_warning("output: invalid GL framebuffer state\n");
ret = -EFAULT;
goto err_fb;
}
ret = kmscon_framebuffer_new(&output->fb, comp->ctx, output->rb[0].bo,
output->rb[1].bo);
if (ret)
goto err_rb;
glViewport(0, 0, mode->info.hdisplay, mode->info.vdisplay);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
kmscon_context_viewport(output->comp->ctx, mode->info.hdisplay,
mode->info.vdisplay);
kmscon_context_clear(output->comp->ctx);
ret = kmscon_output_swap(output);
if (ret)
@ -691,8 +650,8 @@ int kmscon_output_activate(struct kmscon_output *output,
return 0;
err_fb:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &output->fb);
kmscon_framebuffer_destroy(output->fb);
err_rb:
destroy_rb(&output->rb[0], output->comp);
destroy_rb(&output->rb[1], output->comp);
output->active = 0;
@ -730,8 +689,7 @@ void kmscon_output_deactivate(struct kmscon_output *output)
output->saved_crtc = NULL;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &output->fb);
kmscon_framebuffer_destroy(output->fb);
destroy_rb(&output->rb[0], output->comp);
destroy_rb(&output->rb[1], output->comp);
output->current = NULL;
@ -763,9 +721,7 @@ int kmscon_output_use(struct kmscon_output *output)
if (kmscon_compositor_is_asleep(output->comp))
return -EINVAL;
glBindFramebuffer(GL_FRAMEBUFFER, output->fb);
glViewport(0, 0, output->current->info.hdisplay,
output->current->info.vdisplay);
kmscon_framebuffer_use(output->fb);
return 0;
}
@ -780,7 +736,7 @@ int kmscon_output_use(struct kmscon_output *output)
*/
int kmscon_output_swap(struct kmscon_output *output)
{
int ret;
int ret, num;
if (!output || !output->active)
return -EINVAL;
@ -788,119 +744,30 @@ int kmscon_output_swap(struct kmscon_output *output)
if (kmscon_compositor_is_asleep(output->comp))
return -EINVAL;
glBindFramebuffer(GL_FRAMEBUFFER, output->fb);
glFinish();
kmscon_context_flush(output->comp->ctx);
num = kmscon_framebuffer_swap(output->fb);
if (num < 0)
return num;
if (num > 1)
num = 1;
num ^= 1;
ret = drmModeSetCrtc(output->comp->drm_fd, output->crtc_id,
output->rb[output->cur_rb].fb, 0, 0, &output->conn_id, 1,
output->rb[num].fb, 0, 0, &output->conn_id, 1,
&output->current->info);
if (ret) {
log_warning("output: cannot set CRTC\n");
ret = -EFAULT;
}
output->cur_rb ^= 1;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, output->rb[output->cur_rb].rb);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE) {
log_warning("output: invalid GL framebuffer state\n");
ret = -EFAULT;
}
kmscon_context_viewport(output->comp->ctx,
output->current->info.hdisplay,
output->current->info.vdisplay);
kmscon_context_clear(output->comp->ctx);
return ret;
}
/*
* Initializes the compositor object. This opens the DRI device, initializes
* EGL and creates a GL context. It does not activate the GL context. You need
* to call kmscon_compositor_use() to activate the context.
* Returns 0 on success.
*/
static int compositor_init(struct kmscon_compositor *comp)
{
EGLint major, minor;
int ret;
const char *ext;
comp->state = COMPOSITOR_ASLEEP;
/* TODO: Retrieve this path dynamically */
comp->drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (comp->drm_fd < 0) {
log_warning("output: cannot open dri/card0: %d\n", errno);
return -errno;
}
comp->gbm = gbm_create_device(comp->drm_fd);
if (!comp->gbm) {
log_warning("output: cannot allocate gbm device\n");
ret = -EFAULT;
goto err_drm;
}
comp->display = eglGetDisplay((EGLNativeDisplayType)comp->gbm);
if (!comp->display) {
log_warning("output: cannot get EGL display\n");
ret = -EFAULT;
goto err_gbm;
}
ret = eglInitialize(comp->display, &major, &minor);
if (!ret) {
log_warning("output: cannot initialize EGL display\n");
ret = -EFAULT;
goto err_gbm;
}
ext = eglQueryString(comp->display, EGL_EXTENSIONS);
if (!ext || !strstr(ext, "EGL_KHR_surfaceless_opengl")) {
log_warning("output: surfaceless EGL not supported\n");
ret = -ENOTSUP;
goto err_display;
}
if (!eglBindAPI(EGL_OPENGL_API)) {
log_warning("output: cannot bind EGL OpenGL API\n");
ret = -EFAULT;
goto err_display;
}
comp->context = eglCreateContext(comp->display, NULL,
EGL_NO_CONTEXT, NULL);
if (!comp->context) {
log_warning("output: cannot create EGL context\n");
ret = -EFAULT;
goto err_display;
}
return 0;
err_display:
eglTerminate(comp->display);
err_gbm:
gbm_device_destroy(comp->gbm);
err_drm:
close(comp->drm_fd);
return ret;
}
/*
* Counterpart of compositor_init(). Must not be called if compositor_init()
* failed.
*/
static void compositor_deinit(struct kmscon_compositor *comp)
{
while (comp->outputs)
kmscon_output_unbind(comp->outputs);
eglDestroyContext(comp->display, comp->context);
eglTerminate(comp->display);
gbm_device_destroy(comp->gbm);
close(comp->drm_fd);
}
/*
* Create a new compositor object. A GL context is created but the
* compositor is asleep by default so no outputs are connected.
@ -921,15 +788,37 @@ int kmscon_compositor_new(struct kmscon_compositor **out)
memset(comp, 0, sizeof(*comp));
comp->ref = 1;
comp->state = COMPOSITOR_ASLEEP;
ret = compositor_init(comp);
if (ret) {
free(comp);
return ret;
/* TODO: Retrieve this path dynamically */
comp->drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (comp->drm_fd < 0) {
log_warning("output: cannot open /dev/dri/card0: %m\n");
ret = -errno;
goto err_free;
}
comp->gbm = gbm_create_device(comp->drm_fd);
if (!comp->gbm) {
log_warning("output: cannot allocate gbm device\n");
ret = -EFAULT;
goto err_drm;
}
ret = kmscon_context_new(&comp->ctx, comp->gbm);
if (ret)
goto err_gbm;
*out = comp;
return 0;
err_gbm:
gbm_device_destroy(comp->gbm);
err_drm:
close(comp->drm_fd);
err_free:
free(comp);
return ret;
}
void kmscon_compositor_ref(struct kmscon_compositor *comp)
@ -952,14 +841,19 @@ void kmscon_compositor_unref(struct kmscon_compositor *comp)
if (--comp->ref)
return;
compositor_deinit(comp);
while (comp->outputs)
kmscon_output_unbind(comp->outputs);
kmscon_context_destroy(comp->ctx);
gbm_device_destroy(comp->gbm);
close(comp->drm_fd);
free(comp);
log_debug("output: destroying compositor\n");
}
/*
* This puts the compositor asleep. While the compositor is asleep, no access
* to the DRI are made so other applications may use the DRM.
* to the DRM are made so other applications may use the DRM.
* You shouldn't access the compositor and its outputs while it is asleep as
* almost all functions will return -EINVAL while asleep.
*/
@ -1036,13 +930,10 @@ bool kmscon_compositor_is_asleep(struct kmscon_compositor *comp)
*/
int kmscon_compositor_use(struct kmscon_compositor *comp)
{
if (!eglMakeCurrent(comp->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
comp->context)) {
log_warning("output: cannot use EGL context\n");
return -EFAULT;
}
if (!comp)
return -EINVAL;
return 0;
return kmscon_context_use(comp->ctx);
}
/*

View File

@ -64,7 +64,9 @@
#include <stdlib.h>
struct kmscon_mode;
struct kmscon_framebuffer;
struct kmscon_output;
struct kmscon_context;
struct kmscon_compositor;
/* output modes */
@ -78,6 +80,15 @@ const char *kmscon_mode_get_name(const struct kmscon_mode *mode);
uint32_t kmscon_mode_get_width(const struct kmscon_mode *mode);
uint32_t kmscon_mode_get_height(const struct kmscon_mode *mode);
/* framebuffer */
int kmscon_framebuffer_new(struct kmscon_framebuffer **out,
struct kmscon_context *ctx, void *bo1, void *bo2);
void kmscon_framebuffer_destroy(struct kmscon_framebuffer *fb);
void kmscon_framebuffer_use(struct kmscon_framebuffer *fb);
int kmscon_framebuffer_swap(struct kmscon_framebuffer *fb);
/* compositor outputs */
int kmscon_output_new(struct kmscon_output **out);
@ -98,6 +109,17 @@ bool kmscon_output_is_active(struct kmscon_output *output);
int kmscon_output_use(struct kmscon_output *output);
int kmscon_output_swap(struct kmscon_output *output);
/* drawing contexts */
int kmscon_context_new(struct kmscon_context **out, void *gbm);
void kmscon_context_destroy(struct kmscon_context *ctx);
int kmscon_context_use(struct kmscon_context *ctx);
bool kmscon_context_is_active(struct kmscon_context *ctx);
void kmscon_context_flush(struct kmscon_context *ctx);
void kmscon_context_viewport(struct kmscon_context *ctx,
unsigned int width, unsigned int height);
void kmscon_context_clear(struct kmscon_context *ctx);
/* compositors */
int kmscon_compositor_new(struct kmscon_compositor **out);

408
src/output_context.c Normal file
View File

@ -0,0 +1,408 @@
/*
* 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>
#include <GL/gl.h>
#include <GL/glext.h>
#include "log.h"
#include "output.h"
struct kmscon_context {
EGLDisplay display;
EGLContext context;
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;
};
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()
{
return glGetError() != GL_NO_ERROR;
}
/*
* 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;
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->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");
if (!ctx->proc_rbuf_storage || !ctx->proc_create_image ||
!ctx->proc_destroy_image) {
log_warning("context: KHR images not supported\n");
ret = -ENOTSUP;
goto err_free;
} else if (!ctx->proc_gen_renderbuffers ||
!ctx->proc_bind_renderbuffer ||
!ctx->proc_delete_renderbuffers ||
!ctx->proc_framebuffer_renderbuffer ||
!ctx->proc_check_framebuffer_status) {
log_warning("context: renderbuffers not supported\n");
ret = -ENOTSUP;
goto err_free;
}
ctx->display = eglGetDisplay((EGLNativeDisplayType) gbm);
if (!ctx->display) {
log_warning("context: cannot get EGL display\n");
ret = -EFAULT;
goto err_free;
}
ret = eglInitialize(ctx->display, &major, &minor);
if (!ret) {
log_warning("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_warning("context: surfaceless EGL not supported\n");
ret = -ENOTSUP;
goto err_display;
}
if (!eglBindAPI(EGL_OPENGL_API)) {
log_warning("context: cannot bind EGL OpenGL API\n");
ret = -EFAULT;
goto err_display;
}
ctx->context = eglCreateContext(ctx->display, NULL, EGL_NO_CONTEXT,
NULL);
if (!ctx->context) {
log_warning("context: cannot create EGL context\n");
ret = -EFAULT;
goto err_display;
}
*out = ctx;
return 0;
err_display:
eglTerminate(ctx->display);
err_free:
free(ctx);
return ret;
}
void kmscon_context_destroy(struct kmscon_context *ctx)
{
if (!ctx)
return;
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_warning("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);
}
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_warning("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_warning("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_warning("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_warning("context: invalid GL framebuffer state\n");
return fb->current_rb;
}