1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> |
6 | Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org> |
7 | |
8 | Based on glcompmgr code by Felix Bellaby. |
9 | Using code from Compiz and Beryl. |
10 | |
11 | This program is free software; you can redistribute it and/or modify |
12 | it under the terms of the GNU General Public License as published by |
13 | the Free Software Foundation; either version 2 of the License, or |
14 | (at your option) any later version. |
15 | |
16 | This program is distributed in the hope that it will be useful, |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | GNU General Public License for more details. |
20 | |
21 | You should have received a copy of the GNU General Public License |
22 | along 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 | |
41 | namespace KWin |
42 | { |
43 | GlxBackend::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 | |
55 | GlxBackend::~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 | |
75 | static bool gs_tripleBufferUndetected = true; |
76 | static bool gs_tripleBufferNeedsDetection = false; |
77 | |
78 | void 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 | |
163 | bool 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 | |
231 | bool 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 | |
264 | bool 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 | |
297 | bool 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 | |
410 | void 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 | |
420 | void 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 | |
437 | void 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 | |
497 | void 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 | |
512 | SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture) |
513 | { |
514 | return new GlxTexture(texture, this); |
515 | } |
516 | |
517 | QRegion 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 | |
541 | void 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 | *******************************************************/ |
584 | GlxTexture::GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend) |
585 | : SceneOpenGL::TexturePrivate() |
586 | , q(texture) |
587 | , m_backend(backend) |
588 | , m_glxpixmap(None) |
589 | { |
590 | } |
591 | |
592 | GlxTexture::~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 | |
603 | void 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 | |
612 | void 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 | |
644 | bool 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 | |
713 | OpenGLBackend *GlxTexture::backend() |
714 | { |
715 | return m_backend; |
716 | } |
717 | |
718 | } // namespace |
719 | #endif |
720 | |