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
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>.
20*********************************************************************/
21
22#ifndef KWIN_SCENE_OPENGL_H
23#define KWIN_SCENE_OPENGL_H
24
25#include "scene.h"
26#include "shadow.h"
27
28#include "kwinglutils.h"
29#include "kwingltexture_p.h"
30
31namespace KWin
32{
33class ColorCorrection;
34class LanczosFilter;
35class OpenGLBackend;
36class OpenGLPaintRedirector;
37
38class SceneOpenGL
39 : public Scene
40{
41 Q_OBJECT
42public:
43 class EffectFrame;
44 class Texture;
45 class TexturePrivate;
46 class Window;
47 virtual ~SceneOpenGL();
48 virtual bool initFailed() const;
49 virtual bool hasPendingFlush() const;
50 virtual qint64 paint(QRegion damage, ToplevelList windows);
51 virtual void windowAdded(Toplevel*);
52 virtual void windowDeleted(Deleted*);
53 virtual void screenGeometryChanged(const QSize &size);
54 virtual OverlayWindow *overlayWindow();
55 virtual bool blocksForRetrace() const;
56 virtual bool syncsToVBlank() const;
57
58 void idle();
59
60 bool debug() const { return m_debug; }
61
62 /**
63 * @brief Factory method to create a backend specific texture.
64 *
65 * @return :SceneOpenGL::Texture*
66 **/
67 Texture *createTexture();
68 Texture *createTexture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
69
70#ifndef KWIN_HAVE_OPENGLES
71 /**
72 * Copy a region of pixels from the current read to the current draw buffer
73 */
74 static void copyPixels(const QRegion &region);
75#endif
76
77 static SceneOpenGL *createScene();
78
79protected:
80 SceneOpenGL(Workspace* ws, OpenGLBackend *backend);
81 virtual void paintBackground(QRegion region);
82 virtual void extendPaintRegion(QRegion &region, bool opaqueFullscreen);
83 QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const;
84 virtual void paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data);
85
86 void handleGraphicsReset(GLenum status);
87
88 virtual void doPaintBackground(const QVector<float> &vertices) = 0;
89 virtual SceneOpenGL::Window *createWindow(Toplevel *t) = 0;
90
91Q_SIGNALS:
92 void resetCompositing();
93
94public Q_SLOTS:
95 virtual void windowOpacityChanged(KWin::Toplevel* c);
96 virtual void windowGeometryShapeChanged(KWin::Toplevel* c);
97 virtual void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted);
98protected:
99 bool init_ok;
100private:
101 bool viewportLimitsMatched(const QSize &size) const;
102private:
103 QHash< Toplevel*, Window* > windows;
104 bool m_debug;
105 OpenGLBackend *m_backend;
106};
107
108class SceneOpenGL2 : public SceneOpenGL
109{
110 Q_OBJECT
111public:
112 explicit SceneOpenGL2(OpenGLBackend *backend);
113 virtual ~SceneOpenGL2();
114 virtual CompositingType compositingType() const {
115 return OpenGL2Compositing;
116 }
117
118 static bool supported(OpenGLBackend *backend);
119
120 ColorCorrection *colorCorrection();
121
122protected:
123 virtual void paintGenericScreen(int mask, ScreenPaintData data);
124 virtual void doPaintBackground(const QVector< float >& vertices);
125 virtual SceneOpenGL::Window *createWindow(Toplevel *t);
126 virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
127 virtual void paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data);
128
129private Q_SLOTS:
130 void slotColorCorrectedChanged(bool recreateShaders = true);
131 void resetLanczosFilter();
132
133private:
134 void performPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
135
136private:
137 LanczosFilter *m_lanczosFilter;
138 QScopedPointer<ColorCorrection> m_colorCorrection;
139 GLuint vao;
140};
141
142#ifdef KWIN_HAVE_OPENGL_1
143class SceneOpenGL1 : public SceneOpenGL
144{
145public:
146 explicit SceneOpenGL1(OpenGLBackend *backend);
147 virtual ~SceneOpenGL1();
148 virtual void screenGeometryChanged(const QSize &size);
149 virtual qint64 paint(QRegion damage, ToplevelList windows);
150 virtual CompositingType compositingType() const {
151 return OpenGL1Compositing;
152 }
153
154 static bool supported(OpenGLBackend *backend);
155
156protected:
157 virtual void paintGenericScreen(int mask, ScreenPaintData data);
158 virtual void doPaintBackground(const QVector< float >& vertices);
159 virtual SceneOpenGL::Window *createWindow(Toplevel *t);
160
161private:
162 void setupModelViewProjectionMatrix();
163 bool m_resetModelViewProjectionMatrix;
164};
165#endif
166
167class SceneOpenGL::TexturePrivate
168 : public GLTexturePrivate
169{
170public:
171 virtual ~TexturePrivate();
172
173 virtual void findTarget() = 0;
174 virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth) = 0;
175 virtual OpenGLBackend *backend() = 0;
176 virtual bool update(const QRegion &damage);
177
178protected:
179 TexturePrivate();
180
181private:
182 Q_DISABLE_COPY(TexturePrivate)
183};
184
185class SceneOpenGL::Texture
186 : public GLTexture
187{
188public:
189 Texture(OpenGLBackend *backend);
190 Texture(OpenGLBackend *backend, const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
191 virtual ~Texture();
192
193 Texture & operator = (const Texture& tex);
194
195 using GLTexture::load;
196 virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D);
197 virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
198 virtual void discard();
199 bool update(const QRegion &damage);
200
201protected:
202 void findTarget();
203 virtual bool load(const Pixmap& pix, const QSize& size, int depth,
204 QRegion region);
205 virtual bool load(const Pixmap& pix, const QSize& size, int depth);
206
207 Texture(TexturePrivate& dd);
208
209private:
210 Q_DECLARE_PRIVATE(Texture)
211
212 friend class OpenGLWindowPixmap;
213};
214
215class SceneOpenGL::Window
216 : public Scene::Window
217{
218public:
219 virtual ~Window();
220 bool beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data);
221 virtual void performPaint(int mask, QRegion region, WindowPaintData data) = 0;
222 void endRenderWindow();
223 bool bindTexture();
224 void setScene(SceneOpenGL *scene) {
225 m_scene = scene;
226 }
227
228protected:
229 virtual WindowPixmap* createWindowPixmap();
230 Window(Toplevel* c);
231 enum TextureType {
232 Content,
233 DecorationLeftRight,
234 DecorationTopBottom,
235 Shadow
236 };
237
238 QMatrix4x4 transformation(int mask, const WindowPaintData &data) const;
239 bool getDecorationTextures(GLTexture **textures) const;
240 void paintDecoration(GLTexture *texture, TextureType type, const QRegion &region, const WindowPaintData &data, const WindowQuadList &quads);
241 void paintShadow(const QRegion &region, const WindowPaintData &data);
242 void renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture* tex, bool normalized);
243 /**
244 * @brief Prepare the OpenGL rendering state before the texture with @p type will be rendered.
245 *
246 * @param type The type of the Texture which will be rendered
247 * @param opacity The opacity value to use for this rendering
248 * @param brightness The brightness value to use for this rendering
249 * @param saturation The saturation value to use for this rendering
250 * @param screen The index of the screen to use for this rendering
251 **/
252 virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) = 0;
253 /**
254 * @brief Restores the OpenGL rendering state after the texture with @p type has been rendered.
255 *
256 * @param type The type of the Texture which has been rendered
257 * @param opacity The opacity value used for the rendering
258 * @param brightness The brightness value used for this rendering
259 * @param saturation The saturation value used for this rendering
260 * @param screen The index of the screen to use for this rendering
261 **/
262 virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) = 0;
263
264 /**
265 * @brief Returns the texture for the given @p type.
266 *
267 * @param type The Texture Type for which the texture should be retrieved
268 * @return :GLTexture* the texture
269 **/
270 GLTexture *textureForType(TextureType type);
271
272 void paintDecorations(const WindowPaintData &data, const QRegion &region);
273
274protected:
275 SceneOpenGL *m_scene;
276 bool m_hardwareClipping;
277
278private:
279 OpenGLPaintRedirector *paintRedirector() const;
280};
281
282class SceneOpenGL2Window : public SceneOpenGL::Window
283{
284public:
285 enum Leaf { ShadowLeaf = 0, LeftRightLeaf, TopBottomLeaf, ContentLeaf, PreviousContentLeaf, LeafCount };
286
287 struct LeafNode
288 {
289 LeafNode()
290 : texture(0),
291 firstVertex(0),
292 vertexCount(0),
293 opacity(1.0),
294 hasAlpha(false),
295 coordinateType(UnnormalizedCoordinates)
296 {
297 }
298
299 GLTexture *texture;
300 int firstVertex;
301 int vertexCount;
302 float opacity;
303 bool hasAlpha;
304 TextureCoordinateType coordinateType;
305 };
306
307 explicit SceneOpenGL2Window(Toplevel *c);
308 virtual ~SceneOpenGL2Window();
309
310protected:
311 QVector4D modulate(float opacity, float brightness) const;
312 void setBlendEnabled(bool enabled);
313 void setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data);
314 virtual void performPaint(int mask, QRegion region, WindowPaintData data);
315 virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen);
316 virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation);
317
318private:
319 /**
320 * Whether prepareStates enabled blending and restore states should disable again.
321 **/
322 bool m_blendingEnabled;
323};
324
325#ifdef KWIN_HAVE_OPENGL_1
326class SceneOpenGL1Window : public SceneOpenGL::Window
327{
328public:
329 explicit SceneOpenGL1Window(Toplevel *c);
330 virtual ~SceneOpenGL1Window();
331
332protected:
333 virtual void performPaint(int mask, QRegion region, WindowPaintData data);
334 virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen);
335 virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation);
336private:
337 void paintContent(SceneOpenGL::Texture* content, const QRegion& region, int mask, qreal opacity,
338 const WindowPaintData& data, const WindowQuadList &contentQuads, bool normalized);
339};
340#endif
341
342class OpenGLWindowPixmap : public WindowPixmap
343{
344public:
345 explicit OpenGLWindowPixmap(Scene::Window *window, SceneOpenGL *scene);
346 virtual ~OpenGLWindowPixmap();
347 SceneOpenGL::Texture *texture() const;
348 bool bind();
349private:
350 SceneOpenGL *m_scene;
351 QScopedPointer<SceneOpenGL::Texture> m_texture;
352};
353
354class SceneOpenGL::EffectFrame
355 : public Scene::EffectFrame
356{
357public:
358 EffectFrame(EffectFrameImpl* frame, SceneOpenGL *scene);
359 virtual ~EffectFrame();
360
361 virtual void free();
362 virtual void freeIconFrame();
363 virtual void freeTextFrame();
364 virtual void freeSelection();
365
366 virtual void render(QRegion region, double opacity, double frameOpacity);
367
368 virtual void crossFadeIcon();
369 virtual void crossFadeText();
370
371 static void cleanup();
372
373private:
374 void updateTexture();
375 void updateTextTexture();
376
377 Texture* m_texture;
378 Texture* m_textTexture;
379 Texture* m_oldTextTexture;
380 QPixmap* m_textPixmap; // need to keep the pixmap around to workaround some driver problems
381 Texture* m_iconTexture;
382 Texture* m_oldIconTexture;
383 Texture* m_selectionTexture;
384 GLVertexBuffer* m_unstyledVBO;
385 SceneOpenGL *m_scene;
386
387 static GLTexture* m_unstyledTexture;
388 static QPixmap* m_unstyledPixmap; // need to keep the pixmap around to workaround some driver problems
389 static void updateUnstyledTexture(); // Update OpenGL unstyled frame texture
390};
391
392/**
393 * @short OpenGL implementation of Shadow.
394 *
395 * This class extends Shadow by the Elements required for OpenGL rendering.
396 * @author Martin Gräßlin <mgraesslin@kde.org>
397 **/
398class SceneOpenGLShadow
399 : public Shadow
400{
401public:
402 explicit SceneOpenGLShadow(Toplevel *toplevel);
403 virtual ~SceneOpenGLShadow();
404
405 GLTexture *shadowTexture() {
406 return m_texture;
407 }
408protected:
409 virtual void buildQuads();
410 virtual bool prepareBackend();
411private:
412 GLTexture *m_texture;
413};
414
415/**
416 * @short Profiler to detect whether we have triple buffering
417 * The strategy is to start setBlocksForRetrace(false) but assume blocking and have the system prove that assumption wrong
418 **/
419class SwapProfiler
420{
421public:
422 SwapProfiler();
423 void init();
424 void begin();
425 /**
426 * @return char being 'd' for double, 't' for triple (or more - but non-blocking) buffering and
427 * 0 (NOT '0') otherwise, so you can act on "if (char result = SwapProfiler::end()) { fooBar(); }
428 **/
429 char end();
430private:
431 QElapsedTimer m_timer;
432 qint64 m_time;
433 int m_counter;
434};
435
436/**
437 * @brief The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
438 *
439 * The OpenGLBackend is an abstract base class used by the SceneOpenGL to abstract away the differences
440 * between various OpenGL windowing systems such as GLX and EGL.
441 *
442 * A concrete implementation has to create and release the OpenGL context in a way so that the
443 * SceneOpenGL does not have to care about it.
444 *
445 * In addition a major task for this class is to generate the SceneOpenGL::TexturePrivate which is
446 * able to perform the texture from pixmap operation in the given backend.
447 *
448 * @author Martin Gräßlin <mgraesslin@kde.org>
449 **/
450class OpenGLBackend
451{
452public:
453 OpenGLBackend();
454 virtual ~OpenGLBackend();
455 /**
456 * @return Time passes since start of rendering current frame.
457 * @see startRenderTimer
458 **/
459 qint64 renderTime() {
460 return m_renderTimer.nsecsElapsed();
461 }
462 virtual void screenGeometryChanged(const QSize &size) = 0;
463 virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture) = 0;
464
465 /**
466 * @brief Backend specific code to prepare the rendering of a frame including flushing the
467 * previously rendered frame to the screen if the backend works this way.
468 *
469 * @return A region that if not empty will be repainted in addition to the damaged region
470 **/
471 virtual QRegion prepareRenderingFrame() = 0;
472
473 /**
474 * @brief Backend specific code to handle the end of rendering a frame.
475 *
476 * @param renderedRegion The possibly larger region that has been rendered
477 * @param damagedRegion The damaged region that should be posted
478 **/
479 virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) = 0;
480
481 /**
482 * @brief Compositor is going into idle mode, flushes any pending paints.
483 **/
484 void idle();
485
486 /**
487 * @return bool Whether the scene needs to flush a frame.
488 **/
489 bool hasPendingFlush() const {
490 return !m_lastDamage.isEmpty();
491 }
492
493 /**
494 * @brief Returns the OverlayWindow used by the backend.
495 *
496 * A backend does not have to use an OverlayWindow, this is mostly for the X world.
497 * In case the backend does not use an OverlayWindow it is allowed to return @c null.
498 * It's the task of the caller to check whether it is @c null.
499 *
500 * @return :OverlayWindow*
501 **/
502 OverlayWindow *overlayWindow() {
503 return m_overlayWindow;
504 }
505 /**
506 * @brief Whether the creation of the Backend failed.
507 *
508 * The SceneOpenGL should test whether the Backend got constructed correctly. If this method
509 * returns @c true, the SceneOpenGL should not try to start the rendering.
510 *
511 * @return bool @c true if the creation of the Backend failed, @c false otherwise.
512 **/
513 bool isFailed() const {
514 return m_failed;
515 }
516 /**
517 * @brief Whether the Backend provides VSync.
518 *
519 * Currently only the GLX backend can provide VSync.
520 *
521 * @return bool @c true if VSync support is available, @c false otherwise
522 **/
523 bool syncsToVBlank() const {
524 return m_syncsToVBlank;
525 }
526 /**
527 * @brief Whether VSync blocks execution until the screen is in the retrace
528 *
529 * Case for waitVideoSync and non triple buffering buffer swaps
530 *
531 **/
532 bool blocksForRetrace() const {
533 return m_blocksForRetrace;
534 }
535 /**
536 * @brief Whether the backend uses direct rendering.
537 *
538 * Some OpenGLScene modes require direct rendering. E.g. the OpenGL 2 should not be used
539 * if direct rendering is not supported by the Scene.
540 *
541 * @return bool @c true if the GL context is direct, @c false if indirect
542 **/
543 bool isDirectRendering() const {
544 return m_directRendering;
545 }
546
547 bool supportsBufferAge() const {
548 return m_haveBufferAge;
549 }
550
551 /**
552 * Returns the damage that has accumulated since a buffer of the given age was presented.
553 */
554 QRegion accumulatedDamageHistory(int bufferAge) const;
555
556 /**
557 * Saves the given region to damage history.
558 */
559 void addToDamageHistory(const QRegion &region);
560
561protected:
562 /**
563 * @brief Backend specific flushing of frame to screen.
564 **/
565 virtual void present() = 0;
566 /**
567 * @brief Sets the backend initialization to failed.
568 *
569 * This method should be called by the concrete subclass in case the initialization failed.
570 * The given @p reason is logged as a warning.
571 *
572 * @param reason The reason why the initialization failed.
573 **/
574 void setFailed(const QString &reason);
575 /**
576 * @brief Sets whether the backend provides VSync.
577 *
578 * Should be called by the concrete subclass once it is determined whether VSync is supported.
579 * If the subclass does not call this method, the backend defaults to @c false.
580 * @param enabled @c true if VSync support available, @c false otherwise.
581 **/
582 void setSyncsToVBlank(bool enabled) {
583 m_syncsToVBlank = enabled;
584 }
585 /**
586 * @brief Sets whether the VSync iplementation blocks
587 *
588 * Should be called by the concrete subclass once it is determined how VSync works.
589 * If the subclass does not call this method, the backend defaults to @c false.
590 * @param enabled @c true if VSync blocks, @c false otherwise.
591 **/
592 void setBlocksForRetrace(bool enabled) {
593 m_blocksForRetrace = enabled;
594 }
595 /**
596 * @brief Sets whether the OpenGL context is direct.
597 *
598 * Should be called by the concrete subclass once it is determined whether the OpenGL context is
599 * direct or indirect.
600 * If the subclass does not call this method, the backend defaults to @c false.
601 *
602 * @param direct @c true if the OpenGL context is direct, @c false if indirect
603 **/
604 void setIsDirectRendering(bool direct) {
605 m_directRendering = direct;
606 }
607
608 void setSupportsBufferAge(bool value) {
609 m_haveBufferAge = value;
610 }
611
612 /**
613 * @return const QRegion& Damage of previously rendered frame
614 **/
615 const QRegion &lastDamage() const {
616 return m_lastDamage;
617 }
618 void setLastDamage(const QRegion &damage) {
619 m_lastDamage = damage;
620 }
621 /**
622 * @brief Starts the timer for how long it takes to render the frame.
623 *
624 * @see renderTime
625 **/
626 void startRenderTimer() {
627 m_renderTimer.start();
628 }
629
630 SwapProfiler m_swapProfiler;
631
632private:
633 /**
634 * @brief The OverlayWindow used by this Backend.
635 **/
636 OverlayWindow *m_overlayWindow;
637 /**
638 * @brief Whether VSync is available and used, defaults to @c false.
639 **/
640 bool m_syncsToVBlank;
641 /**
642 * @brief Whether present() will block execution until the next vertical retrace @c false.
643 **/
644 bool m_blocksForRetrace;
645 /**
646 * @brief Whether direct rendering is used, defaults to @c false.
647 **/
648 bool m_directRendering;
649 /**
650 * @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age.
651 */
652 bool m_haveBufferAge;
653 /**
654 * @brief Whether the initialization failed, of course default to @c false.
655 **/
656 bool m_failed;
657 /**
658 * @brief Damaged region of previously rendered frame.
659 **/
660 QRegion m_lastDamage;
661 /**
662 * @brief The damage history for the past 10 frames.
663 */
664 QList<QRegion> m_damageHistory;
665 /**
666 * @brief Timer to measure how long a frame renders.
667 **/
668 QElapsedTimer m_renderTimer;
669};
670
671inline bool SceneOpenGL::hasPendingFlush() const
672{
673 return m_backend->hasPendingFlush();
674}
675
676inline SceneOpenGL::Texture* OpenGLWindowPixmap::texture() const
677{
678 return m_texture.data();
679}
680
681} // namespace
682
683#endif
684