Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/x11/common/eglonxbackend.cpp
Show First 20 Lines • Show All 67 Lines • ▼ Show 20 Line(s) | 60 | EglOnXBackend::EglOnXBackend(xcb_connection_t *connection, Display *display, xcb_window_t rootWindow, int screenNumber, xcb_window_t renderingWindow) | |||
---|---|---|---|---|---|
68 | , m_rootWindow(rootWindow) | 68 | , m_rootWindow(rootWindow) | ||
69 | , m_x11ScreenNumber(screenNumber) | 69 | , m_x11ScreenNumber(screenNumber) | ||
70 | , m_renderingWindow(renderingWindow) | 70 | , m_renderingWindow(renderingWindow) | ||
71 | { | 71 | { | ||
72 | // Egl is always direct rendering | 72 | // Egl is always direct rendering | ||
73 | setIsDirectRendering(true); | 73 | setIsDirectRendering(true); | ||
74 | } | 74 | } | ||
75 | 75 | | |||
76 | static bool gs_tripleBufferUndetected = true; | | |||
77 | static bool gs_tripleBufferNeedsDetection = false; | | |||
78 | | ||||
79 | EglOnXBackend::~EglOnXBackend() | 76 | EglOnXBackend::~EglOnXBackend() | ||
80 | { | 77 | { | ||
81 | if (isFailed() && m_overlayWindow) { | 78 | if (isFailed() && m_overlayWindow) { | ||
82 | m_overlayWindow->destroy(); | 79 | m_overlayWindow->destroy(); | ||
83 | } | 80 | } | ||
84 | cleanup(); | 81 | cleanup(); | ||
85 | 82 | | |||
86 | gs_tripleBufferUndetected = true; | | |||
87 | gs_tripleBufferNeedsDetection = false; | | |||
88 | | ||||
89 | if (m_overlayWindow) { | 83 | if (m_overlayWindow) { | ||
90 | if (overlayWindow()->window()) { | 84 | if (overlayWindow()->window()) { | ||
91 | overlayWindow()->destroy(); | 85 | overlayWindow()->destroy(); | ||
92 | } | 86 | } | ||
93 | delete m_overlayWindow; | 87 | delete m_overlayWindow; | ||
94 | } | 88 | } | ||
95 | } | 89 | } | ||
96 | 90 | | |||
Show All 25 Lines | 115 | if (error != EGL_SUCCESS && error != EGL_BAD_ATTRIBUTE) { | |||
122 | setFailed(QStringLiteral("query surface failed")); | 116 | setFailed(QStringLiteral("query surface failed")); | ||
123 | return; | 117 | return; | ||
124 | } else { | 118 | } else { | ||
125 | surfaceHasSubPost = EGL_FALSE; | 119 | surfaceHasSubPost = EGL_FALSE; | ||
126 | } | 120 | } | ||
127 | } | 121 | } | ||
128 | } | 122 | } | ||
129 | 123 | | |||
130 | setSyncsToVBlank(false); | | |||
131 | setBlocksForRetrace(false); | | |||
132 | gs_tripleBufferNeedsDetection = false; | | |||
133 | m_swapProfiler.init(); | | |||
134 | if (surfaceHasSubPost) { | 124 | if (surfaceHasSubPost) { | ||
135 | qCDebug(KWIN_CORE) << "EGL implementation and surface support eglPostSubBufferNV, let's use it"; | 125 | qCDebug(KWIN_CORE) << "EGL implementation and surface support eglPostSubBufferNV, let's use it"; | ||
136 | 126 | | |||
137 | if (options->glPreferBufferSwap() != Options::NoSwapEncourage) { | | |||
138 | // check if swap interval 1 is supported | 127 | // check if swap interval 1 is supported | ||
139 | EGLint val; | 128 | EGLint val; | ||
140 | eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val); | 129 | eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val); | ||
141 | if (val >= 1) { | 130 | if (val >= 1) { | ||
142 | if (eglSwapInterval(eglDisplay(), 1)) { | 131 | if (eglSwapInterval(eglDisplay(), 1)) { | ||
143 | qCDebug(KWIN_CORE) << "Enabled v-sync"; | 132 | qCDebug(KWIN_CORE) << "Enabled v-sync"; | ||
144 | setSyncsToVBlank(true); | | |||
145 | const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER"); | | |||
146 | if (!tripleBuffer.isEmpty()) { | | |||
147 | setBlocksForRetrace(qstrcmp(tripleBuffer, "0") == 0); | | |||
148 | gs_tripleBufferUndetected = false; | | |||
149 | } | | |||
150 | gs_tripleBufferNeedsDetection = gs_tripleBufferUndetected; | | |||
151 | } | 133 | } | ||
152 | } else { | 134 | } else { | ||
153 | qCWarning(KWIN_CORE) << "Cannot enable v-sync as max. swap interval is" << val; | 135 | qCWarning(KWIN_CORE) << "Cannot enable v-sync as max. swap interval is" << val; | ||
154 | } | 136 | } | ||
155 | } else { | 137 | | ||
156 | // disable v-sync | | |||
157 | eglSwapInterval(eglDisplay(), 0); | | |||
158 | } | | |||
159 | } else { | 138 | } else { | ||
160 | /* In the GLX backend, we fall back to using glCopyPixels if we have no extension providing support for partial screen updates. | 139 | /* In the GLX backend, we fall back to using glCopyPixels if we have no extension providing support for partial screen updates. | ||
161 | * However, that does not work in EGL - glCopyPixels with glDrawBuffer(GL_FRONT); does nothing. | 140 | * However, that does not work in EGL - glCopyPixels with glDrawBuffer(GL_FRONT); does nothing. | ||
162 | * Hence we need EGL to preserve the backbuffer for us, so that we can draw the partial updates on it and call | 141 | * Hence we need EGL to preserve the backbuffer for us, so that we can draw the partial updates on it and call | ||
163 | * eglSwapBuffers() for each frame. eglSwapBuffers() then does the copy (no page flip possible in this mode), | 142 | * eglSwapBuffers() for each frame. eglSwapBuffers() then does the copy (no page flip possible in this mode), | ||
164 | * which means it is slow and not synced to the v-blank. */ | 143 | * which means it is slow and not synced to the v-blank. */ | ||
165 | qCWarning(KWIN_CORE) << "eglPostSubBufferNV not supported, have to enable buffer preservation - which breaks v-sync and performance"; | 144 | qCWarning(KWIN_CORE) << "eglPostSubBufferNV not supported, have to enable buffer preservation - which breaks v-sync and performance"; | ||
166 | eglSurfaceAttrib(eglDisplay(), surface(), EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); | 145 | eglSurfaceAttrib(eglDisplay(), surface(), EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); | ||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Line(s) | |||||
338 | void EglOnXBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry) | 317 | void EglOnXBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry) | ||
339 | { | 318 | { | ||
340 | if (damage.isEmpty()) { | 319 | if (damage.isEmpty()) { | ||
341 | return; | 320 | return; | ||
342 | } | 321 | } | ||
343 | const bool fullRepaint = supportsBufferAge() || (damage == screenGeometry); | 322 | const bool fullRepaint = supportsBufferAge() || (damage == screenGeometry); | ||
344 | 323 | | |||
345 | if (fullRepaint || !surfaceHasSubPost) { | 324 | if (fullRepaint || !surfaceHasSubPost) { | ||
346 | if (gs_tripleBufferNeedsDetection) { | | |||
347 | eglWaitGL(); | | |||
348 | m_swapProfiler.begin(); | | |||
349 | } | | |||
350 | // the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation) | 325 | // the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation) | ||
351 | eglSwapBuffers(eglDisplay(), surface); | 326 | eglSwapBuffers(eglDisplay(), surface); | ||
352 | if (gs_tripleBufferNeedsDetection) { | | |||
353 | eglWaitGL(); | | |||
354 | if (char result = m_swapProfiler.end()) { | | |||
355 | gs_tripleBufferUndetected = gs_tripleBufferNeedsDetection = false; | | |||
356 | if (result == 'd' && GLPlatform::instance()->driver() == Driver_NVidia) { | | |||
357 | // TODO this is a workaround, we should get __GL_YIELD set before libGL checks it | | |||
358 | if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) { | | |||
359 | options->setGlPreferBufferSwap(0); | | |||
360 | eglSwapInterval(eglDisplay(), 0); | | |||
361 | result = 0; // hint proper behavior | | |||
362 | qCWarning(KWIN_CORE) << "\nIt seems you are using the nvidia driver without triple buffering\n" | | |||
363 | "You must export __GL_YIELD=\"USLEEP\" to prevent large CPU overhead on synced swaps\n" | | |||
364 | "Preferably, enable the TripleBuffer Option in the xorg.conf Device\n" | | |||
365 | "For this reason, the tearing prevention has been disabled.\n" | | |||
366 | "See https://bugs.kde.org/show_bug.cgi?id=322060\n"; | | |||
367 | } | | |||
368 | } | | |||
369 | setBlocksForRetrace(result == 'd'); | | |||
370 | } | | |||
371 | } | | |||
372 | if (supportsBufferAge()) { | 327 | if (supportsBufferAge()) { | ||
373 | eglQuerySurface(eglDisplay(), surface, EGL_BUFFER_AGE_EXT, &m_bufferAge); | 328 | eglQuerySurface(eglDisplay(), surface, EGL_BUFFER_AGE_EXT, &m_bufferAge); | ||
374 | } | 329 | } | ||
375 | } else { | 330 | } else { | ||
376 | // a part of the screen changed, and we can use eglPostSubBufferNV to copy the updated area | 331 | // a part of the screen changed, and we can use eglPostSubBufferNV to copy the updated area | ||
377 | for (const QRect &r : damage) { | 332 | for (const QRect &r : damage) { | ||
378 | eglPostSubBufferNV(eglDisplay(), surface, r.left(), screenGeometry.height() - r.bottom() - 1, r.width(), r.height()); | 333 | eglPostSubBufferNV(eglDisplay(), surface, r.left(), screenGeometry.height() - r.bottom() - 1, r.width(), r.height()); | ||
379 | } | 334 | } | ||
Show All 14 Lines | |||||
394 | { | 349 | { | ||
395 | return new EglTexture(texture, this); | 350 | return new EglTexture(texture, this); | ||
396 | } | 351 | } | ||
397 | 352 | | |||
398 | QRegion EglOnXBackend::prepareRenderingFrame() | 353 | QRegion EglOnXBackend::prepareRenderingFrame() | ||
399 | { | 354 | { | ||
400 | QRegion repaint; | 355 | QRegion repaint; | ||
401 | 356 | | |||
402 | if (gs_tripleBufferNeedsDetection) { | | |||
403 | // the composite timer floors the repaint frequency. This can pollute our triple buffering | | |||
404 | // detection because the glXSwapBuffers call for the new frame has to wait until the pending | | |||
405 | // one scanned out. | | |||
406 | // So we compensate for that by waiting an extra milisecond to give the driver the chance to | | |||
407 | // fllush the buffer queue | | |||
408 | usleep(1000); | | |||
409 | } | | |||
410 | | ||||
411 | present(); | 357 | present(); | ||
412 | 358 | | |||
413 | if (supportsBufferAge()) | 359 | if (supportsBufferAge()) | ||
414 | repaint = accumulatedDamageHistory(m_bufferAge); | 360 | repaint = accumulatedDamageHistory(m_bufferAge); | ||
415 | 361 | | |||
416 | startRenderTimer(); | 362 | startRenderTimer(); | ||
417 | eglWaitNative(EGL_CORE_NATIVE_ENGINE); | 363 | eglWaitNative(EGL_CORE_NATIVE_ENGINE); | ||
418 | 364 | | |||
Show All 16 Lines | 380 | if (!renderedRegion.isEmpty()) | |||
435 | glFlush(); | 381 | glFlush(); | ||
436 | 382 | | |||
437 | m_bufferAge = 1; | 383 | m_bufferAge = 1; | ||
438 | return; | 384 | return; | ||
439 | } | 385 | } | ||
440 | 386 | | |||
441 | setLastDamage(renderedRegion); | 387 | setLastDamage(renderedRegion); | ||
442 | 388 | | |||
443 | if (!blocksForRetrace()) { | | |||
444 | // This also sets lastDamage to empty which prevents the frame from | 389 | // This also sets lastDamage to empty which prevents the frame from | ||
445 | // being posted again when prepareRenderingFrame() is called. | 390 | // being posted again when prepareRenderingFrame() is called. | ||
446 | present(); | 391 | present(); | ||
447 | } else { | | |||
448 | // Make sure that the GPU begins processing the command stream | | |||
449 | // now and not the next time prepareRenderingFrame() is called. | | |||
450 | glFlush(); | | |||
451 | } | | |||
452 | 392 | | |||
453 | if (m_overlayWindow && overlayWindow()->window()) // show the window only after the first pass, | 393 | if (m_overlayWindow && overlayWindow()->window()) // show the window only after the first pass, | ||
454 | overlayWindow()->show(); // since that pass may take long | 394 | overlayWindow()->show(); // since that pass may take long | ||
455 | 395 | | |||
456 | // Save the damaged region to history | 396 | // Save the damaged region to history | ||
457 | if (supportsBufferAge()) | 397 | if (supportsBufferAge()) | ||
458 | addToDamageHistory(damagedRegion); | 398 | addToDamageHistory(damagedRegion); | ||
459 | } | 399 | } | ||
▲ Show 20 Lines • Show All 85 Lines • Show Last 20 Lines |