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:
parent
a8dcede8c7
commit
7c7a4e32f9
@ -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 \
|
||||
|
235
src/output.c
235
src/output.c
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
22
src/output.h
22
src/output.h
@ -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
408
src/output_context.c
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user