330d40f425
* "" needs to be wrapped in QStringLiteral * QString::fromUtf8 needed for const char* and QByteArray * QByteArray::constData() needed to get to the const char*
630 lines
21 KiB
C++
630 lines
21 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
|
|
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
Based on glcompmgr code by Felix Bellaby.
|
|
Using code from Compiz and Beryl.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
|
|
// TODO: cmake magic
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
// own
|
|
#include "glxbackend.h"
|
|
// kwin
|
|
#include "options.h"
|
|
#include "utils.h"
|
|
#include "overlaywindow.h"
|
|
// kwin libs
|
|
#include <kwinglplatform.h>
|
|
// KDE
|
|
#include <KDE/KDebug>
|
|
#include <KDE/KXErrorHandler>
|
|
|
|
namespace KWin
|
|
{
|
|
GlxBackend::GlxBackend()
|
|
: OpenGLBackend()
|
|
, window(None)
|
|
, fbconfig(NULL)
|
|
, glxWindow(None)
|
|
, ctx(nullptr)
|
|
, haveSwapInterval(false)
|
|
{
|
|
init();
|
|
}
|
|
|
|
GlxBackend::~GlxBackend()
|
|
{
|
|
// TODO: cleanup in error case
|
|
// do cleanup after initBuffer()
|
|
cleanupGL();
|
|
glXMakeCurrent(display(), None, NULL);
|
|
|
|
if (ctx)
|
|
glXDestroyContext(display(), ctx);
|
|
|
|
if (glxWindow)
|
|
glXDestroyWindow(display(), glxWindow);
|
|
|
|
if (window)
|
|
XDestroyWindow(display(), window);
|
|
|
|
overlayWindow()->destroy();
|
|
checkGLError("Cleanup");
|
|
}
|
|
|
|
static bool gs_tripleBufferUndetected = true;
|
|
static bool gs_tripleBufferNeedsDetection = false;
|
|
|
|
void GlxBackend::init()
|
|
{
|
|
initGLX();
|
|
// require at least GLX 1.3
|
|
if (!hasGLXVersion(1, 3)) {
|
|
setFailed(QStringLiteral("Requires at least GLX 1.3"));
|
|
return;
|
|
}
|
|
if (!initDrawableConfigs()) {
|
|
setFailed(QStringLiteral("Could not initialize the drawable configs"));
|
|
return;
|
|
}
|
|
if (!initBuffer()) {
|
|
setFailed(QStringLiteral("Could not initialize the buffer"));
|
|
return;
|
|
}
|
|
if (!initRenderingContext()) {
|
|
setFailed(QStringLiteral("Could not initialize rendering context"));
|
|
return;
|
|
}
|
|
// Initialize OpenGL
|
|
GLPlatform *glPlatform = GLPlatform::instance();
|
|
glPlatform->detect(GlxPlatformInterface);
|
|
glPlatform->printResults();
|
|
initGL(GlxPlatformInterface);
|
|
// Check whether certain features are supported
|
|
haveSwapInterval = glXSwapIntervalMESA || glXSwapIntervalEXT || glXSwapIntervalSGI;
|
|
setSyncsToVBlank(false);
|
|
setBlocksForRetrace(false);
|
|
haveWaitSync = false;
|
|
gs_tripleBufferNeedsDetection = false;
|
|
m_swapProfiler.init();
|
|
const bool wantSync = options->glPreferBufferSwap() != Options::NoSwapEncourage;
|
|
if (wantSync && glXIsDirect(display(), ctx)) {
|
|
if (haveSwapInterval) { // glXSwapInterval is preferred being more reliable
|
|
setSwapInterval(1);
|
|
setSyncsToVBlank(true);
|
|
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
|
|
if (!tripleBuffer.isEmpty()) {
|
|
setBlocksForRetrace(qstrcmp(tripleBuffer, "0") == 0);
|
|
gs_tripleBufferUndetected = false;
|
|
}
|
|
gs_tripleBufferNeedsDetection = gs_tripleBufferUndetected;
|
|
} else if (glXGetVideoSync) {
|
|
unsigned int sync;
|
|
if (glXGetVideoSync(&sync) == 0 && glXWaitVideoSync(1, 0, &sync) == 0) {
|
|
setSyncsToVBlank(true);
|
|
setBlocksForRetrace(true);
|
|
haveWaitSync = true;
|
|
} else
|
|
qWarning() << "NO VSYNC! glXSwapInterval is not supported, glXWaitVideoSync is supported but broken";
|
|
} else
|
|
qWarning() << "NO VSYNC! neither glSwapInterval nor glXWaitVideoSync are supported";
|
|
} else {
|
|
// disable v-sync (if possible)
|
|
setSwapInterval(0);
|
|
}
|
|
if (glPlatform->isVirtualBox()) {
|
|
// VirtualBox does not support glxQueryDrawable
|
|
// this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
|
|
// and the GLPlatform has not been initialized at the moment when initGLX() is called.
|
|
glXQueryDrawable = NULL;
|
|
}
|
|
|
|
setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
|
|
|
|
kDebug(1212) << "Direct rendering:" << isDirectRendering() << endl;
|
|
}
|
|
|
|
bool GlxBackend::initRenderingContext()
|
|
{
|
|
bool direct = options->isGlDirect();
|
|
|
|
// Use glXCreateContextAttribsARB() when it's available
|
|
if (glXCreateContextAttribsARB) {
|
|
const int attribs_31_core_robustness[] = {
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
|
|
GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB,
|
|
0
|
|
};
|
|
|
|
const int attribs_31_core[] = {
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
const int attribs_legacy_robustness[] = {
|
|
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
|
|
GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB,
|
|
0
|
|
};
|
|
|
|
const int attribs_legacy[] = {
|
|
0
|
|
};
|
|
|
|
const bool have_robustness = hasGLExtension(QStringLiteral("GLX_ARB_create_context_robustness"));
|
|
|
|
// Try to create a 3.1 context first
|
|
if (options->glCoreProfile()) {
|
|
if (have_robustness)
|
|
ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_31_core_robustness);
|
|
|
|
if (!ctx)
|
|
ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_31_core);
|
|
}
|
|
|
|
if (!ctx && have_robustness)
|
|
ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_legacy_robustness);
|
|
|
|
if (!ctx)
|
|
ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_legacy);
|
|
}
|
|
|
|
if (!ctx)
|
|
ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, NULL, direct);
|
|
|
|
if (!ctx) {
|
|
kDebug(1212) << "Failed to create an OpenGL context.";
|
|
return false;
|
|
}
|
|
|
|
if (!glXMakeCurrent(display(), glxWindow, ctx)) {
|
|
kDebug(1212) << "Failed to make the OpenGL context current.";
|
|
glXDestroyContext(display(), ctx);
|
|
ctx = 0;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GlxBackend::initBuffer()
|
|
{
|
|
if (!initFbConfig())
|
|
return false;
|
|
|
|
if (overlayWindow()->create()) {
|
|
// Try to create double-buffered window in the overlay
|
|
XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbconfig);
|
|
XSetWindowAttributes attrs;
|
|
attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone);
|
|
window = XCreateWindow(display(), overlayWindow()->window(), 0, 0, displayWidth(), displayHeight(),
|
|
0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs);
|
|
glxWindow = glXCreateWindow(display(), fbconfig, window, NULL);
|
|
overlayWindow()->setup(window);
|
|
XFree(visual);
|
|
} else {
|
|
kError(1212) << "Failed to create overlay window";
|
|
return false;
|
|
}
|
|
|
|
int vis_buffer;
|
|
glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, &vis_buffer);
|
|
XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig(display(), fbconfig);
|
|
kDebug(1212) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number(vis_buffer, 16);
|
|
XFree(visinfo_buffer);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GlxBackend::initFbConfig()
|
|
{
|
|
const int attribs[] = {
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
GLX_RED_SIZE, 1,
|
|
GLX_GREEN_SIZE, 1,
|
|
GLX_BLUE_SIZE, 1,
|
|
GLX_ALPHA_SIZE, 0,
|
|
GLX_DEPTH_SIZE, 0,
|
|
GLX_STENCIL_SIZE, 0,
|
|
GLX_CONFIG_CAVEAT, GLX_NONE,
|
|
GLX_DOUBLEBUFFER, true,
|
|
0
|
|
};
|
|
|
|
// Try to find a double buffered configuration
|
|
int count = 0;
|
|
GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
|
|
|
|
if (count > 0) {
|
|
fbconfig = configs[0];
|
|
XFree(configs);
|
|
}
|
|
|
|
if (fbconfig == NULL) {
|
|
kError(1212) << "Failed to find a usable framebuffer configuration";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GlxBackend::initDrawableConfigs()
|
|
{
|
|
const int attribs[] = {
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
|
|
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
|
GLX_X_RENDERABLE, True,
|
|
GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), // The ARGB32 visual is marked non-conformant in Catalyst
|
|
GLX_RED_SIZE, 5,
|
|
GLX_GREEN_SIZE, 5,
|
|
GLX_BLUE_SIZE, 5,
|
|
GLX_ALPHA_SIZE, 0,
|
|
GLX_STENCIL_SIZE, 0,
|
|
GLX_DEPTH_SIZE, 0,
|
|
0
|
|
};
|
|
|
|
int count = 0;
|
|
GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
|
|
|
|
if (count < 1) {
|
|
kError(1212) << "Could not find any usable framebuffer configurations.";
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i <= 32; i++) {
|
|
fbcdrawableinfo[i].fbconfig = NULL;
|
|
fbcdrawableinfo[i].bind_texture_format = 0;
|
|
fbcdrawableinfo[i].texture_targets = 0;
|
|
fbcdrawableinfo[i].y_inverted = 0;
|
|
fbcdrawableinfo[i].mipmap = 0;
|
|
}
|
|
|
|
// Find the first usable framebuffer configuration for each depth.
|
|
// Single-buffered ones will appear first in the list.
|
|
const int depths[] = { 15, 16, 24, 30, 32 };
|
|
for (unsigned int i = 0; i < sizeof(depths) / sizeof(depths[0]); i++) {
|
|
const int depth = depths[i];
|
|
|
|
for (int j = 0; j < count; j++) {
|
|
int alpha_size, buffer_size;
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_ALPHA_SIZE, &alpha_size);
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BUFFER_SIZE, &buffer_size);
|
|
|
|
if (buffer_size != depth && (buffer_size - alpha_size) != depth)
|
|
continue;
|
|
|
|
if (depth == 32 && alpha_size != 8)
|
|
continue;
|
|
|
|
XVisualInfo *vi = glXGetVisualFromFBConfig(display(), configs[j]);
|
|
if (vi == NULL)
|
|
continue;
|
|
|
|
int visual_depth = vi->depth;
|
|
XFree(vi);
|
|
|
|
if (visual_depth != depth)
|
|
continue;
|
|
|
|
int bind_rgb, bind_rgba;
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
|
|
|
|
// Skip this config if it cannot be bound to a texture
|
|
if (!bind_rgb && !bind_rgba)
|
|
continue;
|
|
|
|
int texture_format;
|
|
if (depth == 32)
|
|
texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
|
|
else
|
|
texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
|
|
|
|
int y_inverted, texture_targets;
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_Y_INVERTED_EXT, &y_inverted);
|
|
|
|
fbcdrawableinfo[depth].fbconfig = configs[j];
|
|
fbcdrawableinfo[depth].bind_texture_format = texture_format;
|
|
fbcdrawableinfo[depth].texture_targets = texture_targets;
|
|
fbcdrawableinfo[depth].y_inverted = y_inverted;
|
|
fbcdrawableinfo[depth].mipmap = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count)
|
|
XFree(configs);
|
|
|
|
if (fbcdrawableinfo[DefaultDepth(display(), DefaultScreen(display()))].fbconfig == NULL) {
|
|
kError(1212) << "Could not find a framebuffer configuration for the default depth.";
|
|
return false;
|
|
}
|
|
|
|
if (fbcdrawableinfo[32].fbconfig == NULL) {
|
|
kError(1212) << "Could not find a framebuffer configuration for depth 32.";
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i <= 32; i++) {
|
|
if (fbcdrawableinfo[i].fbconfig == NULL)
|
|
continue;
|
|
|
|
int vis_drawable = 0;
|
|
glXGetFBConfigAttrib(display(), fbcdrawableinfo[i].fbconfig, GLX_VISUAL_ID, &vis_drawable);
|
|
|
|
kDebug(1212) << "Drawable visual (depth " << i << "): 0x" << QString::number(vis_drawable, 16);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GlxBackend::setSwapInterval(int interval)
|
|
{
|
|
if (glXSwapIntervalEXT)
|
|
glXSwapIntervalEXT(display(), glxWindow, interval);
|
|
else if (glXSwapIntervalMESA)
|
|
glXSwapIntervalMESA(interval);
|
|
else if (glXSwapIntervalSGI)
|
|
glXSwapIntervalSGI(interval);
|
|
}
|
|
|
|
void GlxBackend::waitSync()
|
|
{
|
|
// NOTE that vsync has no effect with indirect rendering
|
|
if (haveWaitSync) {
|
|
uint sync;
|
|
#if 0
|
|
// TODO: why precisely is this important?
|
|
// the sync counter /can/ perform multiple steps during glXGetVideoSync & glXWaitVideoSync
|
|
// but this only leads to waiting for two frames??!?
|
|
glXGetVideoSync(&sync);
|
|
glXWaitVideoSync(2, (sync + 1) % 2, &sync);
|
|
#else
|
|
glXWaitVideoSync(1, 0, &sync);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void GlxBackend::present()
|
|
{
|
|
if (lastDamage().isEmpty())
|
|
return;
|
|
|
|
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
|
|
const bool fullRepaint = (lastDamage() == displayRegion);
|
|
|
|
if (fullRepaint) {
|
|
if (haveSwapInterval) {
|
|
if (gs_tripleBufferNeedsDetection) {
|
|
glXWaitGL();
|
|
m_swapProfiler.begin();
|
|
}
|
|
glXSwapBuffers(display(), glxWindow);
|
|
if (gs_tripleBufferNeedsDetection) {
|
|
glXWaitGL();
|
|
if (char result = m_swapProfiler.end()) {
|
|
gs_tripleBufferUndetected = gs_tripleBufferNeedsDetection = false;
|
|
setBlocksForRetrace(result == 'd');
|
|
}
|
|
}
|
|
} else {
|
|
waitSync();
|
|
glXSwapBuffers(display(), glxWindow);
|
|
}
|
|
} else if (glXCopySubBuffer) {
|
|
foreach (const QRect & r, lastDamage().rects()) {
|
|
// convert to OpenGL coordinates
|
|
int y = displayHeight() - r.y() - r.height();
|
|
glXCopySubBuffer(display(), glxWindow, r.x(), y, r.width(), r.height());
|
|
}
|
|
} else { // Copy Pixels (horribly slow on Mesa)
|
|
glDrawBuffer(GL_FRONT);
|
|
SceneOpenGL::copyPixels(lastDamage());
|
|
glDrawBuffer(GL_BACK);
|
|
}
|
|
|
|
glXWaitGL();
|
|
setLastDamage(QRegion());
|
|
XFlush(display());
|
|
}
|
|
|
|
void GlxBackend::screenGeometryChanged(const QSize &size)
|
|
{
|
|
glXMakeCurrent(display(), None, NULL);
|
|
|
|
XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
|
|
overlayWindow()->setup(window);
|
|
XSync(display(), false);
|
|
|
|
glXMakeCurrent(display(), glxWindow, ctx);
|
|
glViewport(0, 0, size.width(), size.height());
|
|
}
|
|
|
|
SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture)
|
|
{
|
|
return new GlxTexture(texture, this);
|
|
}
|
|
|
|
void GlxBackend::prepareRenderingFrame()
|
|
{
|
|
present();
|
|
startRenderTimer();
|
|
glXWaitX();
|
|
}
|
|
|
|
void GlxBackend::endRenderingFrame(const QRegion &damage)
|
|
{
|
|
setLastDamage(damage);
|
|
glFlush();
|
|
if (!blocksForRetrace()) {
|
|
present(); // this sets lastDamage emtpy and prevents execution from prepareRenderingFrame()
|
|
}
|
|
|
|
if (overlayWindow()->window()) // show the window only after the first pass,
|
|
overlayWindow()->show(); // since that pass may take long
|
|
}
|
|
|
|
|
|
/********************************************************
|
|
* GlxTexture
|
|
*******************************************************/
|
|
GlxTexture::GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend)
|
|
: SceneOpenGL::TexturePrivate()
|
|
, q(texture)
|
|
, m_backend(backend)
|
|
, m_glxpixmap(None)
|
|
{
|
|
}
|
|
|
|
GlxTexture::~GlxTexture()
|
|
{
|
|
if (m_glxpixmap != None) {
|
|
if (!options->isGlStrictBinding()) {
|
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
|
}
|
|
glXDestroyPixmap(display(), m_glxpixmap);
|
|
m_glxpixmap = None;
|
|
}
|
|
}
|
|
|
|
void GlxTexture::onDamage()
|
|
{
|
|
if (options->isGlStrictBinding() && m_glxpixmap) {
|
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
|
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
|
}
|
|
GLTexturePrivate::onDamage();
|
|
}
|
|
|
|
void GlxTexture::findTarget()
|
|
{
|
|
unsigned int new_target = 0;
|
|
if (glXQueryDrawable && m_glxpixmap != None)
|
|
glXQueryDrawable(display(), m_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
|
|
// HACK: this used to be a hack for Xgl.
|
|
// without this hack the NVIDIA blob aborts when trying to bind a texture from
|
|
// a pixmap icon
|
|
if (new_target == 0) {
|
|
if (GLTexture::NPOTTextureSupported() ||
|
|
(isPowerOfTwo(m_size.width()) && isPowerOfTwo(m_size.height()))) {
|
|
new_target = GLX_TEXTURE_2D_EXT;
|
|
} else {
|
|
new_target = GLX_TEXTURE_RECTANGLE_EXT;
|
|
}
|
|
}
|
|
switch(new_target) {
|
|
case GLX_TEXTURE_2D_EXT:
|
|
m_target = GL_TEXTURE_2D;
|
|
m_scale.setWidth(1.0f / m_size.width());
|
|
m_scale.setHeight(1.0f / m_size.height());
|
|
break;
|
|
case GLX_TEXTURE_RECTANGLE_EXT:
|
|
m_target = GL_TEXTURE_RECTANGLE_ARB;
|
|
m_scale.setWidth(1.0f);
|
|
m_scale.setHeight(1.0f);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
bool GlxTexture::loadTexture(const Pixmap& pix, const QSize& size, int depth)
|
|
{
|
|
#ifdef CHECK_GL_ERROR
|
|
checkGLError("TextureLoad1");
|
|
#endif
|
|
if (pix == None || size.isEmpty() || depth < 1)
|
|
return false;
|
|
if (m_backend->fbcdrawableinfo[ depth ].fbconfig == NULL) {
|
|
kDebug(1212) << "No framebuffer configuration for depth " << depth
|
|
<< "; not binding pixmap" << endl;
|
|
return false;
|
|
}
|
|
|
|
m_size = size;
|
|
// new texture, or texture contents changed; mipmaps now invalid
|
|
q->setDirty();
|
|
|
|
#ifdef CHECK_GL_ERROR
|
|
checkGLError("TextureLoad2");
|
|
#endif
|
|
// tfp mode, simply bind the pixmap to texture
|
|
glGenTextures(1, &m_texture);
|
|
// The GLX pixmap references the contents of the original pixmap, so it doesn't
|
|
// need to be recreated when the contents change.
|
|
// The texture may or may not use the same storage depending on the EXT_tfp
|
|
// implementation. When options->glStrictBinding is true, the texture uses
|
|
// a different storage and needs to be updated with a call to
|
|
// glXBindTexImageEXT() when the contents of the pixmap has changed.
|
|
int attrs[] = {
|
|
GLX_TEXTURE_FORMAT_EXT, m_backend->fbcdrawableinfo[ depth ].bind_texture_format,
|
|
GLX_MIPMAP_TEXTURE_EXT, m_backend->fbcdrawableinfo[ depth ].mipmap > 0,
|
|
None, None, None
|
|
};
|
|
// Specifying the texture target explicitly is reported to cause a performance
|
|
// regression with R300G (see bug #256654).
|
|
if (GLPlatform::instance()->driver() != Driver_R300G) {
|
|
if ((m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
|
|
(GLTexture::NPOTTextureSupported() ||
|
|
(isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
|
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
|
attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
|
|
} else if (m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
|
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
|
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
|
|
}
|
|
}
|
|
m_glxpixmap = glXCreatePixmap(display(), m_backend->fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
|
|
#ifdef CHECK_GL_ERROR
|
|
checkGLError("TextureLoadTFP1");
|
|
#endif
|
|
findTarget();
|
|
m_yInverted = m_backend->fbcdrawableinfo[ depth ].y_inverted ? true : false;
|
|
m_canUseMipmaps = m_backend->fbcdrawableinfo[ depth ].mipmap > 0;
|
|
q->setFilter(m_backend->fbcdrawableinfo[ depth ].mipmap > 0 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST);
|
|
glBindTexture(m_target, m_texture);
|
|
#ifdef CHECK_GL_ERROR
|
|
checkGLError("TextureLoadTFP2");
|
|
#endif
|
|
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
|
#ifdef CHECK_GL_ERROR
|
|
checkGLError("TextureLoad0");
|
|
#endif
|
|
|
|
updateMatrix();
|
|
|
|
unbind();
|
|
return true;
|
|
}
|
|
|
|
OpenGLBackend *GlxTexture::backend()
|
|
{
|
|
return m_backend;
|
|
}
|
|
|
|
} // namespace
|
|
#endif
|