xserver/hw/kdrive/ephyr/ephyr_glamor.c

441 lines
12 KiB
C

/*
* Copyright © 2013 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*/
/** @file ephyr_glamor.c
*
* Glamor support and EGL setup.
*/
#define MESA_EGL_NO_X11_HEADERS
#define EGL_NO_X11
#include <stdlib.h>
#include <stdint.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <pixman.h>
#include "glamor_context.h"
#include "glamor_egl.h"
#include "glamor_priv.h"
#include "ephyr.h"
#include "ephyr_glamor.h"
#include "os.h"
/* until we need geometry shaders GL3.1 should suffice. */
/* Xephyr has its own copy of this for build reasons */
#define GLAMOR_GL_CORE_VER_MAJOR 3
#define GLAMOR_GL_CORE_VER_MINOR 1
/** @{
*
* global state for Xephyr with glamor, all of which is arguably a bug.
*/
Bool ephyr_glamor_gles2;
Bool ephyr_glamor_skip_present;
/** @} */
/**
* Per-screen state for Xephyr with glamor.
*/
struct ephyr_glamor {
EGLDisplay dpy;
EGLContext ctx;
xcb_window_t win;
EGLSurface egl_win;
GLuint tex;
GLuint texture_shader;
GLuint texture_shader_position_loc;
GLuint texture_shader_texcoord_loc;
/* Size of the window that we're rendering to. */
unsigned width, height;
GLuint vao, vbo;
};
static void
glamor_egl_make_current(struct glamor_context *glamor_ctx)
{
/* There's only a single global dispatch table in Mesa. EGL, GLX,
* and AIGLX's direct dispatch table manipulation don't talk to
* each other. We need to set the context to NULL first to avoid
* EGL's no-op context change fast path when switching back to
* EGL.
*/
eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglMakeCurrent(glamor_ctx->display,
glamor_ctx->surface, glamor_ctx->surface,
glamor_ctx->ctx)) {
FatalError("Failed to make EGL context current\n");
}
}
void
glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
{
KdScreenPriv(screen);
KdScreenInfo *kd_screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = kd_screen->driver;
struct ephyr_glamor *ephyr_glamor = scrpriv->glamor;
glamor_enable_dri3(screen);
glamor_ctx->display = ephyr_glamor->dpy;
glamor_ctx->ctx = ephyr_glamor->ctx;
glamor_ctx->surface = ephyr_glamor->egl_win;
glamor_ctx->make_current = glamor_egl_make_current;
}
int
glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
PixmapPtr pixmap,
CARD16 *stride, CARD32 *size)
{
return -1;
}
int
glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds,
uint32_t *offsets, uint32_t *strides,
uint64_t *modifier)
{
return 0;
}
int
glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
CARD16 *stride, CARD32 *size)
{
return -1;
}
static GLuint
ephyr_glamor_build_glsl_prog(GLuint vs, GLuint fs)
{
GLint ok;
GLuint prog;
prog = glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (!ok) {
GLchar *info;
GLint size;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
info = malloc(size);
glGetProgramInfoLog(prog, size, NULL, info);
ErrorF("Failed to link: %s\n", info);
FatalError("GLSL link failure\n");
}
return prog;
}
static void
ephyr_glamor_setup_texturing_shader(struct ephyr_glamor *glamor)
{
const char *vs_source =
"attribute vec2 texcoord;\n"
"attribute vec2 position;\n"
"varying vec2 t;\n"
"\n"
"void main()\n"
"{\n"
" t = texcoord;\n"
" gl_Position = vec4(position, 0, 1);\n"
"}\n";
const char *fs_source =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"\n"
"varying vec2 t;\n"
"uniform sampler2D s; /* initially 0 */\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(s, t);\n"
"}\n";
GLuint fs, vs, prog;
vs = glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_source);
fs = glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, fs_source);
prog = ephyr_glamor_build_glsl_prog(vs, fs);
glamor->texture_shader = prog;
glamor->texture_shader_position_loc = glGetAttribLocation(prog, "position");
assert(glamor->texture_shader_position_loc != -1);
glamor->texture_shader_texcoord_loc = glGetAttribLocation(prog, "texcoord");
assert(glamor->texture_shader_texcoord_loc != -1);
}
#ifndef EGL_PLATFORM_XCB_EXT
#define EGL_PLATFORM_XCB_EXT 0x31DC
#endif
#include <dlfcn.h>
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT NULL
#endif
/* (loud booing)
*
* keeping this as a static variable is bad form, we _could_ have zaphod heads
* on different displays (for example). but other bits of Xephyr are already
* broken for that case, and fixing that would entail fixing the rest of the
* contortions with hostx.c anyway, so this works for now.
*/
static EGLDisplay edpy = EGL_NO_DISPLAY;
xcb_connection_t *
ephyr_glamor_connect(void)
{
int major = 0, minor = 0;
/*
* Try pure xcb first. If that doesn't work but we can find XOpenDisplay,
* fall back to xlib. This lets us potentially not load libX11 at all, if
* the EGL is also pure xcb.
*/
if (epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_xcb")) {
xcb_connection_t *conn = xcb_connect(NULL, NULL);
EGLDisplay dpy = glamor_egl_get_display(EGL_PLATFORM_XCB_EXT, conn);
if (dpy == EGL_NO_DISPLAY) {
xcb_disconnect(conn);
return NULL;
}
edpy = dpy;
eglInitialize(dpy, &major, &minor);
return conn;
}
if (epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_x11") ||
epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_KHR_platform_x11)")) {
void *lib = NULL;
xcb_connection_t *ret = NULL;
void *(*x_open_display)(void *) =
(void *) dlsym(RTLD_DEFAULT, "XOpenDisplay");
xcb_connection_t *(*x_get_xcb_connection)(void *) =
(void *) dlsym(RTLD_DEFAULT, "XGetXCBConnection");
if (x_open_display == NULL)
return NULL;
if (x_get_xcb_connection == NULL) {
lib = dlopen("libX11-xcb.so.1", RTLD_LOCAL | RTLD_LAZY);
x_get_xcb_connection =
(void *) dlsym(lib, "XGetXCBConnection");
}
if (x_get_xcb_connection == NULL)
goto out;
void *xdpy = x_open_display(NULL);
EGLDisplay dpy = glamor_egl_get_display(EGL_PLATFORM_X11_KHR, xdpy);
if (dpy == EGL_NO_DISPLAY)
goto out;
edpy = dpy;
eglInitialize(dpy, &major, &minor);
ret = x_get_xcb_connection(xdpy);
out:
if (lib)
dlclose(lib);
return ret;
}
return NULL;
}
void
ephyr_glamor_set_texture(struct ephyr_glamor *glamor, uint32_t tex)
{
glamor->tex = tex;
}
static void
ephyr_glamor_set_vertices(struct ephyr_glamor *glamor)
{
glVertexAttribPointer(glamor->texture_shader_position_loc,
2, GL_FLOAT, FALSE, 0, (void *) 0);
glVertexAttribPointer(glamor->texture_shader_texcoord_loc,
2, GL_FLOAT, FALSE, 0, (void *) (sizeof (float) * 8));
glEnableVertexAttribArray(glamor->texture_shader_position_loc);
glEnableVertexAttribArray(glamor->texture_shader_texcoord_loc);
}
void
ephyr_glamor_damage_redisplay(struct ephyr_glamor *glamor,
struct pixman_region16 *damage)
{
GLint old_vao;
/* Skip presenting the output in this mode. Presentation is
* expensive, and if we're just running the X Test suite headless,
* nobody's watching.
*/
if (ephyr_glamor_skip_present)
return;
eglMakeCurrent(glamor->dpy, glamor->egl_win, glamor->egl_win, glamor->ctx);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
glBindVertexArray(glamor->vao);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(glamor->texture_shader);
glViewport(0, 0, glamor->width, glamor->height);
if (!ephyr_glamor_gles2)
glDisable(GL_COLOR_LOGIC_OP);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glamor->tex);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(old_vao);
eglSwapBuffers(glamor->dpy, glamor->egl_win);
}
struct ephyr_glamor *
ephyr_glamor_screen_init(xcb_window_t win, xcb_visualid_t vid)
{
static const float position[] = {
-1, -1,
1, -1,
1, 1,
-1, 1,
0, 1,
1, 1,
1, 0,
0, 0,
};
GLint old_vao;
EGLContext ctx;
struct ephyr_glamor *glamor;
EGLSurface egl_win;
glamor = calloc(1, sizeof(struct ephyr_glamor));
if (!glamor) {
FatalError("malloc");
return NULL;
}
const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NATIVE_VISUAL_ID, vid,
EGL_NONE,
};
EGLConfig config = EGL_NO_CONFIG_KHR;
int num_configs = 0;
/* (loud booing (see above)) */
glamor->dpy = edpy;
eglChooseConfig(glamor->dpy, config_attribs, &config, 1, &num_configs);
if (num_configs != 1)
FatalError("Unable to find an EGLConfig for vid %#x\n", vid);
egl_win = eglCreatePlatformWindowSurfaceEXT(glamor->dpy, config,
&win, NULL);
if (ephyr_glamor_gles2)
eglBindAPI(EGL_OPENGL_ES_API);
else
eglBindAPI(EGL_OPENGL_API);
EGLint context_attribs[5];
int i = 0;
context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
context_attribs[i++] = ephyr_glamor_gles2 ? 2 : 3;
context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
context_attribs[i++] = ephyr_glamor_gles2 ? 0 : 1;
context_attribs[i++] = EGL_NONE;
ctx = eglCreateContext(glamor->dpy, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT,
context_attribs);
if (ctx == NULL)
FatalError("eglCreateContext failed\n");
if (!eglMakeCurrent(glamor->dpy, egl_win, egl_win, ctx))
FatalError("eglMakeCurrent failed\n");
glamor->ctx = ctx;
glamor->win = win;
glamor->egl_win = egl_win;
ephyr_glamor_setup_texturing_shader(glamor);
glGenVertexArrays(1, &glamor->vao);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
glBindVertexArray(glamor->vao);
glGenBuffers(1, &glamor->vbo);
glBindBuffer(GL_ARRAY_BUFFER, glamor->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof (position), position, GL_STATIC_DRAW);
ephyr_glamor_set_vertices(glamor);
glBindVertexArray(old_vao);
return glamor;
}
void
ephyr_glamor_screen_fini(struct ephyr_glamor *glamor)
{
eglMakeCurrent(glamor->dpy,
EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(glamor->dpy, glamor->ctx);
eglDestroySurface(glamor->dpy, glamor->egl_win);
free(glamor);
}
void
ephyr_glamor_set_window_size(struct ephyr_glamor *glamor,
unsigned width, unsigned height)
{
if (!glamor)
return;
glamor->width = width;
glamor->height = height;
}