1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
6Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
7
8Based on glcompmgr code by Felix Bellaby.
9Using code from Compiz and Beryl.
10
11This program is free software; you can redistribute it and/or modify
12it under the terms of the GNU General Public License as published by
13the Free Software Foundation; either version 2 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program. If not, see <http://www.gnu.org/licenses/>.
23*********************************************************************/
24
25// TODO: cmake magic
26#ifndef KWIN_HAVE_OPENGLES
27// own
28#include "glxbackend.h"
29// kwin
30#include "options.h"
31#include "utils.h"
32#include "overlaywindow.h"
33// kwin libs
34#include <kwinglplatform.h>
35// KDE
36#include <KDE/KDebug>
37#include <KDE/KXErrorHandler>
38// system
39#include <unistd.h>
40
41namespace KWin
42{
43GlxBackend::GlxBackend()
44 : OpenGLBackend()
45 , window(None)
46 , fbconfig(NULL)
47 , glxWindow(None)
48 , ctx(None)
49 , m_bufferAge(0)
50 , haveSwapInterval(false)
51{
52 init();
53}
54
55GlxBackend::~GlxBackend()
56{
57 // TODO: cleanup in error case
58 // do cleanup after initBuffer()
59 cleanupGL();
60 checkGLError("Cleanup");
61 glXMakeCurrent(display(), None, NULL);
62
63 if (ctx)
64 glXDestroyContext(display(), ctx);
65
66 if (glxWindow)
67 glXDestroyWindow(display(), glxWindow);
68
69 if (window)
70 XDestroyWindow(display(), window);
71
72 overlayWindow()->destroy();
73}
74
75static bool gs_tripleBufferUndetected = true;
76static bool gs_tripleBufferNeedsDetection = false;
77
78void GlxBackend::init()
79{
80 initGLX();
81 // require at least GLX 1.3
82 if (!hasGLXVersion(1, 3)) {
83 setFailed("Requires at least GLX 1.3");
84 return;
85 }
86 if (!initDrawableConfigs()) {
87 setFailed("Could not initialize the drawable configs");
88 return;
89 }
90 if (!initBuffer()) {
91 setFailed("Could not initialize the buffer");
92 return;
93 }
94 if (!initRenderingContext()) {
95 setFailed("Could not initialize rendering context");
96 return;
97 }
98 // Initialize OpenGL
99 GLPlatform *glPlatform = GLPlatform::instance();
100 glPlatform->detect(GlxPlatformInterface);
101 if (GLPlatform::instance()->driver() == Driver_Intel)
102 options->setUnredirectFullscreen(false); // bug #252817
103 options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting
104 if (options->glPreferBufferSwap() == Options::AutoSwapStrategy)
105 options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen
106 glPlatform->printResults();
107 initGL(GlxPlatformInterface);
108
109 // Check whether certain features are supported
110 haveSwapInterval = glXSwapIntervalMESA || glXSwapIntervalEXT || glXSwapIntervalSGI;
111
112 setSupportsBufferAge(false);
113
114 if (hasGLExtension("GLX_EXT_buffer_age")) {
115 const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
116
117 if (useBufferAge != "0")
118 setSupportsBufferAge(true);
119 }
120
121 setSyncsToVBlank(false);
122 setBlocksForRetrace(false);
123 haveWaitSync = false;
124 gs_tripleBufferNeedsDetection = false;
125 m_swapProfiler.init();
126 const bool wantSync = options->glPreferBufferSwap() != Options::NoSwapEncourage;
127 if (wantSync && glXIsDirect(display(), ctx)) {
128 if (haveSwapInterval) { // glXSwapInterval is preferred being more reliable
129 setSwapInterval(1);
130 setSyncsToVBlank(true);
131 const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
132 if (!tripleBuffer.isEmpty()) {
133 setBlocksForRetrace(qstrcmp(tripleBuffer, "0") == 0);
134 gs_tripleBufferUndetected = false;
135 }
136 gs_tripleBufferNeedsDetection = gs_tripleBufferUndetected;
137 } else if (glXGetVideoSync) {
138 unsigned int sync;
139 if (glXGetVideoSync(&sync) == 0 && glXWaitVideoSync(1, 0, &sync) == 0) {
140 setSyncsToVBlank(true);
141 setBlocksForRetrace(true);
142 haveWaitSync = true;
143 } else
144 qWarning() << "NO VSYNC! glXSwapInterval is not supported, glXWaitVideoSync is supported but broken";
145 } else
146 qWarning() << "NO VSYNC! neither glSwapInterval nor glXWaitVideoSync are supported";
147 } else {
148 // disable v-sync (if possible)
149 setSwapInterval(0);
150 }
151 if (glPlatform->isVirtualBox()) {
152 // VirtualBox does not support glxQueryDrawable
153 // this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
154 // and the GLPlatform has not been initialized at the moment when initGLX() is called.
155 glXQueryDrawable = NULL;
156 }
157
158 setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
159
160 kDebug(1212) << "Direct rendering:" << isDirectRendering() << endl;
161}
162
163bool GlxBackend::initRenderingContext()
164{
165 bool direct = options->isGlDirect();
166
167 // Use glXCreateContextAttribsARB() when it's available
168 if (glXCreateContextAttribsARB) {
169 const int attribs_31_core_robustness[] = {
170 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
171 GLX_CONTEXT_MINOR_VERSION_ARB, 1,
172 GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
173 GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB,
174 0
175 };
176
177 const int attribs_31_core[] = {
178 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
179 GLX_CONTEXT_MINOR_VERSION_ARB, 1,
180 0
181 };
182
183 const int attribs_legacy_robustness[] = {
184 GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
185 GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB,
186 0
187 };
188
189 const int attribs_legacy[] = {
190 GLX_CONTEXT_MAJOR_VERSION_ARB, 1,
191 GLX_CONTEXT_MINOR_VERSION_ARB, 2,
192 0
193 };
194
195 const bool have_robustness = hasGLExtension("GLX_ARB_create_context_robustness");
196
197 // Try to create a 3.1 context first
198 if (options->glCoreProfile()) {
199 if (have_robustness)
200 ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_31_core_robustness);
201
202 if (!ctx)
203 ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_31_core);
204 }
205
206 if (!ctx && have_robustness)
207 ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_legacy_robustness);
208
209 if (!ctx)
210 ctx = glXCreateContextAttribsARB(display(), fbconfig, 0, direct, attribs_legacy);
211 }
212
213 if (!ctx)
214 ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, NULL, direct);
215
216 if (!ctx) {
217 kDebug(1212) << "Failed to create an OpenGL context.";
218 return false;
219 }
220
221 if (!glXMakeCurrent(display(), glxWindow, ctx)) {
222 kDebug(1212) << "Failed to make the OpenGL context current.";
223 glXDestroyContext(display(), ctx);
224 ctx = 0;
225 return false;
226 }
227
228 return true;
229}
230
231bool GlxBackend::initBuffer()
232{
233 if (!initFbConfig())
234 return false;
235
236 if (overlayWindow()->create()) {
237 // Try to create double-buffered window in the overlay
238 XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbconfig);
239 if (!visual) {
240 kError(1212) << "Failed to get visual from fbconfig";
241 return false;
242 }
243 XSetWindowAttributes attrs;
244 attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone);
245 window = XCreateWindow(display(), overlayWindow()->window(), 0, 0, displayWidth(), displayHeight(),
246 0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs);
247 glxWindow = glXCreateWindow(display(), fbconfig, window, NULL);
248 overlayWindow()->setup(window);
249 XFree(visual);
250 } else {
251 kError(1212) << "Failed to create overlay window";
252 return false;
253 }
254
255 int vis_buffer;
256 glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, &vis_buffer);
257 XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig(display(), fbconfig);
258 kDebug(1212) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number(vis_buffer, 16);
259 XFree(visinfo_buffer);
260
261 return true;
262}
263
264bool GlxBackend::initFbConfig()
265{
266 const int attribs[] = {
267 GLX_RENDER_TYPE, GLX_RGBA_BIT,
268 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
269 GLX_RED_SIZE, 1,
270 GLX_GREEN_SIZE, 1,
271 GLX_BLUE_SIZE, 1,
272 GLX_ALPHA_SIZE, 0,
273 GLX_DEPTH_SIZE, 0,
274 GLX_STENCIL_SIZE, 0,
275 GLX_CONFIG_CAVEAT, GLX_NONE,
276 GLX_DOUBLEBUFFER, true,
277 0
278 };
279
280 // Try to find a double buffered configuration
281 int count = 0;
282 GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
283
284 if (count > 0) {
285 fbconfig = configs[0];
286 XFree(configs);
287 }
288
289 if (fbconfig == NULL) {
290 kError(1212) << "Failed to find a usable framebuffer configuration";
291 return false;
292 }
293
294 return true;
295}
296
297bool GlxBackend::initDrawableConfigs()
298{
299 const int attribs[] = {
300 GLX_RENDER_TYPE, GLX_RGBA_BIT,
301 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
302 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
303 GLX_X_RENDERABLE, True,
304 GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), // The ARGB32 visual is marked non-conformant in Catalyst
305 GLX_RED_SIZE, 5,
306 GLX_GREEN_SIZE, 5,
307 GLX_BLUE_SIZE, 5,
308 GLX_ALPHA_SIZE, 0,
309 GLX_STENCIL_SIZE, 0,
310 GLX_DEPTH_SIZE, 0,
311 0
312 };
313
314 int count = 0;
315 GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
316
317 if (count < 1) {
318 kError(1212) << "Could not find any usable framebuffer configurations.";
319 return false;
320 }
321
322 for (int i = 0; i <= 32; i++) {
323 fbcdrawableinfo[i].fbconfig = NULL;
324 fbcdrawableinfo[i].bind_texture_format = 0;
325 fbcdrawableinfo[i].texture_targets = 0;
326 fbcdrawableinfo[i].y_inverted = 0;
327 fbcdrawableinfo[i].mipmap = 0;
328 }
329
330 // Find the first usable framebuffer configuration for each depth.
331 // Single-buffered ones will appear first in the list.
332 const int depths[] = { 15, 16, 24, 30, 32 };
333 for (unsigned int i = 0; i < sizeof(depths) / sizeof(depths[0]); i++) {
334 const int depth = depths[i];
335
336 for (int j = 0; j < count; j++) {
337 int alpha_size, buffer_size;
338 glXGetFBConfigAttrib(display(), configs[j], GLX_ALPHA_SIZE, &alpha_size);
339 glXGetFBConfigAttrib(display(), configs[j], GLX_BUFFER_SIZE, &buffer_size);
340
341 if (buffer_size != depth && (buffer_size - alpha_size) != depth)
342 continue;
343
344 if (depth == 32 && alpha_size != 8)
345 continue;
346
347 XVisualInfo *vi = glXGetVisualFromFBConfig(display(), configs[j]);
348 if (vi == NULL)
349 continue;
350
351 int visual_depth = vi->depth;
352 XFree(vi);
353
354 if (visual_depth != depth)
355 continue;
356
357 int bind_rgb, bind_rgba;
358 glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
359 glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
360
361 // Skip this config if it cannot be bound to a texture
362 if (!bind_rgb && !bind_rgba)
363 continue;
364
365 int texture_format;
366 if (depth == 32)
367 texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
368 else
369 texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
370
371 int y_inverted, texture_targets;
372 glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
373 glXGetFBConfigAttrib(display(), configs[j], GLX_Y_INVERTED_EXT, &y_inverted);
374
375 fbcdrawableinfo[depth].fbconfig = configs[j];
376 fbcdrawableinfo[depth].bind_texture_format = texture_format;
377 fbcdrawableinfo[depth].texture_targets = texture_targets;
378 fbcdrawableinfo[depth].y_inverted = y_inverted;
379 fbcdrawableinfo[depth].mipmap = 0;
380 break;
381 }
382 }
383
384 if (count)
385 XFree(configs);
386
387 if (fbcdrawableinfo[DefaultDepth(display(), DefaultScreen(display()))].fbconfig == NULL) {
388 kError(1212) << "Could not find a framebuffer configuration for the default depth.";
389 return false;
390 }
391
392 if (fbcdrawableinfo[32].fbconfig == NULL) {
393 kError(1212) << "Could not find a framebuffer configuration for depth 32.";
394 return false;
395 }
396
397 for (int i = 0; i <= 32; i++) {
398 if (fbcdrawableinfo[i].fbconfig == NULL)
399 continue;
400
401 int vis_drawable = 0;
402 glXGetFBConfigAttrib(display(), fbcdrawableinfo[i].fbconfig, GLX_VISUAL_ID, &vis_drawable);
403
404 kDebug(1212) << "Drawable visual (depth " << i << "): 0x" << QString::number(vis_drawable, 16);
405 }
406
407 return true;
408}
409
410void GlxBackend::setSwapInterval(int interval)
411{
412 if (glXSwapIntervalEXT)
413 glXSwapIntervalEXT(display(), glxWindow, interval);
414 else if (glXSwapIntervalMESA)
415 glXSwapIntervalMESA(interval);
416 else if (glXSwapIntervalSGI)
417 glXSwapIntervalSGI(interval);
418}
419
420void GlxBackend::waitSync()
421{
422 // NOTE that vsync has no effect with indirect rendering
423 if (haveWaitSync) {
424 uint sync;
425#if 0
426 // TODO: why precisely is this important?
427 // the sync counter /can/ perform multiple steps during glXGetVideoSync & glXWaitVideoSync
428 // but this only leads to waiting for two frames??!?
429 glXGetVideoSync(&sync);
430 glXWaitVideoSync(2, (sync + 1) % 2, &sync);
431#else
432 glXWaitVideoSync(1, 0, &sync);
433#endif
434 }
435}
436
437void GlxBackend::present()
438{
439 if (lastDamage().isEmpty())
440 return;
441
442 const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
443 const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion);
444
445 if (fullRepaint) {
446 if (haveSwapInterval) {
447 if (gs_tripleBufferNeedsDetection) {
448 glXWaitGL();
449 m_swapProfiler.begin();
450 }
451 glXSwapBuffers(display(), glxWindow);
452 if (gs_tripleBufferNeedsDetection) {
453 glXWaitGL();
454 if (char result = m_swapProfiler.end()) {
455 gs_tripleBufferUndetected = gs_tripleBufferNeedsDetection = false;
456 if (result == 'd' && GLPlatform::instance()->driver() == Driver_NVidia) {
457 // TODO this is a workaround, we should get __GL_YIELD set before libGL checks it
458 if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) {
459 options->setGlPreferBufferSwap(0);
460 setSwapInterval(0);
461 kWarning(1212) << "\nIt seems you are using the nvidia driver without triple buffering\n"
462 "You must export __GL_YIELD=\"USLEEP\" to prevent large CPU overhead on synced swaps\n"
463 "Preferably, enable the TripleBuffer Option in the xorg.conf Device\n"
464 "For this reason, the tearing prevention has been disabled.\n"
465 "See https://bugs.kde.org/show_bug.cgi?id=322060\n";
466 }
467 }
468 setBlocksForRetrace(result == 'd');
469 }
470 }
471 } else {
472 waitSync();
473 glXSwapBuffers(display(), glxWindow);
474 }
475 if (supportsBufferAge()) {
476 glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *) &m_bufferAge);
477 }
478 } else if (glXCopySubBuffer) {
479 foreach (const QRect & r, lastDamage().rects()) {
480 // convert to OpenGL coordinates
481 int y = displayHeight() - r.y() - r.height();
482 glXCopySubBuffer(display(), glxWindow, r.x(), y, r.width(), r.height());
483 }
484 } else { // Copy Pixels (horribly slow on Mesa)
485 glDrawBuffer(GL_FRONT);
486 SceneOpenGL::copyPixels(lastDamage());
487 glDrawBuffer(GL_BACK);
488 }
489
490 setLastDamage(QRegion());
491 if (!supportsBufferAge()) {
492 glXWaitGL();
493 XFlush(display());
494 }
495}
496
497void GlxBackend::screenGeometryChanged(const QSize &size)
498{
499 glXMakeCurrent(display(), None, NULL);
500
501 XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
502 overlayWindow()->setup(window);
503 XSync(display(), false);
504
505 glXMakeCurrent(display(), glxWindow, ctx);
506 glViewport(0, 0, size.width(), size.height());
507
508 // The back buffer contents are now undefined
509 m_bufferAge = 0;
510}
511
512SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture)
513{
514 return new GlxTexture(texture, this);
515}
516
517QRegion GlxBackend::prepareRenderingFrame()
518{
519 QRegion repaint;
520
521 if (gs_tripleBufferNeedsDetection) {
522 // the composite timer floors the repaint frequency. This can pollute our triple buffering
523 // detection because the glXSwapBuffers call for the new frame has to wait until the pending
524 // one scanned out.
525 // So we compensate for that by waiting an extra milisecond to give the driver the chance to
526 // fllush the buffer queue
527 usleep(1000);
528 }
529
530 present();
531
532 if (supportsBufferAge())
533 repaint = accumulatedDamageHistory(m_bufferAge);
534
535 startRenderTimer();
536 glXWaitX();
537
538 return repaint;
539}
540
541void GlxBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
542{
543 if (damagedRegion.isEmpty()) {
544 setLastDamage(QRegion());
545
546 // If the damaged region of a window is fully occluded, the only
547 // rendering done, if any, will have been to repair a reused back
548 // buffer, making it identical to the front buffer.
549 //
550 // In this case we won't post the back buffer. Instead we'll just
551 // set the buffer age to 1, so the repaired regions won't be
552 // rendered again in the next frame.
553 if (!renderedRegion.isEmpty())
554 glFlush();
555
556 m_bufferAge = 1;
557 return;
558 }
559
560 setLastDamage(renderedRegion);
561
562 if (!blocksForRetrace()) {
563 // This also sets lastDamage to empty which prevents the frame from
564 // being posted again when prepareRenderingFrame() is called.
565 present();
566 } else {
567 // Make sure that the GPU begins processing the command stream
568 // now and not the next time prepareRenderingFrame() is called.
569 glFlush();
570 }
571
572 if (overlayWindow()->window()) // show the window only after the first pass,
573 overlayWindow()->show(); // since that pass may take long
574
575 // Save the damaged region to history
576 if (supportsBufferAge())
577 addToDamageHistory(damagedRegion);
578}
579
580
581/********************************************************
582 * GlxTexture
583 *******************************************************/
584GlxTexture::GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend)
585 : SceneOpenGL::TexturePrivate()
586 , q(texture)
587 , m_backend(backend)
588 , m_glxpixmap(None)
589{
590}
591
592GlxTexture::~GlxTexture()
593{
594 if (m_glxpixmap != None) {
595 if (!options->isGlStrictBinding()) {
596 glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
597 }
598 glXDestroyPixmap(display(), m_glxpixmap);
599 m_glxpixmap = None;
600 }
601}
602
603void GlxTexture::onDamage()
604{
605 if (options->isGlStrictBinding() && m_glxpixmap) {
606 glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
607 glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
608 }
609 GLTexturePrivate::onDamage();
610}
611
612void GlxTexture::findTarget()
613{
614 unsigned int new_target = 0;
615 if (glXQueryDrawable && m_glxpixmap != None)
616 glXQueryDrawable(display(), m_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
617 // HACK: this used to be a hack for Xgl.
618 // without this hack the NVIDIA blob aborts when trying to bind a texture from
619 // a pixmap icon
620 if (new_target == 0) {
621 if (GLTexture::NPOTTextureSupported() ||
622 (isPowerOfTwo(m_size.width()) && isPowerOfTwo(m_size.height()))) {
623 new_target = GLX_TEXTURE_2D_EXT;
624 } else {
625 new_target = GLX_TEXTURE_RECTANGLE_EXT;
626 }
627 }
628 switch(new_target) {
629 case GLX_TEXTURE_2D_EXT:
630 m_target = GL_TEXTURE_2D;
631 m_scale.setWidth(1.0f / m_size.width());
632 m_scale.setHeight(1.0f / m_size.height());
633 break;
634 case GLX_TEXTURE_RECTANGLE_EXT:
635 m_target = GL_TEXTURE_RECTANGLE_ARB;
636 m_scale.setWidth(1.0f);
637 m_scale.setHeight(1.0f);
638 break;
639 default:
640 abort();
641 }
642}
643
644bool GlxTexture::loadTexture(const Pixmap& pix, const QSize& size, int depth)
645{
646#ifdef CHECK_GL_ERROR
647 checkGLError("TextureLoad1");
648#endif
649 if (pix == None || size.isEmpty() || depth < 1)
650 return false;
651 if (m_backend->fbcdrawableinfo[ depth ].fbconfig == NULL) {
652 kDebug(1212) << "No framebuffer configuration for depth " << depth
653 << "; not binding pixmap" << endl;
654 return false;
655 }
656
657 m_size = size;
658 // new texture, or texture contents changed; mipmaps now invalid
659 q->setDirty();
660
661#ifdef CHECK_GL_ERROR
662 checkGLError("TextureLoad2");
663#endif
664 // tfp mode, simply bind the pixmap to texture
665 glGenTextures(1, &m_texture);
666 // The GLX pixmap references the contents of the original pixmap, so it doesn't
667 // need to be recreated when the contents change.
668 // The texture may or may not use the same storage depending on the EXT_tfp
669 // implementation. When options->glStrictBinding is true, the texture uses
670 // a different storage and needs to be updated with a call to
671 // glXBindTexImageEXT() when the contents of the pixmap has changed.
672 int attrs[] = {
673 GLX_TEXTURE_FORMAT_EXT, m_backend->fbcdrawableinfo[ depth ].bind_texture_format,
674 GLX_MIPMAP_TEXTURE_EXT, m_backend->fbcdrawableinfo[ depth ].mipmap > 0,
675 None, None, None
676 };
677 // Specifying the texture target explicitly is reported to cause a performance
678 // regression with R300G (see bug #256654).
679 if (GLPlatform::instance()->driver() != Driver_R300G) {
680 if ((m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
681 (GLTexture::NPOTTextureSupported() ||
682 (isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
683 attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
684 attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
685 } else if (m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
686 attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
687 attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
688 }
689 }
690 m_glxpixmap = glXCreatePixmap(display(), m_backend->fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
691#ifdef CHECK_GL_ERROR
692 checkGLError("TextureLoadTFP1");
693#endif
694 findTarget();
695 m_yInverted = m_backend->fbcdrawableinfo[ depth ].y_inverted ? true : false;
696 m_canUseMipmaps = m_backend->fbcdrawableinfo[ depth ].mipmap > 0;
697 q->setFilter(m_backend->fbcdrawableinfo[ depth ].mipmap > 0 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST);
698 glBindTexture(m_target, m_texture);
699#ifdef CHECK_GL_ERROR
700 checkGLError("TextureLoadTFP2");
701#endif
702 glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
703#ifdef CHECK_GL_ERROR
704 checkGLError("TextureLoad0");
705#endif
706
707 updateMatrix();
708
709 unbind();
710 return true;
711}
712
713OpenGLBackend *GlxTexture::backend()
714{
715 return m_backend;
716}
717
718} // namespace
719#endif
720