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) 2009, 2010, 2011 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#include "scene_opengl.h"
25#ifdef KWIN_HAVE_EGL
26#include "eglonxbackend.h"
27// for Wayland
28#include "config-workspace.h"
29#ifdef WAYLAND_FOUND
30#include "egl_wayland_backend.h"
31#endif
32#endif
33#ifndef KWIN_HAVE_OPENGLES
34#include "glxbackend.h"
35#endif
36
37#include <kxerrorhandler.h>
38
39#include <kwinglcolorcorrection.h>
40#include <kwinglplatform.h>
41
42#include "utils.h"
43#include "client.h"
44#include "composite.h"
45#include "deleted.h"
46#include "effects.h"
47#include "lanczosfilter.h"
48#include "overlaywindow.h"
49#include "paintredirector.h"
50#include "screens.h"
51#include "workspace.h"
52
53#include <cmath>
54#include <unistd.h>
55#include <stddef.h>
56
57// turns on checks for opengl errors in various places (for easier finding of them)
58// normally only few of them are enabled
59//#define CHECK_GL_ERROR
60
61#include <X11/extensions/Xcomposite.h>
62
63#include <qpainter.h>
64#include <QDBusConnection>
65#include <QDBusConnectionInterface>
66#include <QDBusInterface>
67#include <QGraphicsScale>
68#include <QStringList>
69#include <QVector2D>
70#include <QVector4D>
71#include <QMatrix4x4>
72
73#include <KDE/KLocalizedString>
74#include <KDE/KNotification>
75#include <KProcess>
76
77namespace KWin
78{
79
80extern int currentRefreshRate();
81
82//****************************************
83// SceneOpenGL
84//****************************************
85OpenGLBackend::OpenGLBackend()
86 : m_overlayWindow(new OverlayWindow()) // TODO: maybe create only if needed?
87 , m_syncsToVBlank(false)
88 , m_blocksForRetrace(false)
89 , m_directRendering(false)
90 , m_haveBufferAge(false)
91 , m_failed(false)
92{
93}
94
95OpenGLBackend::~OpenGLBackend()
96{
97 if (isFailed()) {
98 m_overlayWindow->destroy();
99 }
100 delete m_overlayWindow;
101}
102
103void OpenGLBackend::setFailed(const QString &reason)
104{
105 kWarning(1212) << "Creating the OpenGL rendering failed: " << reason;
106 m_failed = true;
107}
108
109void OpenGLBackend::idle()
110{
111 if (hasPendingFlush())
112 present();
113}
114
115void OpenGLBackend::addToDamageHistory(const QRegion &region)
116{
117 if (m_damageHistory.count() > 10)
118 m_damageHistory.removeLast();
119
120 m_damageHistory.prepend(region);
121}
122
123QRegion OpenGLBackend::accumulatedDamageHistory(int bufferAge) const
124{
125 QRegion region;
126
127 // Note: An age of zero means the buffer contents are undefined
128 if (bufferAge > 0 && bufferAge <= m_damageHistory.count()) {
129 for (int i = 0; i < bufferAge - 1; i++)
130 region |= m_damageHistory[i];
131 } else {
132 region = QRegion(0, 0, displayWidth(), displayHeight());
133 }
134
135 return region;
136}
137
138/************************************************
139 * SceneOpenGL
140 ***********************************************/
141
142SceneOpenGL::SceneOpenGL(Workspace* ws, OpenGLBackend *backend)
143 : Scene(ws)
144 , init_ok(true)
145 , m_backend(backend)
146{
147 if (m_backend->isFailed()) {
148 init_ok = false;
149 return;
150 }
151 if (!viewportLimitsMatched(QSize(displayWidth(), displayHeight())))
152 return;
153
154 // perform Scene specific checks
155 GLPlatform *glPlatform = GLPlatform::instance();
156#ifndef KWIN_HAVE_OPENGLES
157 if (!hasGLExtension("GL_ARB_texture_non_power_of_two")
158 && !hasGLExtension("GL_ARB_texture_rectangle")) {
159 kError(1212) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
160 init_ok = false;
161 return; // error
162 }
163#endif
164 if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(8, 0)) {
165 kError(1212) << "KWin requires at least Mesa 8.0 for OpenGL compositing.";
166 init_ok = false;
167 return;
168 }
169#ifndef KWIN_HAVE_OPENGLES
170 glDrawBuffer(GL_BACK);
171#endif
172
173 m_debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
174
175 // set strict binding
176 if (options->isGlStrictBindingFollowsDriver()) {
177 options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
178 }
179}
180
181SceneOpenGL::~SceneOpenGL()
182{
183 foreach (Window * w, windows) {
184 delete w;
185 }
186 // do cleanup after initBuffer()
187 SceneOpenGL::EffectFrame::cleanup();
188 if (init_ok) {
189 // backend might be still needed for a different scene
190 delete m_backend;
191 }
192}
193
194SceneOpenGL *SceneOpenGL::createScene()
195{
196 OpenGLBackend *backend = NULL;
197 OpenGLPlatformInterface platformInterface = NoOpenGLPlatformInterface;
198 // should we use glx?
199#ifndef KWIN_HAVE_OPENGLES
200 // on OpenGL we default to glx
201 platformInterface = GlxPlatformInterface;
202#endif
203
204 const QByteArray envOpenGLInterface(qgetenv("KWIN_OPENGL_INTERFACE"));
205#ifdef KWIN_HAVE_EGL
206#ifdef KWIN_HAVE_OPENGLES
207 // for OpenGL ES we need to use the Egl Backend
208 platformInterface = EglPlatformInterface;
209#else
210 // check environment variable
211 if (qstrcmp(envOpenGLInterface, "egl") == 0 ||
212 qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
213 kDebug(1212) << "Forcing EGL native interface through environment variable";
214 platformInterface = EglPlatformInterface;
215 }
216#endif
217#endif
218
219 switch (platformInterface) {
220 case GlxPlatformInterface:
221#ifndef KWIN_HAVE_OPENGLES
222 backend = new GlxBackend();
223#endif
224 break;
225 case EglPlatformInterface:
226#ifdef KWIN_HAVE_EGL
227#ifdef WAYLAND_FOUND
228 if (qstrcmp(envOpenGLInterface, "egl_wayland") == 0) {
229 backend = new EglWaylandBackend();
230 } else {
231 backend = new EglOnXBackend();
232 }
233#else
234 backend = new EglOnXBackend();
235#endif
236#endif
237 break;
238 default:
239 // no backend available
240 return NULL;
241 }
242 if (!backend || backend->isFailed()) {
243 delete backend;
244 return NULL;
245 }
246 SceneOpenGL *scene = NULL;
247 // first let's try an OpenGL 2 scene
248 if (SceneOpenGL2::supported(backend)) {
249 scene = new SceneOpenGL2(backend);
250 if (scene->initFailed()) {
251 delete scene;
252 scene = NULL;
253 } else {
254 return scene;
255 }
256 }
257#ifdef KWIN_HAVE_OPENGL_1
258 if (SceneOpenGL1::supported(backend)) {
259 scene = new SceneOpenGL1(backend);
260 if (scene->initFailed()) {
261 delete scene;
262 scene = NULL;
263 }
264 }
265#endif
266 if (!scene) {
267 if (GLPlatform::instance()->recommendedCompositor() == XRenderCompositing) {
268 kError(1212) << "OpenGL driver recommends XRender based compositing. Falling back to XRender.";
269 kError(1212) << "To overwrite the detection use the environment variable KWIN_COMPOSE";
270 kError(1212) << "For more information see http://community.kde.org/KWin/Environment_Variables#KWIN_COMPOSE";
271 QTimer::singleShot(0, Compositor::self(), SLOT(fallbackToXRenderCompositing()));
272 }
273 delete backend;
274 }
275
276 return scene;
277}
278
279OverlayWindow *SceneOpenGL::overlayWindow()
280{
281 return m_backend->overlayWindow();
282}
283
284bool SceneOpenGL::syncsToVBlank() const
285{
286 return m_backend->syncsToVBlank();
287}
288
289bool SceneOpenGL::blocksForRetrace() const
290{
291 return m_backend->blocksForRetrace();
292}
293
294void SceneOpenGL::idle()
295{
296 m_backend->idle();
297 Scene::idle();
298}
299
300bool SceneOpenGL::initFailed() const
301{
302 return !init_ok;
303}
304
305#ifndef KWIN_HAVE_OPENGLES
306void SceneOpenGL::copyPixels(const QRegion &region)
307{
308 foreach (const QRect &r, region.rects()) {
309 const int x0 = r.x();
310 const int y0 = displayHeight() - r.y() - r.height();
311 const int x1 = r.x() + r.width();
312 const int y1 = displayHeight() - r.y();
313
314 glBlitFramebuffer(x0, y0, x1, y1, x0, y0, x1, y1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
315 }
316}
317#endif
318
319#ifndef KWIN_HAVE_OPENGLES
320# define GL_GUILTY_CONTEXT_RESET_KWIN GL_GUILTY_CONTEXT_RESET_ARB
321# define GL_INNOCENT_CONTEXT_RESET_KWIN GL_INNOCENT_CONTEXT_RESET_ARB
322# define GL_UNKNOWN_CONTEXT_RESET_KWIN GL_UNKNOWN_CONTEXT_RESET_ARB
323#else
324# define GL_GUILTY_CONTEXT_RESET_KWIN GL_GUILTY_CONTEXT_RESET_EXT
325# define GL_INNOCENT_CONTEXT_RESET_KWIN GL_INNOCENT_CONTEXT_RESET_EXT
326# define GL_UNKNOWN_CONTEXT_RESET_KWIN GL_UNKNOWN_CONTEXT_RESET_EXT
327#endif
328
329void SceneOpenGL::handleGraphicsReset(GLenum status)
330{
331 switch (status) {
332 case GL_GUILTY_CONTEXT_RESET_KWIN:
333 kDebug(1212) << "A graphics reset attributable to the current GL context occurred.";
334 break;
335
336 case GL_INNOCENT_CONTEXT_RESET_KWIN:
337 kDebug(1212) << "A graphics reset not attributable to the current GL context occurred.";
338 break;
339
340 case GL_UNKNOWN_CONTEXT_RESET_KWIN:
341 kDebug(1212) << "A graphics reset of an unknown cause occurred.";
342 break;
343
344 default:
345 break;
346 }
347
348 QElapsedTimer timer;
349 timer.start();
350
351 // Wait until the reset is completed or max 10 seconds
352 while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR)
353 usleep(50);
354
355 kDebug(1212) << "Attempting to reset compositing.";
356 QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection);
357
358 KNotification::event("graphicsreset", i18n("Desktop effects were restarted due to a graphics reset"));
359}
360
361qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
362{
363 // actually paint the frame, flushed with the NEXT frame
364 foreach (Toplevel * c, toplevels) {
365 // TODO: cache the stacking_order in case it has not changed
366 assert(windows.contains(c));
367 stacking_order.append(windows[ c ]);
368 }
369
370 QRegion repaint = m_backend->prepareRenderingFrame();
371
372 const GLenum status = glGetGraphicsResetStatus();
373 if (status != GL_NO_ERROR) {
374 handleGraphicsReset(status);
375 return 0;
376 }
377
378 int mask = 0;
379#ifdef CHECK_GL_ERROR
380 checkGLError("Paint1");
381#endif
382
383 // After this call, updateRegion will contain the damaged region in the
384 // back buffer. This is the region that needs to be posted to repair
385 // the front buffer. It doesn't include the additional damage returned
386 // by prepareRenderingFrame(). validRegion is the region that has been
387 // repainted, and may be larger than updateRegion.
388 QRegion updateRegion, validRegion;
389 paintScreen(&mask, damage, repaint, &updateRegion, &validRegion); // call generic implementation
390
391#ifndef KWIN_HAVE_OPENGLES
392 const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
393
394 // copy dirty parts from front to backbuffer
395 if (!m_backend->supportsBufferAge() &&
396 options->glPreferBufferSwap() == Options::CopyFrontBuffer &&
397 validRegion != displayRegion) {
398 glReadBuffer(GL_FRONT);
399 copyPixels(displayRegion - validRegion);
400 glReadBuffer(GL_BACK);
401 validRegion = displayRegion;
402 }
403#endif
404
405#ifdef CHECK_GL_ERROR
406 checkGLError("Paint2");
407#endif
408
409 m_backend->endRenderingFrame(validRegion, updateRegion);
410
411 // do cleanup
412 stacking_order.clear();
413 checkGLError("PostPaint");
414 return m_backend->renderTime();
415}
416
417QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const
418{
419 QMatrix4x4 matrix;
420
421 if (!(mask & PAINT_SCREEN_TRANSFORMED))
422 return matrix;
423
424 matrix.translate(data.translation());
425 data.scale().applyTo(&matrix);
426
427 if (data.rotationAngle() == 0.0)
428 return matrix;
429
430 // Apply the rotation
431 // cannot use data.rotation->applyTo(&matrix) as QGraphicsRotation uses projectedRotate to map back to 2D
432 matrix.translate(data.rotationOrigin());
433 const QVector3D axis = data.rotationAxis();
434 matrix.rotate(data.rotationAngle(), axis.x(), axis.y(), axis.z());
435 matrix.translate(-data.rotationOrigin());
436
437 return matrix;
438}
439
440void SceneOpenGL::paintBackground(QRegion region)
441{
442 PaintClipper pc(region);
443 if (!PaintClipper::clip()) {
444 glClearColor(0, 0, 0, 1);
445 glClear(GL_COLOR_BUFFER_BIT);
446 return;
447 }
448 if (pc.clip() && pc.paintArea().isEmpty())
449 return; // no background to paint
450 QVector<float> verts;
451 for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) {
452 QRect r = iterator.boundingRect();
453 verts << r.x() + r.width() << r.y();
454 verts << r.x() << r.y();
455 verts << r.x() << r.y() + r.height();
456 verts << r.x() << r.y() + r.height();
457 verts << r.x() + r.width() << r.y() + r.height();
458 verts << r.x() + r.width() << r.y();
459 }
460 doPaintBackground(verts);
461}
462
463void SceneOpenGL::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
464{
465 if (m_backend->supportsBufferAge())
466 return;
467
468 if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints
469 const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
470 uint damagedPixels = 0;
471 const uint fullRepaintLimit = (opaqueFullscreen?0.49f:0.748f)*displayWidth()*displayHeight();
472 // 16:9 is 75% of 4:3 and 2.55:1 is 49.01% of 5:4
473 // (5:4 is the most square format and 2.55:1 is Cinemascope55 - the widest ever shot
474 // movie aspect - two times ;-) It's a Fox format, though, so maybe we want to restrict
475 // to 2.20:1 - Panavision - which has actually been used for interesting movies ...)
476 // would be 57% of 5/4
477 foreach (const QRect &r, region.rects()) {
478// damagedPixels += r.width() * r.height(); // combined window damage test
479 damagedPixels = r.width() * r.height(); // experimental single window damage testing
480 if (damagedPixels > fullRepaintLimit) {
481 region = displayRegion;
482 return;
483 }
484 }
485 } else if (options->glPreferBufferSwap() == Options::PaintFullScreen) { // forced full rePaint
486 region = QRegion(0, 0, displayWidth(), displayHeight());
487 }
488}
489
490void SceneOpenGL::windowAdded(Toplevel* c)
491{
492 assert(!windows.contains(c));
493 Window *w = createWindow(c);
494 windows[ c ] = w;
495 w->setScene(this);
496 connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), SLOT(windowOpacityChanged(KWin::Toplevel*)));
497 connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(windowGeometryShapeChanged(KWin::Toplevel*)));
498 connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), SLOT(windowClosed(KWin::Toplevel*,KWin::Deleted*)));
499 c->effectWindow()->setSceneWindow(windows[ c ]);
500 c->getShadow();
501 windows[ c ]->updateShadow(c->shadow());
502}
503
504void SceneOpenGL::windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted)
505{
506 assert(windows.contains(c));
507 if (deleted != NULL) {
508 // replace c with deleted
509 Window* w = windows.take(c);
510 w->updateToplevel(deleted);
511 if (w->shadow()) {
512 w->shadow()->setToplevel(deleted);
513 }
514 windows[ deleted ] = w;
515 } else {
516 delete windows.take(c);
517 c->effectWindow()->setSceneWindow(NULL);
518 }
519}
520
521void SceneOpenGL::windowDeleted(Deleted* c)
522{
523 assert(windows.contains(c));
524 delete windows.take(c);
525 c->effectWindow()->setSceneWindow(NULL);
526}
527
528void SceneOpenGL::windowGeometryShapeChanged(KWin::Toplevel* c)
529{
530 if (!windows.contains(c)) // this is ok, shape is not valid
531 return; // by default
532 Window* w = windows[ c ];
533 w->discardShape();
534}
535
536void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t)
537{
538 Q_UNUSED(t)
539#if 0 // not really needed, windows are painted on every repaint
540 // and opacity is used when applying texture, not when
541 // creating it
542 if (!windows.contains(c)) // this is ok, texture is created
543 return; // on demand
544 Window* w = windows[ c ];
545 w->discardTexture();
546#endif
547}
548
549SceneOpenGL::Texture *SceneOpenGL::createTexture()
550{
551 return new Texture(m_backend);
552}
553
554SceneOpenGL::Texture *SceneOpenGL::createTexture(const QPixmap &pix, GLenum target)
555{
556 return new Texture(m_backend, pix, target);
557}
558
559bool SceneOpenGL::viewportLimitsMatched(const QSize &size) const {
560 GLint limit[2];
561 glGetIntegerv(GL_MAX_VIEWPORT_DIMS, limit);
562 if (limit[0] < size.width() || limit[1] < size.height()) {
563 QMetaObject::invokeMethod(Compositor::self(), "suspend",
564 Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, Compositor::AllReasonSuspend));
565 const QString message = i18n("<h1>OpenGL desktop effects not possible</h1>"
566 "Your system cannot perform OpenGL Desktop Effects at the "
567 "current resolution<br><br>"
568 "You can try to select the XRender backend, but it "
569 "might be very slow for this resolution as well.<br>"
570 "Alternatively, lower the combined resolution of all screens "
571 "to %1x%2 ", limit[0], limit[1]);
572 const QString details = i18n("The demanded resolution exceeds the GL_MAX_VIEWPORT_DIMS "
573 "limitation of your GPU and is therefore not compatible "
574 "with the OpenGL compositor.<br>"
575 "XRender does not know such limitation, but the performance "
576 "will usually be impacted by the hardware limitations that "
577 "restrict the OpenGL viewport size.");
578 const int oldTimeout = QDBusConnection::sessionBus().interface()->timeout();
579 QDBusConnection::sessionBus().interface()->setTimeout(500);
580 if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kwinCompositingDialog").value()) {
581 QDBusInterface dialog( "org.kde.kwinCompositingDialog", "/CompositorSettings", "org.kde.kwinCompositingDialog" );
582 dialog.asyncCall("warn", message, details, "");
583 } else {
584 const QString args = "warn " + message.toLocal8Bit().toBase64() + " details " + details.toLocal8Bit().toBase64();
585 KProcess::startDetached("kcmshell4", QStringList() << "kwincompositing" << "--args" << args);
586 }
587 QDBusConnection::sessionBus().interface()->setTimeout(oldTimeout);
588 return false;
589 }
590 glGetIntegerv(GL_MAX_TEXTURE_SIZE, limit);
591 if (limit[0] < size.width() || limit[0] < size.height()) {
592 KConfig cfg("kwin_dialogsrc");
593
594 if (!KConfigGroup(&cfg, "Notification Messages").readEntry("max_tex_warning", true))
595 return true;
596
597 const QString message = i18n("<h1>OpenGL desktop effects might be unusable</h1>"
598 "OpenGL Desktop Effects at the current resolution are supported "
599 "but might be exceptionally slow.<br>"
600 "Also large windows will turn entirely black.<br><br>"
601 "Consider to suspend compositing, switch to the XRender backend "
602 "or lower the resolution to %1x%1." , limit[0]);
603 const QString details = i18n("The demanded resolution exceeds the GL_MAX_TEXTURE_SIZE "
604 "limitation of your GPU, thus windows of that size cannot be "
605 "assigned to textures and will be entirely black.<br>"
606 "Also this limit will often be a performance level barrier despite "
607 "below GL_MAX_VIEWPORT_DIMS, because the driver might fall back to "
608 "software rendering in this case.");
609 const int oldTimeout = QDBusConnection::sessionBus().interface()->timeout();
610 QDBusConnection::sessionBus().interface()->setTimeout(500);
611 if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kwinCompositingDialog").value()) {
612 QDBusInterface dialog( "org.kde.kwinCompositingDialog", "/CompositorSettings", "org.kde.kwinCompositingDialog" );
613 dialog.asyncCall("warn", message, details, "kwin_dialogsrc:max_tex_warning");
614 } else {
615 const QString args = "warn " + message.toLocal8Bit().toBase64() + " details " +
616 details.toLocal8Bit().toBase64() + " dontagain kwin_dialogsrc:max_tex_warning";
617 KProcess::startDetached("kcmshell4", QStringList() << "kwincompositing" << "--args" << args);
618 }
619 QDBusConnection::sessionBus().interface()->setTimeout(oldTimeout);
620 }
621 return true;
622}
623
624void SceneOpenGL::screenGeometryChanged(const QSize &size)
625{
626 if (!viewportLimitsMatched(size))
627 return;
628 Scene::screenGeometryChanged(size);
629 glViewport(0,0, size.width(), size.height());
630 m_backend->screenGeometryChanged(size);
631 ShaderManager::instance()->resetAllShaders();
632}
633
634void SceneOpenGL::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
635{
636 const QRect r = region.boundingRect();
637 glEnable(GL_SCISSOR_TEST);
638 glScissor(r.x(), displayHeight() - r.y() - r.height(), r.width(), r.height());
639 KWin::Scene::paintDesktop(desktop, mask, region, data);
640 glDisable(GL_SCISSOR_TEST);
641}
642
643//****************************************
644// SceneOpenGL2
645//****************************************
646bool SceneOpenGL2::supported(OpenGLBackend *backend)
647{
648 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
649 if (!forceEnv.isEmpty()) {
650 if (qstrcmp(forceEnv, "O2") == 0) {
651 kDebug(1212) << "OpenGL 2 compositing enforced by environment variable";
652 return true;
653 } else {
654 // OpenGL 2 disabled by environment variable
655 return false;
656 }
657 }
658 if (!backend->isDirectRendering()) {
659 return false;
660 }
661 if (GLPlatform::instance()->recommendedCompositor() < OpenGL2Compositing) {
662 kDebug(1212) << "Driver does not recommend OpenGL 2 compositing";
663#ifndef KWIN_HAVE_OPENGLES
664 return false;
665#endif
666 }
667 if (options->isGlLegacy()) {
668 kDebug(1212) << "OpenGL 2 disabled by config option";
669 return false;
670 }
671 return true;
672}
673
674SceneOpenGL2::SceneOpenGL2(OpenGLBackend *backend)
675 : SceneOpenGL(Workspace::self(), backend)
676 , m_lanczosFilter(NULL)
677 , m_colorCorrection()
678{
679 if (!init_ok) {
680 // base ctor already failed
681 return;
682 }
683 // Initialize color correction before the shaders
684 slotColorCorrectedChanged(false);
685 connect(options, SIGNAL(colorCorrectedChanged()), this, SLOT(slotColorCorrectedChanged()), Qt::QueuedConnection);
686
687 if (!ShaderManager::instance()->isValid()) {
688 kDebug(1212) << "No Scene Shaders available";
689 init_ok = false;
690 return;
691 }
692
693 // push one shader on the stack so that one is always bound
694 ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
695 if (checkGLError("Init")) {
696 kError(1212) << "OpenGL 2 compositing setup failed";
697 init_ok = false;
698 return; // error
699 }
700
701 kDebug(1212) << "OpenGL 2 compositing successfully initialized";
702
703#ifndef KWIN_HAVE_OPENGLES
704 // It is not legal to not have a vertex array object bound in a core context
705 if (hasGLExtension("GL_ARB_vertex_array_object")) {
706 glGenVertexArrays(1, &vao);
707 glBindVertexArray(vao);
708 }
709#endif
710
711 init_ok = true;
712}
713
714SceneOpenGL2::~SceneOpenGL2()
715{
716}
717
718void SceneOpenGL2::paintGenericScreen(int mask, ScreenPaintData data)
719{
720 ShaderBinder binder(ShaderManager::GenericShader);
721
722 binder.shader()->setUniform(GLShader::ScreenTransformation, transformation(mask, data));
723
724 Scene::paintGenericScreen(mask, data);
725}
726
727void SceneOpenGL2::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
728{
729 ShaderBinder binder(ShaderManager::GenericShader);
730 GLShader *shader = binder.shader();
731 QMatrix4x4 screenTransformation = shader->getUniformMatrix4x4("screenTransformation");
732
733 KWin::SceneOpenGL::paintDesktop(desktop, mask, region, data);
734
735 shader->setUniform(GLShader::ScreenTransformation, screenTransformation);
736}
737
738void SceneOpenGL2::doPaintBackground(const QVector< float >& vertices)
739{
740 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
741 vbo->reset();
742 vbo->setUseColor(true);
743 vbo->setData(vertices.count() / 2, 2, vertices.data(), NULL);
744
745 ShaderBinder binder(ShaderManager::ColorShader);
746 binder.shader()->setUniform(GLShader::Offset, QVector2D(0, 0));
747
748 vbo->render(GL_TRIANGLES);
749}
750
751SceneOpenGL::Window *SceneOpenGL2::createWindow(Toplevel *t)
752{
753 return new SceneOpenGL2Window(t);
754}
755
756void SceneOpenGL2::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
757{
758 if (!m_colorCorrection.isNull() && m_colorCorrection->isEnabled()) {
759 // Split the painting for separate screens
760 const int numScreens = screens()->count();
761 for (int screen = 0; screen < numScreens; ++ screen) {
762 QRegion regionForScreen(region);
763 if (numScreens > 1)
764 regionForScreen = region.intersected(screens()->geometry(screen));
765
766 data.setScreen(screen);
767 performPaintWindow(w, mask, regionForScreen, data);
768 }
769 } else {
770 performPaintWindow(w, mask, region, data);
771 }
772}
773
774void SceneOpenGL2::performPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
775{
776 if (mask & PAINT_WINDOW_LANCZOS) {
777 if (!m_lanczosFilter) {
778 m_lanczosFilter = new LanczosFilter(this);
779 // recreate the lanczos filter when the screen gets resized
780 connect(screens(), SIGNAL(changed()), SLOT(resetLanczosFilter()));
781 }
782 m_lanczosFilter->performPaint(w, mask, region, data);
783 } else
784 w->sceneWindow()->performPaint(mask, region, data);
785}
786
787void SceneOpenGL2::resetLanczosFilter()
788{
789 // TODO: Qt5 - replace by a lambda slot
790 delete m_lanczosFilter;
791 m_lanczosFilter = NULL;
792}
793
794ColorCorrection *SceneOpenGL2::colorCorrection()
795{
796 return m_colorCorrection.data();
797}
798
799void SceneOpenGL2::slotColorCorrectedChanged(bool recreateShaders)
800{
801 kDebug(1212) << "Color correction:" << options->isColorCorrected();
802 if (options->isColorCorrected() && m_colorCorrection.isNull()) {
803 m_colorCorrection.reset(new ColorCorrection(this));
804 if (!m_colorCorrection->setEnabled(true)) {
805 m_colorCorrection.reset();
806 return;
807 }
808 connect(m_colorCorrection.data(), SIGNAL(changed()), Compositor::self(), SLOT(addRepaintFull()));
809 connect(m_colorCorrection.data(), SIGNAL(errorOccured()), options, SLOT(setColorCorrected()), Qt::QueuedConnection);
810 if (recreateShaders) {
811 // Reload all shaders
812 ShaderManager::cleanup();
813 ShaderManager::instance();
814 }
815 } else {
816 m_colorCorrection.reset();
817 }
818 Compositor::self()->addRepaintFull();
819}
820
821
822//****************************************
823// SceneOpenGL1
824//****************************************
825#ifdef KWIN_HAVE_OPENGL_1
826bool SceneOpenGL1::supported(OpenGLBackend *backend)
827{
828 Q_UNUSED(backend)
829 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
830 if (!forceEnv.isEmpty()) {
831 if (qstrcmp(forceEnv, "O1") == 0) {
832 kDebug(1212) << "OpenGL 1 compositing enforced by environment variable";
833 return true;
834 } else {
835 // OpenGL 1 disabled by environment variable
836 return false;
837 }
838 }
839 if (GLPlatform::instance()->recommendedCompositor() < OpenGL1Compositing) {
840 kDebug(1212) << "Driver does not recommend OpenGL 1 compositing";
841 return false;
842 }
843 return true;
844}
845
846SceneOpenGL1::SceneOpenGL1(OpenGLBackend *backend)
847 : SceneOpenGL(Workspace::self(), backend)
848 , m_resetModelViewProjectionMatrix(true)
849{
850 if (!init_ok) {
851 // base ctor already failed
852 return;
853 }
854 ShaderManager::disable();
855 setupModelViewProjectionMatrix();
856 if (checkGLError("Init")) {
857 kError(1212) << "OpenGL 1 compositing setup failed";
858 init_ok = false;
859 return; // error
860 }
861
862 kDebug(1212) << "OpenGL 1 compositing successfully initialized";
863}
864
865SceneOpenGL1::~SceneOpenGL1()
866{
867}
868
869qint64 SceneOpenGL1::paint(QRegion damage, ToplevelList windows)
870{
871 if (m_resetModelViewProjectionMatrix) {
872 // reset model view projection matrix if required
873 setupModelViewProjectionMatrix();
874 }
875 return SceneOpenGL::paint(damage, windows);
876}
877
878void SceneOpenGL1::paintGenericScreen(int mask, ScreenPaintData data)
879{
880 pushMatrix(transformation(mask, data));
881 Scene::paintGenericScreen(mask, data);
882 popMatrix();
883}
884
885void SceneOpenGL1::doPaintBackground(const QVector< float >& vertices)
886{
887 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
888 vbo->reset();
889 vbo->setUseColor(true);
890 vbo->setData(vertices.count() / 2, 2, vertices.data(), NULL);
891 vbo->render(GL_TRIANGLES);
892}
893
894void SceneOpenGL1::setupModelViewProjectionMatrix()
895{
896 glMatrixMode(GL_PROJECTION);
897 glLoadIdentity();
898 float fovy = 60.0f;
899 float aspect = 1.0f;
900 float zNear = 0.1f;
901 float zFar = 100.0f;
902 float ymax = zNear * tan(fovy * M_PI / 360.0f);
903 float ymin = -ymax;
904 float xmin = ymin * aspect;
905 float xmax = ymax * aspect;
906 // swap top and bottom to have OpenGL coordinate system match X system
907 glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
908 glMatrixMode(GL_MODELVIEW);
909 glLoadIdentity();
910 float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
911 glTranslatef(xmin * scaleFactor, ymax * scaleFactor, -1.1);
912 glScalef((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001);
913 m_resetModelViewProjectionMatrix = false;
914}
915
916void SceneOpenGL1::screenGeometryChanged(const QSize &size)
917{
918 SceneOpenGL::screenGeometryChanged(size);
919 m_resetModelViewProjectionMatrix = true;
920}
921
922SceneOpenGL::Window *SceneOpenGL1::createWindow(Toplevel *t)
923{
924 return new SceneOpenGL1Window(t);
925}
926
927#endif
928
929//****************************************
930// SceneOpenGL::Texture
931//****************************************
932
933SceneOpenGL::Texture::Texture(OpenGLBackend *backend)
934 : GLTexture(*backend->createBackendTexture(this))
935{
936}
937
938SceneOpenGL::Texture::Texture(OpenGLBackend *backend, const QPixmap &pix, GLenum target)
939 : GLTexture(*backend->createBackendTexture(this))
940{
941 load(pix, target);
942}
943
944SceneOpenGL::Texture::~Texture()
945{
946}
947
948SceneOpenGL::Texture& SceneOpenGL::Texture::operator = (const SceneOpenGL::Texture& tex)
949{
950 d_ptr = tex.d_ptr;
951 return *this;
952}
953
954void SceneOpenGL::Texture::discard()
955{
956 d_ptr = d_func()->backend()->createBackendTexture(this);
957}
958
959bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
960 int depth)
961{
962 if (pix == None)
963 return false;
964 return load(pix, size, depth,
965 QRegion(0, 0, size.width(), size.height()));
966}
967
968bool SceneOpenGL::Texture::load(const QImage& image, GLenum target)
969{
970 if (image.isNull())
971 return false;
972 return load(QPixmap::fromImage(image), target);
973}
974
975bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target)
976{
977 if (pixmap.isNull())
978 return false;
979
980 // Checking whether QPixmap comes with its own X11 Pixmap
981 if (Extensions::nonNativePixmaps()) {
982 return GLTexture::load(pixmap.toImage(), target);
983 }
984
985 // use the X11 pixmap provided by Qt
986 return load(pixmap.handle(), pixmap.size(), pixmap.depth());
987}
988
989void SceneOpenGL::Texture::findTarget()
990{
991 Q_D(Texture);
992 d->findTarget();
993}
994
995bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
996 int depth, QRegion region)
997{
998 Q_UNUSED(region)
999 // decrease the reference counter for the old texture
1000 d_ptr = d_func()->backend()->createBackendTexture(this); //new TexturePrivate();
1001
1002 Q_D(Texture);
1003 return d->loadTexture(pix, size, depth);
1004}
1005
1006bool SceneOpenGL::Texture::update(const QRegion &damage)
1007{
1008 Q_D(Texture);
1009 return d->update(damage);
1010}
1011
1012//****************************************
1013// SceneOpenGL::Texture
1014//****************************************
1015SceneOpenGL::TexturePrivate::TexturePrivate()
1016{
1017}
1018
1019SceneOpenGL::TexturePrivate::~TexturePrivate()
1020{
1021}
1022
1023bool SceneOpenGL::TexturePrivate::update(const QRegion &damage)
1024{
1025 Q_UNUSED(damage)
1026 return true;
1027}
1028
1029//****************************************
1030// SceneOpenGL::Window
1031//****************************************
1032
1033SceneOpenGL::Window::Window(Toplevel* c)
1034 : Scene::Window(c)
1035 , m_scene(NULL)
1036{
1037}
1038
1039SceneOpenGL::Window::~Window()
1040{
1041}
1042
1043static SceneOpenGL::Texture *s_frameTexture = NULL;
1044// Bind the window pixmap to an OpenGL texture.
1045bool SceneOpenGL::Window::bindTexture()
1046{
1047 s_frameTexture = NULL;
1048 OpenGLWindowPixmap *pixmap = windowPixmap<OpenGLWindowPixmap>();
1049 if (!pixmap) {
1050 return false;
1051 }
1052 s_frameTexture = pixmap->texture();
1053 if (pixmap->isDiscarded()) {
1054 return !pixmap->texture()->isNull();
1055 }
1056 return pixmap->bind();
1057}
1058
1059QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const
1060{
1061 QMatrix4x4 matrix;
1062 matrix.translate(x(), y());
1063
1064 if (!(mask & PAINT_WINDOW_TRANSFORMED))
1065 return matrix;
1066
1067 matrix.translate(data.translation());
1068 data.scale().applyTo(&matrix);
1069
1070 if (data.rotationAngle() == 0.0)
1071 return matrix;
1072
1073 // Apply the rotation
1074 // cannot use data.rotation.applyTo(&matrix) as QGraphicsRotation uses projectedRotate to map back to 2D
1075 matrix.translate(data.rotationOrigin());
1076 const QVector3D axis = data.rotationAxis();
1077 matrix.rotate(data.rotationAngle(), axis.x(), axis.y(), axis.z());
1078 matrix.translate(-data.rotationOrigin());
1079
1080 return matrix;
1081}
1082
1083bool SceneOpenGL::Window::beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data)
1084{
1085 if (region.isEmpty())
1086 return false;
1087
1088 m_hardwareClipping = region != infiniteRegion() && (mask & PAINT_WINDOW_TRANSFORMED) && !(mask & PAINT_SCREEN_TRANSFORMED);
1089 if (region != infiniteRegion() && !m_hardwareClipping) {
1090 WindowQuadList quads;
1091 quads.reserve(data.quads.count());
1092
1093 const QRegion filterRegion = region.translated(-x(), -y());
1094 // split all quads in bounding rect with the actual rects in the region
1095 foreach (const WindowQuad &quad, data.quads) {
1096 foreach (const QRect &r, filterRegion.rects()) {
1097 const QRectF rf(r);
1098 const QRectF quadRect(QPointF(quad.left(), quad.top()), QPointF(quad.right(), quad.bottom()));
1099 // case 1: completely contains, include and do not check other rects
1100 if (rf.contains(quadRect)) {
1101 quads << quad;
1102 break;
1103 }
1104 // case 2: intersection
1105 if (rf.intersects(quadRect)) {
1106 const QRectF intersected = rf.intersected(quadRect);
1107 quads << quad.makeSubQuad(intersected.left(), intersected.top(), intersected.right(), intersected.bottom());
1108 }
1109 }
1110 }
1111 data.quads = quads;
1112 }
1113
1114 if (data.quads.isEmpty())
1115 return false;
1116
1117 if (!bindTexture() || !s_frameTexture) {
1118 return false;
1119 }
1120
1121 if (m_hardwareClipping) {
1122 glEnable(GL_SCISSOR_TEST);
1123 }
1124
1125 // Update the texture filter
1126 if (options->glSmoothScale() != 0 &&
1127 (mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)))
1128 filter = ImageFilterGood;
1129 else
1130 filter = ImageFilterFast;
1131
1132 s_frameTexture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST);
1133
1134 const GLVertexAttrib attribs[] = {
1135 { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) },
1136 { VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) },
1137 };
1138
1139 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
1140 vbo->reset();
1141 vbo->setAttribLayout(attribs, 2, sizeof(GLVertex2D));
1142
1143 return true;
1144}
1145
1146void SceneOpenGL::Window::endRenderWindow()
1147{
1148 if (m_hardwareClipping) {
1149 glDisable(GL_SCISSOR_TEST);
1150 }
1151}
1152
1153
1154OpenGLPaintRedirector *SceneOpenGL::Window::paintRedirector() const
1155{
1156 if (toplevel->isClient()) {
1157 Client *client = static_cast<Client *>(toplevel);
1158 if (client->noBorder())
1159 return 0;
1160
1161 return static_cast<OpenGLPaintRedirector *>(client->decorationPaintRedirector());
1162 }
1163
1164 if (toplevel->isDeleted()) {
1165 Deleted *deleted = static_cast<Deleted *>(toplevel);
1166 if (deleted->noBorder())
1167 return 0;
1168
1169 return static_cast<OpenGLPaintRedirector *>(deleted->decorationPaintRedirector());
1170 }
1171
1172 return 0;
1173}
1174
1175bool SceneOpenGL::Window::getDecorationTextures(GLTexture **textures) const
1176{
1177 OpenGLPaintRedirector *redirector = paintRedirector();
1178 if (!redirector)
1179 return false;
1180
1181 redirector->ensurePixmapsPainted();
1182
1183 textures[0] = redirector->leftRightTexture();
1184 textures[1] = redirector->topBottomTexture();
1185
1186 redirector->markAsRepainted();
1187 return true;
1188}
1189
1190void SceneOpenGL::Window::paintDecorations(const WindowPaintData &data, const QRegion &region)
1191{
1192 GLTexture *textures[2];
1193 if (!getDecorationTextures(textures))
1194 return;
1195
1196 WindowQuadList quads[2]; // left-right, top-bottom
1197
1198 // Split the quads into two lists
1199 foreach (const WindowQuad &quad, data.quads) {
1200 switch (quad.type()) {
1201 case WindowQuadDecorationLeftRight:
1202 quads[0].append(quad);
1203 continue;
1204
1205 case WindowQuadDecorationTopBottom:
1206 quads[1].append(quad);
1207 continue;
1208
1209 default:
1210 continue;
1211 }
1212 }
1213
1214 TextureType type[] = { DecorationLeftRight, DecorationTopBottom };
1215 for (int i = 0; i < 2; i++)
1216 paintDecoration(textures[i], type[i], region, data, quads[i]);
1217}
1218
1219void SceneOpenGL::Window::paintDecoration(GLTexture *texture, TextureType type,
1220 const QRegion &region, const WindowPaintData &data,
1221 const WindowQuadList &quads)
1222{
1223 if (!texture || quads.isEmpty())
1224 return;
1225
1226 if (filter == ImageFilterGood)
1227 texture->setFilter(GL_LINEAR);
1228 else
1229 texture->setFilter(GL_NEAREST);
1230
1231 texture->setWrapMode(GL_CLAMP_TO_EDGE);
1232 texture->bind();
1233
1234 prepareStates(type, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.screen());
1235 renderQuads(0, region, quads, texture, false);
1236 restoreStates(type, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation());
1237
1238 texture->unbind();
1239
1240#ifndef KWIN_HAVE_OPENGLES
1241 if (m_scene && m_scene->debug()) {
1242 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1243 GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES, m_hardwareClipping);
1244 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1245 }
1246#endif
1247}
1248
1249void SceneOpenGL::Window::paintShadow(const QRegion &region, const WindowPaintData &data)
1250{
1251 WindowQuadList quads;
1252
1253 foreach (const WindowQuad &quad, data.quads) {
1254 switch (quad.type()) {
1255 case WindowQuadShadowTopLeft:
1256 case WindowQuadShadowTop:
1257 case WindowQuadShadowTopRight:
1258 case WindowQuadShadowLeft:
1259 case WindowQuadShadowRight:
1260 case WindowQuadShadowBottomLeft:
1261 case WindowQuadShadowBottom:
1262 case WindowQuadShadowBottomRight:
1263 quads.append(quad);
1264 break;
1265
1266 default:
1267 break;
1268 }
1269 }
1270
1271 if (quads.isEmpty())
1272 return;
1273
1274 GLTexture *texture = static_cast<SceneOpenGLShadow*>(m_shadow)->shadowTexture();
1275 if (!texture) {
1276 return;
1277 }
1278 if (filter == ImageFilterGood)
1279 texture->setFilter(GL_LINEAR);
1280 else
1281 texture->setFilter(GL_NEAREST);
1282 texture->setWrapMode(GL_CLAMP_TO_EDGE);
1283 texture->bind();
1284 prepareStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.screen());
1285 renderQuads(0, region, quads, texture, true);
1286 restoreStates(Shadow, data.opacity(), data.brightness(), data.saturation());
1287 texture->unbind();
1288#ifndef KWIN_HAVE_OPENGLES
1289 if (m_scene && m_scene->debug()) {
1290 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1291 renderQuads(0, region, quads, texture, true);
1292 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1293 }
1294#endif
1295}
1296
1297void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQuadList& quads,
1298 GLTexture *tex, bool normalized)
1299{
1300 if (quads.isEmpty())
1301 return;
1302
1303 const QMatrix4x4 matrix = tex->matrix(normalized ? NormalizedCoordinates : UnnormalizedCoordinates);
1304
1305 // Render geometry
1306 GLenum primitiveType;
1307 int primcount;
1308
1309 if (GLVertexBuffer::supportsIndexedQuads()) {
1310 primitiveType = GL_QUADS_KWIN;
1311 primcount = quads.count() * 4;
1312 } else {
1313 primitiveType = GL_TRIANGLES;
1314 primcount = quads.count() * 6;
1315 }
1316
1317 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
1318 vbo->setVertexCount(primcount);
1319
1320 GLVertex2D *map = (GLVertex2D *) vbo->map(primcount * sizeof(GLVertex2D));
1321 quads.makeInterleavedArrays(primitiveType, map, matrix);
1322 vbo->unmap();
1323
1324 vbo->render(region, primitiveType, m_hardwareClipping);
1325}
1326
1327GLTexture *SceneOpenGL::Window::textureForType(SceneOpenGL::Window::TextureType type)
1328{
1329 GLTexture *tex = NULL;
1330 OpenGLPaintRedirector *redirector = NULL;
1331
1332 if (type != Content && type != Shadow) {
1333 if (toplevel->isClient()) {
1334 Client *client = static_cast<Client*>(toplevel);
1335 redirector = static_cast<OpenGLPaintRedirector*>(client->decorationPaintRedirector());
1336 } else if (toplevel->isDeleted()) {
1337 Deleted *deleted = static_cast<Deleted*>(toplevel);
1338 redirector = static_cast<OpenGLPaintRedirector*>(deleted->decorationPaintRedirector());
1339 }
1340 }
1341
1342 switch(type) {
1343 case Content:
1344 tex = s_frameTexture;
1345 break;
1346
1347 case DecorationLeftRight:
1348 tex = redirector ? redirector->leftRightTexture() : 0;
1349 break;
1350
1351 case DecorationTopBottom:
1352 tex = redirector ? redirector->topBottomTexture() : 0;
1353 break;
1354
1355 case Shadow:
1356 tex = static_cast<SceneOpenGLShadow*>(m_shadow)->shadowTexture();
1357 }
1358 return tex;
1359}
1360
1361WindowPixmap* SceneOpenGL::Window::createWindowPixmap()
1362{
1363 return new OpenGLWindowPixmap(this, m_scene);
1364}
1365
1366//***************************************
1367// SceneOpenGL2Window
1368//***************************************
1369SceneOpenGL2Window::SceneOpenGL2Window(Toplevel *c)
1370 : SceneOpenGL::Window(c)
1371 , m_blendingEnabled(false)
1372{
1373}
1374
1375SceneOpenGL2Window::~SceneOpenGL2Window()
1376{
1377}
1378
1379QVector4D SceneOpenGL2Window::modulate(float opacity, float brightness) const
1380{
1381 const float a = opacity;
1382 const float rgb = opacity * brightness;
1383
1384 return QVector4D(rgb, rgb, rgb, a);
1385}
1386
1387void SceneOpenGL2Window::setBlendEnabled(bool enabled)
1388{
1389 if (enabled && !m_blendingEnabled)
1390 glEnable(GL_BLEND);
1391 else if (!enabled && m_blendingEnabled)
1392 glDisable(GL_BLEND);
1393
1394 m_blendingEnabled = enabled;
1395}
1396
1397void SceneOpenGL2Window::setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data)
1398{
1399 if (!quads[ShadowLeaf].isEmpty()) {
1400 nodes[ShadowLeaf].texture = static_cast<SceneOpenGLShadow *>(m_shadow)->shadowTexture();
1401 nodes[ShadowLeaf].opacity = data.opacity();
1402 nodes[ShadowLeaf].hasAlpha = true;
1403 nodes[ShadowLeaf].coordinateType = NormalizedCoordinates;
1404 }
1405
1406 if (!quads[LeftRightLeaf].isEmpty() || !quads[TopBottomLeaf].isEmpty()) {
1407 GLTexture *textures[2];
1408 getDecorationTextures(textures);
1409
1410 nodes[LeftRightLeaf].texture = textures[0];
1411 nodes[LeftRightLeaf].opacity = data.opacity();
1412 nodes[LeftRightLeaf].hasAlpha = true;
1413 nodes[LeftRightLeaf].coordinateType = UnnormalizedCoordinates;
1414
1415 nodes[TopBottomLeaf].texture = textures[1];
1416 nodes[TopBottomLeaf].opacity = data.opacity();
1417 nodes[TopBottomLeaf].hasAlpha = true;
1418 nodes[TopBottomLeaf].coordinateType = UnnormalizedCoordinates;
1419 }
1420
1421 nodes[ContentLeaf].texture = s_frameTexture;
1422 nodes[ContentLeaf].hasAlpha = !isOpaque();
1423 // TODO: ARGB crsoofading is atm. a hack, playing on opacities for two dumb SrcOver operations
1424 // Should be a shader
1425 if (data.crossFadeProgress() != 1.0 && (data.opacity() < 0.95 || toplevel->hasAlpha())) {
1426 const float opacity = 1.0 - data.crossFadeProgress();
1427 nodes[ContentLeaf].opacity = data.opacity() * (1 - pow(opacity, 1.0f + 2.0f * data.opacity()));
1428 } else {
1429 nodes[ContentLeaf].opacity = data.opacity();
1430 }
1431 nodes[ContentLeaf].coordinateType = UnnormalizedCoordinates;
1432
1433 if (data.crossFadeProgress() != 1.0) {
1434 OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>();
1435 nodes[PreviousContentLeaf].texture = previous ? previous->texture() : NULL;
1436 nodes[PreviousContentLeaf].hasAlpha = !isOpaque();
1437 nodes[PreviousContentLeaf].opacity = data.opacity() * (1.0 - data.crossFadeProgress());
1438 nodes[PreviousContentLeaf].coordinateType = NormalizedCoordinates;
1439 }
1440}
1441
1442void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData data)
1443{
1444 if (!beginRenderWindow(mask, region, data))
1445 return;
1446
1447 GLShader *shader = data.shader;
1448 if (!shader) {
1449 if ((mask & Scene::PAINT_WINDOW_TRANSFORMED) || (mask & Scene::PAINT_SCREEN_TRANSFORMED)) {
1450 shader = ShaderManager::instance()->pushShader(ShaderManager::GenericShader);
1451 } else {
1452 shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
1453 shader->setUniform(GLShader::Offset, QVector2D(x(), y()));
1454 }
1455 }
1456
1457 if (ColorCorrection *cc = static_cast<SceneOpenGL2*>(m_scene)->colorCorrection()) {
1458 cc->setupForOutput(data.screen());
1459 }
1460
1461 shader->setUniform(GLShader::WindowTransformation, transformation(mask, data));
1462 shader->setUniform(GLShader::Saturation, data.saturation());
1463
1464 const GLenum filter = (mask & (Effect::PAINT_WINDOW_TRANSFORMED | Effect::PAINT_SCREEN_TRANSFORMED))
1465 && options->glSmoothScale() != 0 ? GL_LINEAR : GL_NEAREST;
1466
1467 WindowQuadList quads[LeafCount];
1468
1469 // Split the quads into separate lists for each type
1470 foreach (const WindowQuad &quad, data.quads) {
1471 switch (quad.type()) {
1472 case WindowQuadDecorationLeftRight:
1473 quads[LeftRightLeaf].append(quad);
1474 continue;
1475
1476 case WindowQuadDecorationTopBottom:
1477 quads[TopBottomLeaf].append(quad);
1478 continue;
1479
1480 case WindowQuadContents:
1481 quads[ContentLeaf].append(quad);
1482 continue;
1483
1484 case WindowQuadShadowTopLeft:
1485 case WindowQuadShadowTop:
1486 case WindowQuadShadowTopRight:
1487 case WindowQuadShadowLeft:
1488 case WindowQuadShadowRight:
1489 case WindowQuadShadowBottomLeft:
1490 case WindowQuadShadowBottom:
1491 case WindowQuadShadowBottomRight:
1492 quads[ShadowLeaf].append(quad);
1493 continue;
1494
1495 default:
1496 continue;
1497 }
1498 }
1499
1500 if (data.crossFadeProgress() != 1.0) {
1501 OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>();
1502 if (previous) {
1503 const QRect &oldGeometry = previous->contentsRect();
1504 Q_FOREACH (const WindowQuad &quad, quads[ContentLeaf]) {
1505 // we need to create new window quads with normalize texture coordinates
1506 // normal quads divide the x/y position by width/height. This would not work as the texture
1507 // is larger than the visible content in case of a decorated Client resulting in garbage being shown.
1508 // So we calculate the normalized texture coordinate in the Client's new content space and map it to
1509 // the previous Client's content space.
1510 WindowQuad newQuad(WindowQuadContents);
1511 for (int i = 0; i < 4; ++i) {
1512 const qreal xFactor = qreal(quad[i].textureX() - toplevel->clientPos().x())/qreal(toplevel->clientSize().width());
1513 const qreal yFactor = qreal(quad[i].textureY() - toplevel->clientPos().y())/qreal(toplevel->clientSize().height());
1514 WindowVertex vertex(quad[i].x(), quad[i].y(),
1515 (xFactor * oldGeometry.width() + oldGeometry.x())/qreal(previous->size().width()),
1516 (yFactor * oldGeometry.height() + oldGeometry.y())/qreal(previous->size().height()));
1517 newQuad[i] = vertex;
1518 }
1519 quads[PreviousContentLeaf].append(newQuad);
1520 }
1521 }
1522 }
1523
1524 const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads();
1525 const GLenum primitiveType = indexedQuads ? GL_QUADS_KWIN : GL_TRIANGLES;
1526 const int verticesPerQuad = indexedQuads ? 4 : 6;
1527
1528 const size_t size = verticesPerQuad *
1529 (quads[0].count() + quads[1].count() + quads[2].count() + quads[3].count() + quads[4].count()) * sizeof(GLVertex2D);
1530
1531 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
1532 GLVertex2D *map = (GLVertex2D *) vbo->map(size);
1533
1534 LeafNode nodes[LeafCount];
1535 setupLeafNodes(nodes, quads, data);
1536
1537 for (int i = 0, v = 0; i < LeafCount; i++) {
1538 if (quads[i].isEmpty() || !nodes[i].texture)
1539 continue;
1540
1541 nodes[i].firstVertex = v;
1542 nodes[i].vertexCount = quads[i].count() * verticesPerQuad;
1543
1544 const QMatrix4x4 matrix = nodes[i].texture->matrix(nodes[i].coordinateType);
1545
1546 quads[i].makeInterleavedArrays(primitiveType, &map[v], matrix);
1547 v += quads[i].count() * verticesPerQuad;
1548 }
1549
1550 vbo->unmap();
1551 vbo->bindArrays();
1552
1553 // Make sure the blend function is set up correctly in case we will be doing blending
1554 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1555
1556 float opacity = -1.0;
1557
1558 for (int i = 0; i < LeafCount; i++) {
1559 if (nodes[i].vertexCount == 0)
1560 continue;
1561
1562 setBlendEnabled(nodes[i].hasAlpha || nodes[i].opacity < 1.0);
1563
1564 if (opacity != nodes[i].opacity) {
1565 shader->setUniform(GLShader::ModulationConstant,
1566 modulate(nodes[i].opacity, data.brightness()));
1567 opacity = nodes[i].opacity;
1568 }
1569
1570 nodes[i].texture->setFilter(filter);
1571 nodes[i].texture->setWrapMode(GL_CLAMP_TO_EDGE);
1572 nodes[i].texture->bind();
1573
1574 vbo->draw(region, primitiveType, nodes[i].firstVertex, nodes[i].vertexCount, m_hardwareClipping);
1575 }
1576
1577 vbo->unbindArrays();
1578
1579 setBlendEnabled(false);
1580
1581 if (!data.shader)
1582 ShaderManager::instance()->popShader();
1583
1584 endRenderWindow();
1585}
1586
1587void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen)
1588{
1589 // setup blending of transparent windows
1590 bool opaque = isOpaque() && opacity == 1.0;
1591 bool alpha = toplevel->hasAlpha() || type != Content;
1592 if (type != Content) {
1593 if (type == Shadow) {
1594 opaque = false;
1595 } else {
1596 if (opacity == 1.0 && toplevel->isClient()) {
1597 opaque = !(static_cast<Client*>(toplevel)->decorationHasAlpha());
1598 } else {
1599 // TODO: add support in Deleted
1600 opaque = false;
1601 }
1602 }
1603 }
1604 if (!opaque) {
1605 glEnable(GL_BLEND);
1606 if (alpha) {
1607 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1608 } else {
1609 glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity);
1610 glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA);
1611 }
1612 }
1613 m_blendingEnabled = !opaque;
1614
1615 const qreal rgb = brightness * opacity;
1616 const qreal a = opacity;
1617
1618 GLShader *shader = ShaderManager::instance()->getBoundShader();
1619 shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a));
1620 shader->setUniform(GLShader::Saturation, saturation);
1621
1622 if (ColorCorrection *cc = static_cast<SceneOpenGL2*>(m_scene)->colorCorrection()) {
1623 cc->setupForOutput(screen);
1624 }
1625}
1626
1627void SceneOpenGL2Window::restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation)
1628{
1629 Q_UNUSED(type);
1630 Q_UNUSED(opacity);
1631 Q_UNUSED(brightness);
1632 Q_UNUSED(saturation);
1633 if (m_blendingEnabled) {
1634 glDisable(GL_BLEND);
1635 }
1636
1637 if (ColorCorrection *cc = static_cast<SceneOpenGL2*>(m_scene)->colorCorrection()) {
1638 cc->setupForOutput(-1);
1639 }
1640}
1641
1642//***************************************
1643// SceneOpenGL1Window
1644//***************************************
1645#ifdef KWIN_HAVE_OPENGL_1
1646SceneOpenGL1Window::SceneOpenGL1Window(Toplevel *c)
1647 : SceneOpenGL::Window(c)
1648{
1649}
1650
1651SceneOpenGL1Window::~SceneOpenGL1Window()
1652{
1653}
1654
1655// paint the window
1656void SceneOpenGL1Window::performPaint(int mask, QRegion region, WindowPaintData data)
1657{
1658 if (!beginRenderWindow(mask, region, data))
1659 return;
1660
1661 pushMatrix(transformation(mask, data));
1662
1663 // shadow
1664 if (m_shadow) {
1665 paintShadow(region, data);
1666 }
1667 // decorations
1668 paintDecorations(data, region);
1669
1670 // paint the content
1671 OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>();
1672 const WindowQuadList contentQuads = data.quads.select(WindowQuadContents);
1673 if (previous && data.crossFadeProgress() != 1.0) {
1674 // TODO: ARGB crsoofading is atm. a hack, playing on opacities for two dumb SrcOver operations
1675 // Will require a caching texture or sth. else 1.2 compliant
1676 float opacity = data.opacity();
1677 if (opacity < 0.95f || toplevel->hasAlpha()) {
1678 opacity = 1 - data.crossFadeProgress();
1679 opacity = data.opacity() * (1 - pow(opacity, 1.0f + 2.0f * data.opacity()));
1680 }
1681 paintContent(s_frameTexture, region, mask, opacity, data, contentQuads, false);
1682 previous->texture()->setFilter(filter == Scene::ImageFilterGood ? GL_LINEAR : GL_NEAREST);
1683 WindowQuadList oldContents;
1684 const QRect &oldGeometry = previous->contentsRect();
1685 Q_FOREACH (const WindowQuad &quad, contentQuads) {
1686 // we need to create new window quads with normalize texture coordinates
1687 // normal quads divide the x/y position by width/height. This would not work as the texture
1688 // is larger than the visible content in case of a decorated Client resulting in garbage being shown.
1689 // So we calculate the normalized texture coordinate in the Client's new content space and map it to
1690 // the previous Client's content space.
1691 WindowQuad newQuad(WindowQuadContents);
1692 for (int i = 0; i < 4; ++i) {
1693 const qreal xFactor = qreal(quad[i].textureX() - toplevel->clientPos().x())/qreal(toplevel->clientSize().width());
1694 const qreal yFactor = qreal(quad[i].textureY() - toplevel->clientPos().y())/qreal(toplevel->clientSize().height());
1695 WindowVertex vertex(quad[i].x(), quad[i].y(),
1696 (xFactor * oldGeometry.width() + oldGeometry.x())/qreal(previous->size().width()),
1697 (yFactor * oldGeometry.height() + oldGeometry.y())/qreal(previous->size().height()));
1698 newQuad[i] = vertex;
1699 }
1700 oldContents.append(newQuad);
1701 }
1702 opacity = data.opacity() * (1.0 - data.crossFadeProgress());
1703 paintContent(previous->texture(), region, mask, opacity, data, oldContents, true);
1704 } else {
1705 paintContent(s_frameTexture, region, mask, data.opacity(), data, contentQuads, false);
1706 }
1707
1708 popMatrix();
1709
1710 endRenderWindow();
1711}
1712
1713void SceneOpenGL1Window::paintContent(SceneOpenGL::Texture* content, const QRegion& region, int mask,
1714 qreal opacity, const WindowPaintData& data, const WindowQuadList &contentQuads, bool normalized)
1715{
1716 if (contentQuads.isEmpty()) {
1717 return;
1718 }
1719 content->bind();
1720 prepareStates(Content, opacity, data.brightness(), data.saturation(), data.screen());
1721 renderQuads(mask, region, contentQuads, content, normalized);
1722 restoreStates(Content, opacity, data.brightness(), data.saturation());
1723 content->unbind();
1724#ifndef KWIN_HAVE_OPENGLES
1725 if (m_scene && m_scene->debug()) {
1726 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1727 renderQuads(mask, region, contentQuads, content, normalized);
1728 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1729 }
1730#endif
1731}
1732
1733void SceneOpenGL1Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen)
1734{
1735 Q_UNUSED(screen)
1736
1737 GLTexture *tex = textureForType(type);
1738 bool alpha = false;
1739 bool opaque = true;
1740 if (type == Content) {
1741 alpha = toplevel->hasAlpha();
1742 opaque = isOpaque() && opacity == 1.0;
1743 } else {
1744 alpha = true;
1745 opaque = false;
1746 }
1747 // setup blending of transparent windows
1748 glPushAttrib(GL_ENABLE_BIT);
1749 if (!opaque) {
1750 glEnable(GL_BLEND);
1751 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1752 }
1753 if (saturation != 1.0 && tex->saturationSupported()) {
1754 // First we need to get the color from [0; 1] range to [0.5; 1] range
1755 glActiveTexture(GL_TEXTURE0);
1756 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1757 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
1758 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
1759 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
1760 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
1761 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
1762 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
1763 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
1764 const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5};
1765 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant);
1766 tex->bind();
1767
1768 // Then we take dot product of the result of previous pass and
1769 // saturation_constant. This gives us completely unsaturated
1770 // (greyscale) image
1771 // Note that both operands have to be in range [0.5; 1] since opengl
1772 // automatically substracts 0.5 from them
1773 glActiveTexture(GL_TEXTURE1);
1774 float saturation_constant[] = { 0.5 + 0.5 * 0.30, 0.5 + 0.5 * 0.59, 0.5 + 0.5 * 0.11,
1775 static_cast<float>(saturation) };
1776 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1777 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
1778 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
1779 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
1780 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
1781 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
1782 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant);
1783 tex->bind();
1784
1785 // Finally we need to interpolate between the original image and the
1786 // greyscale image to get wanted level of saturation
1787 glActiveTexture(GL_TEXTURE2);
1788 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1789 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
1790 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
1791 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
1792 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
1793 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
1794 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
1795 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
1796 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant);
1797 // Also replace alpha by primary color's alpha here
1798 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
1799 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
1800 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
1801 // And make primary color contain the wanted opacity
1802 glColor4f(opacity, opacity, opacity, opacity);
1803 tex->bind();
1804
1805 if (alpha || !opaque || brightness != 1.0f) {
1806 glActiveTexture(GL_TEXTURE3);
1807 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1808 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
1809 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
1810 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
1811 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
1812 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
1813 // The color has to be multiplied by both opacity and brightness
1814 float opacityByBrightness = opacity * brightness;
1815 glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity);
1816 if (alpha) {
1817 // Multiply original texture's alpha by our opacity
1818 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
1819 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
1820 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
1821 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
1822 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
1823 } else {
1824 // Alpha will be taken from previous stage
1825 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
1826 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
1827 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
1828 }
1829 tex->bind();
1830 }
1831
1832 glActiveTexture(GL_TEXTURE0);
1833 } else if (opacity != 1.0 || brightness != 1.0) {
1834 // the window is additionally configured to have its opacity adjusted,
1835 // do it
1836 float opacityByBrightness = opacity * brightness;
1837 if (alpha) {
1838 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1839 glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness,
1840 opacity);
1841 } else {
1842 // Multiply color by brightness and replace alpha by opacity
1843 float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness,
1844 static_cast<float>(opacity) };
1845 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1846 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
1847 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
1848 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
1849 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
1850 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
1851 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
1852 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
1853 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
1854 }
1855 } else if (!alpha && opaque) {
1856 float constant[] = { 1.0, 1.0, 1.0, 1.0 };
1857 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1858 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
1859 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
1860 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
1861 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
1862 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
1863 }
1864}
1865
1866void SceneOpenGL1Window::restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation)
1867{
1868 GLTexture *tex = textureForType(type);
1869 if (opacity != 1.0 || saturation != 1.0 || brightness != 1.0f) {
1870 if (saturation != 1.0 && tex->saturationSupported()) {
1871 glActiveTexture(GL_TEXTURE3);
1872 glDisable(tex->target());
1873 glActiveTexture(GL_TEXTURE2);
1874 glDisable(tex->target());
1875 glActiveTexture(GL_TEXTURE1);
1876 glDisable(tex->target());
1877 glActiveTexture(GL_TEXTURE0);
1878 }
1879 }
1880 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
1881 glColor4f(0, 0, 0, 0);
1882
1883 glPopAttrib(); // ENABLE_BIT
1884}
1885#endif
1886
1887//****************************************
1888// OpenGLWindowPixmap
1889//****************************************
1890
1891OpenGLWindowPixmap::OpenGLWindowPixmap(Scene::Window *window, SceneOpenGL* scene)
1892 : WindowPixmap(window)
1893 , m_scene(scene)
1894 , m_texture(scene->createTexture())
1895{
1896}
1897
1898OpenGLWindowPixmap::~OpenGLWindowPixmap()
1899{
1900}
1901
1902bool OpenGLWindowPixmap::bind()
1903{
1904 if (!m_texture->isNull()) {
1905 if (!toplevel()->damage().isEmpty()) {
1906 const bool success = m_texture->update(toplevel()->damage());
1907 // mipmaps need to be updated
1908 m_texture->setDirty();
1909 toplevel()->resetDamage();
1910 return success;
1911 }
1912 return true;
1913 }
1914 if (!isValid()) {
1915 return false;
1916 }
1917
1918 bool success = m_texture->load(pixmap(), toplevel()->size(), toplevel()->depth(), toplevel()->damage());
1919
1920 if (success)
1921 toplevel()->resetDamage();
1922 else
1923 kDebug(1212) << "Failed to bind window";
1924 return success;
1925}
1926
1927//****************************************
1928// SceneOpenGL::EffectFrame
1929//****************************************
1930
1931GLTexture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL;
1932QPixmap* SceneOpenGL::EffectFrame::m_unstyledPixmap = NULL;
1933
1934SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame, SceneOpenGL *scene)
1935 : Scene::EffectFrame(frame)
1936 , m_texture(NULL)
1937 , m_textTexture(NULL)
1938 , m_oldTextTexture(NULL)
1939 , m_textPixmap(NULL)
1940 , m_iconTexture(NULL)
1941 , m_oldIconTexture(NULL)
1942 , m_selectionTexture(NULL)
1943 , m_unstyledVBO(NULL)
1944 , m_scene(scene)
1945{
1946 if (m_effectFrame->style() == EffectFrameUnstyled && !m_unstyledTexture) {
1947 updateUnstyledTexture();
1948 }
1949}
1950
1951SceneOpenGL::EffectFrame::~EffectFrame()
1952{
1953 delete m_texture;
1954 delete m_textTexture;
1955 delete m_textPixmap;
1956 delete m_oldTextTexture;
1957 delete m_iconTexture;
1958 delete m_oldIconTexture;
1959 delete m_selectionTexture;
1960 delete m_unstyledVBO;
1961}
1962
1963void SceneOpenGL::EffectFrame::free()
1964{
1965 glFlush();
1966 delete m_texture;
1967 m_texture = NULL;
1968 delete m_textTexture;
1969 m_textTexture = NULL;
1970 delete m_textPixmap;
1971 m_textPixmap = NULL;
1972 delete m_iconTexture;
1973 m_iconTexture = NULL;
1974 delete m_selectionTexture;
1975 m_selectionTexture = NULL;
1976 delete m_unstyledVBO;
1977 m_unstyledVBO = NULL;
1978 delete m_oldIconTexture;
1979 m_oldIconTexture = NULL;
1980 delete m_oldTextTexture;
1981 m_oldTextTexture = NULL;
1982}
1983
1984void SceneOpenGL::EffectFrame::freeIconFrame()
1985{
1986 delete m_iconTexture;
1987 m_iconTexture = NULL;
1988}
1989
1990void SceneOpenGL::EffectFrame::freeTextFrame()
1991{
1992 delete m_textTexture;
1993 m_textTexture = NULL;
1994 delete m_textPixmap;
1995 m_textPixmap = NULL;
1996}
1997
1998void SceneOpenGL::EffectFrame::freeSelection()
1999{
2000 delete m_selectionTexture;
2001 m_selectionTexture = NULL;
2002}
2003
2004void SceneOpenGL::EffectFrame::crossFadeIcon()
2005{
2006 delete m_oldIconTexture;
2007 m_oldIconTexture = m_iconTexture;
2008 m_iconTexture = NULL;
2009}
2010
2011void SceneOpenGL::EffectFrame::crossFadeText()
2012{
2013 delete m_oldTextTexture;
2014 m_oldTextTexture = m_textTexture;
2015 m_textTexture = NULL;
2016}
2017
2018void SceneOpenGL::EffectFrame::render(QRegion region, double opacity, double frameOpacity)
2019{
2020 if (m_effectFrame->geometry().isEmpty())
2021 return; // Nothing to display
2022
2023 region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL
2024
2025 GLShader* shader = m_effectFrame->shader();
2026 bool sceneShader = false;
2027 if (!shader && ShaderManager::instance()->isValid()) {
2028 shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
2029 sceneShader = true;
2030 } else if (shader) {
2031 ShaderManager::instance()->pushShader(shader);
2032 }
2033
2034 if (shader) {
2035 if (sceneShader)
2036 shader->setUniform(GLShader::Offset, QVector2D(0, 0));
2037
2038 shader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0));
2039 shader->setUniform(GLShader::Saturation, 1.0f);
2040 }
2041
2042 glEnable(GL_BLEND);
2043 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2044#ifdef KWIN_HAVE_OPENGL_1
2045 if (!shader)
2046 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2047#endif
2048
2049 // Render the actual frame
2050 if (m_effectFrame->style() == EffectFrameUnstyled) {
2051 if (!m_unstyledVBO) {
2052 m_unstyledVBO = new GLVertexBuffer(GLVertexBuffer::Static);
2053 QRect area = m_effectFrame->geometry();
2054 area.moveTo(0, 0);
2055 area.adjust(-5, -5, 5, 5);
2056
2057 const int roundness = 5;
2058 QVector<float> verts, texCoords;
2059 verts.reserve(84);
2060 texCoords.reserve(84);
2061
2062 // top left
2063 verts << area.left() << area.top();
2064 texCoords << 0.0f << 0.0f;
2065 verts << area.left() << area.top() + roundness;
2066 texCoords << 0.0f << 0.5f;
2067 verts << area.left() + roundness << area.top();
2068 texCoords << 0.5f << 0.0f;
2069 verts << area.left() + roundness << area.top() + roundness;
2070 texCoords << 0.5f << 0.5f;
2071 verts << area.left() << area.top() + roundness;
2072 texCoords << 0.0f << 0.5f;
2073 verts << area.left() + roundness << area.top();
2074 texCoords << 0.5f << 0.0f;
2075 // top
2076 verts << area.left() + roundness << area.top();
2077 texCoords << 0.5f << 0.0f;
2078 verts << area.left() + roundness << area.top() + roundness;
2079 texCoords << 0.5f << 0.5f;
2080 verts << area.right() - roundness << area.top();
2081 texCoords << 0.5f << 0.0f;
2082 verts << area.left() + roundness << area.top() + roundness;
2083 texCoords << 0.5f << 0.5f;
2084 verts << area.right() - roundness << area.top() + roundness;
2085 texCoords << 0.5f << 0.5f;
2086 verts << area.right() - roundness << area.top();
2087 texCoords << 0.5f << 0.0f;
2088 // top right
2089 verts << area.right() - roundness << area.top();
2090 texCoords << 0.5f << 0.0f;
2091 verts << area.right() - roundness << area.top() + roundness;
2092 texCoords << 0.5f << 0.5f;
2093 verts << area.right() << area.top();
2094 texCoords << 1.0f << 0.0f;
2095 verts << area.right() - roundness << area.top() + roundness;
2096 texCoords << 0.5f << 0.5f;
2097 verts << area.right() << area.top() + roundness;
2098 texCoords << 1.0f << 0.5f;
2099 verts << area.right() << area.top();
2100 texCoords << 1.0f << 0.0f;
2101 // bottom left
2102 verts << area.left() << area.bottom() - roundness;
2103 texCoords << 0.0f << 0.5f;
2104 verts << area.left() << area.bottom();
2105 texCoords << 0.0f << 1.0f;
2106 verts << area.left() + roundness << area.bottom() - roundness;
2107 texCoords << 0.5f << 0.5f;
2108 verts << area.left() + roundness << area.bottom();
2109 texCoords << 0.5f << 1.0f;
2110 verts << area.left() << area.bottom();
2111 texCoords << 0.0f << 1.0f;
2112 verts << area.left() + roundness << area.bottom() - roundness;
2113 texCoords << 0.5f << 0.5f;
2114 // bottom
2115 verts << area.left() + roundness << area.bottom() - roundness;
2116 texCoords << 0.5f << 0.5f;
2117 verts << area.left() + roundness << area.bottom();
2118 texCoords << 0.5f << 1.0f;
2119 verts << area.right() - roundness << area.bottom() - roundness;
2120 texCoords << 0.5f << 0.5f;
2121 verts << area.left() + roundness << area.bottom();
2122 texCoords << 0.5f << 1.0f;
2123 verts << area.right() - roundness << area.bottom();
2124 texCoords << 0.5f << 1.0f;
2125 verts << area.right() - roundness << area.bottom() - roundness;
2126 texCoords << 0.5f << 0.5f;
2127 // bottom right
2128 verts << area.right() - roundness << area.bottom() - roundness;
2129 texCoords << 0.5f << 0.5f;
2130 verts << area.right() - roundness << area.bottom();
2131 texCoords << 0.5f << 1.0f;
2132 verts << area.right() << area.bottom() - roundness;
2133 texCoords << 1.0f << 0.5f;
2134 verts << area.right() - roundness << area.bottom();
2135 texCoords << 0.5f << 1.0f;
2136 verts << area.right() << area.bottom();
2137 texCoords << 1.0f << 1.0f;
2138 verts << area.right() << area.bottom() - roundness;
2139 texCoords << 1.0f << 0.5f;
2140 // center
2141 verts << area.left() << area.top() + roundness;
2142 texCoords << 0.0f << 0.5f;
2143 verts << area.left() << area.bottom() - roundness;
2144 texCoords << 0.0f << 0.5f;
2145 verts << area.right() << area.top() + roundness;
2146 texCoords << 1.0f << 0.5f;
2147 verts << area.left() << area.bottom() - roundness;
2148 texCoords << 0.0f << 0.5f;
2149 verts << area.right() << area.bottom() - roundness;
2150 texCoords << 1.0f << 0.5f;
2151 verts << area.right() << area.top() + roundness;
2152 texCoords << 1.0f << 0.5f;
2153
2154 m_unstyledVBO->setData(verts.count() / 2, 2, verts.data(), texCoords.data());
2155 }
2156
2157 if (shader) {
2158 const float a = opacity * frameOpacity;
2159 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2160 }
2161#ifdef KWIN_HAVE_OPENGL_1
2162 else
2163 glColor4f(0.0, 0.0, 0.0, opacity * frameOpacity);
2164#endif
2165
2166 m_unstyledTexture->bind();
2167 const QPoint pt = m_effectFrame->geometry().topLeft();
2168 if (sceneShader) {
2169 shader->setUniform(GLShader::Offset, QVector2D(pt.x(), pt.y()));
2170 } else {
2171 QMatrix4x4 translation;
2172 translation.translate(pt.x(), pt.y());
2173 if (shader) {
2174 shader->setUniform(GLShader::WindowTransformation, translation);
2175 } else {
2176 pushMatrix(translation);
2177 }
2178 }
2179 m_unstyledVBO->render(region, GL_TRIANGLES);
2180 if (!sceneShader) {
2181 if (shader) {
2182 shader->setUniform(GLShader::WindowTransformation, QMatrix4x4());
2183 } else {
2184 popMatrix();
2185 }
2186 }
2187 m_unstyledTexture->unbind();
2188 } else if (m_effectFrame->style() == EffectFrameStyled) {
2189 if (!m_texture) // Lazy creation
2190 updateTexture();
2191
2192 if (shader) {
2193 const float a = opacity * frameOpacity;
2194 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2195 }
2196#ifdef KWIN_HAVE_OPENGL_1
2197 else
2198 glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity);
2199#endif
2200 m_texture->bind();
2201 qreal left, top, right, bottom;
2202 m_effectFrame->frame().getMargins(left, top, right, bottom); // m_geometry is the inner geometry
2203 m_texture->render(region, m_effectFrame->geometry().adjusted(-left, -top, right, bottom));
2204 m_texture->unbind();
2205
2206 }
2207 if (!m_effectFrame->selection().isNull()) {
2208 if (!m_selectionTexture) { // Lazy creation
2209 QPixmap pixmap = m_effectFrame->selectionFrame().framePixmap();
2210 if (!pixmap.isNull())
2211 m_selectionTexture = m_scene->createTexture(pixmap);
2212 }
2213 if (m_selectionTexture) {
2214 if (shader) {
2215 const float a = opacity * frameOpacity;
2216 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2217 }
2218 #ifdef KWIN_HAVE_OPENGL_1
2219 else
2220 glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity);
2221 #endif
2222 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2223 m_selectionTexture->bind();
2224 m_selectionTexture->render(region, m_effectFrame->selection());
2225 m_selectionTexture->unbind();
2226 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2227 }
2228 }
2229
2230 // Render icon
2231 if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) {
2232 QPoint topLeft(m_effectFrame->geometry().x(),
2233 m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2);
2234
2235 if (m_effectFrame->isCrossFade() && m_oldIconTexture) {
2236 if (shader) {
2237 const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress());
2238 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2239 }
2240#ifdef KWIN_HAVE_OPENGL_1
2241 else
2242 glColor4f(1.0, 1.0, 1.0, opacity * (1.0 - m_effectFrame->crossFadeProgress()));
2243#endif
2244
2245 m_oldIconTexture->bind();
2246 m_oldIconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize()));
2247 m_oldIconTexture->unbind();
2248 if (shader) {
2249 const float a = opacity * m_effectFrame->crossFadeProgress();
2250 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2251 }
2252#ifdef KWIN_HAVE_OPENGL_1
2253 else
2254 glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress());
2255#endif
2256 } else {
2257 if (shader) {
2258 const QVector4D constant(opacity, opacity, opacity, opacity);
2259 shader->setUniform(GLShader::ModulationConstant, constant);
2260 }
2261#ifdef KWIN_HAVE_OPENGL_1
2262 else
2263 glColor4f(1.0, 1.0, 1.0, opacity);
2264#endif
2265 }
2266
2267 if (!m_iconTexture) { // lazy creation
2268 m_iconTexture = m_scene->createTexture(m_effectFrame->icon());
2269 }
2270 m_iconTexture->bind();
2271 m_iconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize()));
2272 m_iconTexture->unbind();
2273 }
2274
2275 // Render text
2276 if (!m_effectFrame->text().isEmpty()) {
2277 if (m_effectFrame->isCrossFade() && m_oldTextTexture) {
2278 if (shader) {
2279 const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress());
2280 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2281 }
2282#ifdef KWIN_HAVE_OPENGL_1
2283 else
2284 glColor4f(1.0, 1.0, 1.0, opacity *(1.0 - m_effectFrame->crossFadeProgress()));
2285#endif
2286
2287 m_oldTextTexture->bind();
2288 m_oldTextTexture->render(region, m_effectFrame->geometry());
2289 m_oldTextTexture->unbind();
2290 if (shader) {
2291 const float a = opacity * m_effectFrame->crossFadeProgress();
2292 shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
2293 }
2294#ifdef KWIN_HAVE_OPENGL_1
2295 else
2296 glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress());
2297#endif
2298 } else {
2299 if (shader) {
2300 const QVector4D constant(opacity, opacity, opacity, opacity);
2301 shader->setUniform(GLShader::ModulationConstant, constant);
2302 }
2303#ifdef KWIN_HAVE_OPENGL_1
2304 else
2305 glColor4f(1.0, 1.0, 1.0, opacity);
2306#endif
2307 }
2308 if (!m_textTexture) // Lazy creation
2309 updateTextTexture();
2310 m_textTexture->bind();
2311 m_textTexture->render(region, m_effectFrame->geometry());
2312 m_textTexture->unbind();
2313 }
2314
2315 if (shader) {
2316 ShaderManager::instance()->popShader();
2317 }
2318 glDisable(GL_BLEND);
2319}
2320
2321void SceneOpenGL::EffectFrame::updateTexture()
2322{
2323 delete m_texture;
2324 m_texture = 0L;
2325 if (m_effectFrame->style() == EffectFrameStyled) {
2326 QPixmap pixmap = m_effectFrame->frame().framePixmap();
2327 m_texture = m_scene->createTexture(pixmap);
2328 }
2329}
2330
2331void SceneOpenGL::EffectFrame::updateTextTexture()
2332{
2333 delete m_textTexture;
2334 m_textTexture = 0L;
2335 delete m_textPixmap;
2336 m_textPixmap = 0L;
2337
2338 if (m_effectFrame->text().isEmpty())
2339 return;
2340
2341 // Determine position on texture to paint text
2342 QRect rect(QPoint(0, 0), m_effectFrame->geometry().size());
2343 if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty())
2344 rect.setLeft(m_effectFrame->iconSize().width());
2345
2346 // If static size elide text as required
2347 QString text = m_effectFrame->text();
2348 if (m_effectFrame->isStatic()) {
2349 QFontMetrics metrics(m_effectFrame->font());
2350 text = metrics.elidedText(text, Qt::ElideRight, rect.width());
2351 }
2352
2353 m_textPixmap = new QPixmap(m_effectFrame->geometry().size());
2354 m_textPixmap->fill(Qt::transparent);
2355 QPainter p(m_textPixmap);
2356 p.setFont(m_effectFrame->font());
2357 if (m_effectFrame->style() == EffectFrameStyled)
2358 p.setPen(m_effectFrame->styledTextColor());
2359 else // TODO: What about no frame? Custom color setting required
2360 p.setPen(Qt::white);
2361 p.drawText(rect, m_effectFrame->alignment(), text);
2362 p.end();
2363 m_textTexture = m_scene->createTexture(*m_textPixmap);
2364}
2365
2366void SceneOpenGL::EffectFrame::updateUnstyledTexture()
2367{
2368 delete m_unstyledTexture;
2369 m_unstyledTexture = 0L;
2370 delete m_unstyledPixmap;
2371 m_unstyledPixmap = 0L;
2372 // Based off circle() from kwinxrenderutils.cpp
2373#define CS 8
2374 m_unstyledPixmap = new QPixmap(2 * CS, 2 * CS);
2375 m_unstyledPixmap->fill(Qt::transparent);
2376 QPainter p(m_unstyledPixmap);
2377 p.setRenderHint(QPainter::Antialiasing);
2378 p.setPen(Qt::NoPen);
2379 p.setBrush(Qt::black);
2380 p.drawEllipse(m_unstyledPixmap->rect());
2381 p.end();
2382#undef CS
2383 m_unstyledTexture = new GLTexture(*m_unstyledPixmap);
2384}
2385
2386void SceneOpenGL::EffectFrame::cleanup()
2387{
2388 delete m_unstyledTexture;
2389 m_unstyledTexture = NULL;
2390 delete m_unstyledPixmap;
2391 m_unstyledPixmap = NULL;
2392}
2393
2394//****************************************
2395// SceneOpenGL::Shadow
2396//****************************************
2397SceneOpenGLShadow::SceneOpenGLShadow(Toplevel *toplevel)
2398 : Shadow(toplevel)
2399 , m_texture(NULL)
2400{
2401}
2402
2403SceneOpenGLShadow::~SceneOpenGLShadow()
2404{
2405 delete m_texture;
2406}
2407
2408void SceneOpenGLShadow::buildQuads()
2409{
2410 // prepare window quads
2411 m_shadowQuads.clear();
2412 const QSizeF top(shadowPixmap(ShadowElementTop).size());
2413 const QSizeF topRight(shadowPixmap(ShadowElementTopRight).size());
2414 const QSizeF right(shadowPixmap(ShadowElementRight).size());
2415 const QSizeF bottomRight(shadowPixmap(ShadowElementBottomRight).size());
2416 const QSizeF bottom(shadowPixmap(ShadowElementBottom).size());
2417 const QSizeF bottomLeft(shadowPixmap(ShadowElementBottomLeft).size());
2418 const QSizeF left(shadowPixmap(ShadowElementLeft).size());
2419 const QSizeF topLeft(shadowPixmap(ShadowElementTopLeft).size());
2420 if ((left.width() - leftOffset() > topLevel()->width()) ||
2421 (right.width() - rightOffset() > topLevel()->width()) ||
2422 (top.height() - topOffset() > topLevel()->height()) ||
2423 (bottom.height() - bottomOffset() > topLevel()->height())) {
2424 // if our shadow is bigger than the window, we don't render the shadow
2425 setShadowRegion(QRegion());
2426 return;
2427 }
2428
2429 const QRectF outerRect(QPointF(-leftOffset(), -topOffset()),
2430 QPointF(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset()));
2431
2432 const qreal width = topLeft.width() + top.width() + topRight.width();
2433 const qreal height = topLeft.height() + left.height() + bottomLeft.height();
2434
2435 qreal tx1(0.0), tx2(0.0), ty1(0.0), ty2(0.0);
2436
2437 tx2 = topLeft.width()/width;
2438 ty2 = topLeft.height()/height;
2439 WindowQuad topLeftQuad(WindowQuadShadowTopLeft);
2440 topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), tx1, ty1);
2441 topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx2, ty1);
2442 topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), tx2, ty2);
2443 topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty2);
2444 m_shadowQuads.append(topLeftQuad);
2445
2446 tx1 = tx2;
2447 tx2 = (topLeft.width() + top.width())/width;
2448 ty2 = top.height()/height;
2449 WindowQuad topQuad(WindowQuadShadowTop);
2450 topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx1, ty1);
2451 topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx2, ty1);
2452 topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(),tx2, ty2);
2453 topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), tx1, ty2);
2454 m_shadowQuads.append(topQuad);
2455
2456 tx1 = tx2;
2457 tx2 = 1.0;
2458 ty2 = topRight.height()/height;
2459 WindowQuad topRightQuad(WindowQuadShadowTopRight);
2460 topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx1, ty1);
2461 topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), tx2, ty1);
2462 topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty2);
2463 topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), tx1, ty2);
2464 m_shadowQuads.append(topRightQuad);
2465
2466 tx1 = (width - right.width())/width;
2467 ty1 = topRight.height()/height;
2468 ty2 = (topRight.height() + right.height())/height;
2469 WindowQuad rightQuad(WindowQuadShadowRight);
2470 rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), tx1, ty1);
2471 rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty1);
2472 rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty2);
2473 rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), tx1, ty2);
2474 m_shadowQuads.append(rightQuad);
2475
2476 tx1 = (width - bottomRight.width())/width;
2477 ty1 = ty2;
2478 ty2 = 1.0;
2479 WindowQuad bottomRightQuad(WindowQuadShadowBottomRight);
2480 bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), tx1, ty1);
2481 bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty1);
2482 bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), tx2, ty2);
2483 bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx1, ty2);
2484 m_shadowQuads.append(bottomRightQuad);
2485
2486 tx2 = tx1;
2487 tx1 = bottomLeft.width()/width;
2488 ty1 = (height - bottom.height())/height;
2489 WindowQuad bottomQuad(WindowQuadShadowBottom);
2490 bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), tx1, ty1);
2491 bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), tx2, ty1);
2492 bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx2, ty2);
2493 bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx1, ty2);
2494 m_shadowQuads.append(bottomQuad);
2495
2496 tx1 = 0.0;
2497 tx2 = bottomLeft.width()/width;
2498 ty1 = (height - bottomLeft.height())/height;
2499 WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft);
2500 bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty1);
2501 bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty1);
2502 bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx2, ty2);
2503 bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), tx1, ty2);
2504 m_shadowQuads.append(bottomLeftQuad);
2505
2506 tx2 = left.width()/width;
2507 ty2 = ty1;
2508 ty1 = topLeft.height()/height;
2509 WindowQuad leftQuad(WindowQuadShadowLeft);
2510 leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty1);
2511 leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), tx2, ty1);
2512 leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty2);
2513 leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty2);
2514 m_shadowQuads.append(leftQuad);
2515}
2516
2517bool SceneOpenGLShadow::prepareBackend()
2518{
2519 const QSize top(shadowPixmap(ShadowElementTop).size());
2520 const QSize topRight(shadowPixmap(ShadowElementTopRight).size());
2521 const QSize right(shadowPixmap(ShadowElementRight).size());
2522 const QSize bottomRight(shadowPixmap(ShadowElementBottomRight).size());
2523 const QSize bottom(shadowPixmap(ShadowElementBottom).size());
2524 const QSize bottomLeft(shadowPixmap(ShadowElementBottomLeft).size());
2525 const QSize left(shadowPixmap(ShadowElementLeft).size());
2526 const QSize topLeft(shadowPixmap(ShadowElementTopLeft).size());
2527
2528 const int width = topLeft.width() + top.width() + topRight.width();
2529 const int height = topLeft.height() + left.height() + bottomLeft.height();
2530
2531 QImage image(width, height, QImage::Format_ARGB32);
2532 image.fill(Qt::transparent);
2533 QPainter p;
2534 p.begin(&image);
2535 p.drawPixmap(0, 0, shadowPixmap(ShadowElementTopLeft));
2536 p.drawPixmap(topLeft.width(), 0, shadowPixmap(ShadowElementTop));
2537 p.drawPixmap(topLeft.width() + top.width(), 0, shadowPixmap(ShadowElementTopRight));
2538 p.drawPixmap(0, topLeft.height(), shadowPixmap(ShadowElementLeft));
2539 p.drawPixmap(width - right.width(), topRight.height(), shadowPixmap(ShadowElementRight));
2540 p.drawPixmap(0, topLeft.height() + left.height(), shadowPixmap(ShadowElementBottomLeft));
2541 p.drawPixmap(bottomLeft.width(), height - bottom.height(), shadowPixmap(ShadowElementBottom));
2542 p.drawPixmap(bottomLeft.width() + bottom.width(), topRight.height() + right.height(), shadowPixmap(ShadowElementBottomRight));
2543 p.end();
2544
2545 delete m_texture;
2546 m_texture = new GLTexture(image);
2547
2548 return true;
2549}
2550
2551SwapProfiler::SwapProfiler()
2552{
2553 init();
2554}
2555
2556void SwapProfiler::init()
2557{
2558 m_time = 2 * 1000*1000; // we start with a long time mean of 2ms ...
2559 m_counter = 0;
2560}
2561
2562void SwapProfiler::begin()
2563{
2564 m_timer.start();
2565}
2566
2567char SwapProfiler::end()
2568{
2569 // .. and blend in actual values.
2570 // this way we prevent extremes from killing our long time mean
2571 m_time = (10*m_time + m_timer.nsecsElapsed())/11;
2572 if (++m_counter > 500) {
2573 const bool blocks = m_time > 1000 * 1000; // 1ms, i get ~250µs and ~7ms w/o triple buffering...
2574 kDebug(1212) << "Triple buffering detection:" << QString(blocks ? "NOT available" : "Available") <<
2575 " - Mean block time:" << m_time/(1000.0*1000.0) << "ms";
2576 return blocks ? 'd' : 't';
2577 }
2578 return 0;
2579}
2580
2581} // namespace
2582