spice/client/x11/red_pixmap_gl.cpp
Marc-André Lureau b2b3243031 spicec: disable stencil test with primary fbo
The primary buffer doesn't use stencil test. However, this should be
explicitely disabled, since the canvas might change stencil state, and
this will affect primary stencil buffer, making some of further update
operations clipped in unwanted ways.
2013-09-30 02:19:51 +02:00

315 lines
9.7 KiB
C++

/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <X11/Xlib.h>
#include "common/gl_utils.h"
#include "common.h"
#include "red_pixmap_gl.h"
#include "debug.h"
#include "utils.h"
#include "pixels_source_p.h"
#include "x_platform.h"
#include "red_window_p.h"
RedPixmapGL::RedPixmapGL(int width, int height, RedDrawable::Format format,
bool top_bottom, RedWindow *win,
RenderType rendertype)
: RedPixmap(width, height, format, top_bottom)
{
GLuint fbo = 0;
GLuint tex = 0;
GLuint stencil_tex = 0;
GLuint rbo = 0;
Win xwin;
//GLint max_texture_size;
ASSERT(format == RedDrawable::ARGB32 || format == RedDrawable::RGB32 || format == RedDrawable::A1);
ASSERT(sizeof(RedDrawable_p) <= PIXELES_SOURCE_OPAQUE_SIZE);
((PixelsSource_p*)get_opaque())->type = PIXELS_SOURCE_TYPE_GL_TEXTURE;
_glcont = win->create_context_gl();
if (!_glcont) {
THROW("glXCreateContext failed");
}
win->set_gl_context(_glcont);
xwin = ((RedWindow_p*)win)->get_window();
/*glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
if (width > max_texture_size || height > max_texture_size) {
throw Exception(fmt("%s: unsuported max %|| width %|| height %||")
% __FUNCTION__
% max_texture_size
% width
% height);
}*/
if (rendertype == RENDER_TYPE_FBO) {
int w = gl_get_to_power_two(width);
int h = gl_get_to_power_two(height);
glXMakeCurrent(XPlatform::get_display(), xwin, _glcont);
if (!gluCheckExtension((GLubyte *)"GL_EXT_framebuffer_object",
glGetString(GL_EXTENSIONS))) {
glXMakeCurrent(XPlatform::get_display(), 0, 0);
glXDestroyContext(XPlatform::get_display(), _glcont);
THROW("no GL_EXT_framebuffer_object extension");
}
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
THROW("bad fbo status: %d\n", status);
((PixelsSource_p*)get_opaque())->gl.fbo = fbo;
win->set_render_fbo(fbo);
} else if (rendertype == RENDER_TYPE_PBUFF) {
GLXPbuffer pbuff;
pbuff = win->create_pbuff(gl_get_to_power_two(width),
gl_get_to_power_two(height));
if (!pbuff) {
glXDestroyContext(XPlatform::get_display(), _glcont);
THROW("pbuff creation failed");
}
glXMakeCurrent(XPlatform::get_display(), pbuff, _glcont);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, 4, gl_get_to_power_two(width),
gl_get_to_power_two(height), 0, GL_BGRA, GL_UNSIGNED_BYTE,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
((PixelsSource_p*)get_opaque())->gl.pbuff = pbuff;
win->set_render_pbuff(pbuff);
} else {
abort();
}
((PixelsSource_p*)get_opaque())->gl.stencil_tex = stencil_tex;
((PixelsSource_p*)get_opaque())->gl.tex = tex;
((PixelsSource_p*)get_opaque())->gl.width = width;
((PixelsSource_p*)get_opaque())->gl.height = height;
((PixelsSource_p*)get_opaque())->gl.width_powed = gl_get_to_power_two(width);
((PixelsSource_p*)get_opaque())->gl.height_powed = gl_get_to_power_two(height);
((PixelsSource_p*)get_opaque())->gl.win = xwin;
((PixelsSource_p*)get_opaque())->gl.rendertype = rendertype;
((PixelsSource_p*)get_opaque())->gl.context = _glcont;
_textures_lost = false;
GLC_ERROR_TEST_FINISH;
}
void RedPixmapGL::textures_lost()
{
_textures_lost = true;
}
void RedPixmapGL::touch_context()
{
Win win;
GLXPbuffer pbuff;
RenderType rendertype;
rendertype = ((PixelsSource_p*)get_opaque())->gl.rendertype;
if (rendertype == RENDER_TYPE_FBO) {
win = ((PixelsSource_p*)get_opaque())->gl.win;
if (_glcont) {
glXMakeCurrent(XPlatform::get_display(), win, _glcont);
}
} else {
pbuff = ((PixelsSource_p*)get_opaque())->gl.pbuff;
glXMakeCurrent(XPlatform::get_display(), pbuff, _glcont);
}
GLC_ERROR_TEST_FLUSH;
}
void RedPixmapGL::update_texture(const SpiceRect *bbox)
{
RenderType rendertype;
GLuint tex;
int height;
rendertype = ((PixelsSource_p*)get_opaque())->gl.rendertype;
if (rendertype == RENDER_TYPE_PBUFF) {
int tex_x, tex_y;
int vertex_x1, vertex_x2;
int vertex_y1, vertex_y2;
int is_enabled;
GLint prev_tex;
height = ((PixelsSource_p*)get_opaque())->gl.height;
tex = ((PixelsSource_p*)get_opaque())->gl.tex;
tex_x = bbox->left;
tex_y = height - bbox->bottom;
vertex_x1 = bbox->left;
vertex_y1 = height - bbox->bottom;
vertex_x2 = vertex_x1 + (bbox->right - bbox->left);
vertex_y2 = vertex_y1 + (bbox->bottom - bbox->top);
is_enabled = glIsEnabled(GL_TEXTURE_2D);
if (!is_enabled) {
glEnable(GL_TEXTURE_2D);
} else {
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex);
}
glBindTexture(GL_TEXTURE_2D, tex);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, tex_x, tex_y, vertex_x1,
vertex_y1, vertex_x2 - vertex_x1,
vertex_y2 - vertex_y1);
if (!is_enabled) {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
} else {
glBindTexture(GL_TEXTURE_2D, prev_tex);
}
}
GLC_ERROR_TEST_FLUSH;
}
void RedPixmapGL::pre_copy()
{
glFlush();
glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TEXTURE_BIT |
GL_TRANSFORM_BIT);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, ((PixelsSource_p*)get_opaque())->gl.width, 0,
((PixelsSource_p*)get_opaque())->gl.height);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glViewport(0, 0, ((PixelsSource_p*)get_opaque())->gl.width,
((PixelsSource_p*)get_opaque())->gl.height);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDisable(GL_COLOR_LOGIC_OP);
glDisable(GL_STENCIL_TEST);
glColor3f(1, 1, 1);
}
void RedPixmapGL::past_copy()
{
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glPopAttrib();
glFlush();
}
RedPixmapGL::~RedPixmapGL()
{
GLXPbuffer pbuff;
GLuint fbo, rbo;
RenderType rendertype;
GLuint tex;
GLuint stencil_tex;
rendertype = ((PixelsSource_p*)get_opaque())->gl.rendertype;
if (rendertype == RENDER_TYPE_FBO) {
fbo = ((PixelsSource_p*)get_opaque())->gl.fbo;
rbo = ((PixelsSource_p*)get_opaque())->gl.rbo;
spice_debug("deletefbo %u", fbo);
if (fbo) {
glDeleteFramebuffers(1, &fbo);
}
if (rbo) {
glDeleteRenderbuffers(1, &rbo);
}
} else {
pbuff = ((PixelsSource_p*)get_opaque())->gl.pbuff;
glXDestroyPbuffer(XPlatform::get_display(), pbuff);
}
/*
* GL textures might be destroyed by res change.
*/
if (!_textures_lost) {
tex = ((PixelsSource_p*)get_opaque())->gl.tex;
stencil_tex = ((PixelsSource_p*)get_opaque())->gl.stencil_tex;
if (tex) {
glDeleteTextures(1, &tex);
}
if (stencil_tex) {
glDeleteTextures(1, &stencil_tex);
}
if (_glcont) {
glXDestroyContext(XPlatform::get_display(), _glcont);
}
}
/*
* Both tex and stenctil_tex are textures and therefore they are destroyed
* when the context is gone, so we dont free them here as they might have
* already been destroyed.
*/
GLC_ERROR_TEST_FINISH;
}