KWin
Loading...
Searching...
No Matches
screentransform.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9// own
10#include "screentransform.h"
12#include "core/renderlayer.h"
13#include "core/rendertarget.h"
14#include "core/renderviewport.h"
16#include "opengl/glutils.h"
18
19#include <QDebug>
20
21static void ensureResources()
22{
23 // Must initialize resources manually because the effect is a static lib.
24 Q_INIT_RESOURCE(screentransform);
25}
26
27namespace KWin
28{
29
31 : Effect()
32{
33 // Make sure that shaders in /effects/screentransform/shaders/* are loaded.
34 ensureResources();
35
38 QStringLiteral(":/effects/screentransform/shaders/crossfade.vert"),
39 QStringLiteral(":/effects/screentransform/shaders/crossfade.frag"));
40
41 m_modelViewProjectioMatrixLocation = m_shader->uniformLocation("modelViewProjectionMatrix");
42 m_blendFactorLocation = m_shader->uniformLocation("blendFactor");
43 m_previousTextureLocation = m_shader->uniformLocation("previousTexture");
44 m_currentTextureLocation = m_shader->uniformLocation("currentTexture");
45
46 const QList<Output *> screens = effects->screens();
47 for (auto screen : screens) {
48 addScreen(screen);
49 }
50 connect(effects, &EffectsHandler::screenAdded, this, &ScreenTransformEffect::addScreen);
51 connect(effects, &EffectsHandler::screenRemoved, this, &ScreenTransformEffect::removeScreen);
52}
53
55
60
62{
63 auto ensureShort = [](int angle) {
64 return angle > 180 ? angle - 360 : angle < -180 ? angle + 360
65 : angle;
66 };
67 // % 4 to ignore flipped cases (for now)
68 return ensureShort((int(current.kind()) % 4 - int(old.kind()) % 4) * 90);
69}
70
71void ScreenTransformEffect::addScreen(Output *screen)
72{
73 connect(screen, &Output::aboutToChange, this, [this, screen](OutputChangeSet *changeSet) {
74 const OutputTransform transform = changeSet->transform.value_or(screen->transform());
75 if (screen->transform() == transform) {
76 return;
77 }
78
79 Scene *scene = effects->scene();
80 RenderLayer layer(screen->renderLoop());
81 SceneDelegate delegate(scene, screen);
82 delegate.setLayer(&layer);
83
84 // Avoid including this effect while capturing previous screen state.
85 m_capturing = true;
86 auto resetCapturing = qScopeGuard([this]() {
87 m_capturing = false;
88 });
89
90 scene->prePaint(&delegate);
91
93 if (auto texture = GLTexture::allocate(GL_RGBA8, screen->pixelSize())) {
94 auto &state = m_states[screen];
95 state.m_oldTransform = screen->transform();
96 state.m_oldGeometry = screen->geometry();
97 state.m_timeLine.setDuration(std::chrono::milliseconds(long(animationTime(250))));
98 state.m_timeLine.setEasingCurve(QEasingCurve::InOutCubic);
99 state.m_angle = transformAngle(changeSet->transform.value(), state.m_oldTransform);
100 state.m_prev.texture = std::move(texture);
101 state.m_prev.framebuffer = std::make_unique<GLFramebuffer>(state.m_prev.texture.get());
102
103 RenderTarget renderTarget(state.m_prev.framebuffer.get());
104 scene->paint(renderTarget, screen->geometry());
105 } else {
106 m_states.remove(screen);
107 }
108
109 scene->postPaint();
110 });
111}
112
113void ScreenTransformEffect::removeScreen(Output *screen)
114{
115 screen->disconnect(this);
116 if (auto it = m_states.find(screen); it != m_states.end()) {
118 m_states.erase(it);
119 }
120}
121
122void ScreenTransformEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
123{
124 auto it = m_states.find(data.screen);
125 if (it != m_states.end()) {
126 it->m_timeLine.advance(presentTime);
127 if (it->m_timeLine.done()) {
128 m_states.remove(data.screen);
129 }
130 }
131
132 effects->prePaintScreen(data, presentTime);
133}
134
135static GLVertexBuffer *texturedRectVbo(const QRectF &geometry, qreal scale)
136{
138 vbo->reset();
140
141 const auto opt = vbo->map<GLVertex2D>(6);
142 if (!opt) {
143 return nullptr;
144 }
145 const auto map = *opt;
146
147 auto deviceGeometry = scaledRect(geometry, scale);
148
149 // first triangle
150 map[0] = GLVertex2D{
151 .position = QVector2D(deviceGeometry.left(), deviceGeometry.top()),
152 .texcoord = QVector2D(0.0, 1.0),
153 };
154 map[1] = GLVertex2D{
155 .position = QVector2D(deviceGeometry.right(), deviceGeometry.bottom()),
156 .texcoord = QVector2D(1.0, 0.0),
157 };
158 map[2] = GLVertex2D{
159 .position = QVector2D(deviceGeometry.left(), deviceGeometry.bottom()),
160 .texcoord = QVector2D(0.0, 0.0),
161 };
162
163 // second triangle
164 map[3] = GLVertex2D{
165 .position = QVector2D(deviceGeometry.left(), deviceGeometry.top()),
166 .texcoord = QVector2D(0.0, 1.0),
167 };
168 map[4] = GLVertex2D{
169 .position = QVector2D(deviceGeometry.right(), deviceGeometry.top()),
170 .texcoord = QVector2D(1.0, 1.0),
171 };
172 map[5] = GLVertex2D{
173 .position = QVector2D(deviceGeometry.right(), deviceGeometry.bottom()),
174 .texcoord = QVector2D(1.0, 0.0),
175 };
176
177 vbo->unmap();
178 return vbo;
179}
180
181static qreal lerp(qreal a, qreal b, qreal t)
182{
183 return (1 - t) * a + t * b;
184}
185
186static QRectF lerp(const QRectF &a, const QRectF &b, qreal t)
187{
188 QRectF ret;
189 ret.setWidth(lerp(a.width(), b.width(), t));
190 ret.setHeight(lerp(a.height(), b.height(), t));
191 ret.moveCenter(b.center());
192 return ret;
193}
194
195void ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, KWin::Output *screen)
196{
197 auto it = m_states.find(screen);
198 if (it == m_states.end()) {
199 effects->paintScreen(renderTarget, viewport, mask, region, screen);
200 return;
201 }
202
203 // Render the screen in an offscreen texture.
204 const QSize nativeSize = screen->geometry().size() * screen->scale();
205 if (!it->m_current.texture || it->m_current.texture->size() != nativeSize) {
206 it->m_current.texture = GLTexture::allocate(GL_RGBA8, nativeSize);
207 if (!it->m_current.texture) {
208 m_states.remove(screen);
209 return;
210 }
211 it->m_current.framebuffer = std::make_unique<GLFramebuffer>(it->m_current.texture.get());
212 }
213
214 RenderTarget fboRenderTarget(it->m_current.framebuffer.get());
215 RenderViewport fboViewport(viewport.renderRect(), viewport.scale(), fboRenderTarget);
216
217 GLFramebuffer::pushFramebuffer(it->m_current.framebuffer.get());
218 effects->paintScreen(fboRenderTarget, fboViewport, mask, region, screen);
220
221 const qreal blendFactor = it->m_timeLine.value();
222 const QRectF screenRect = screen->geometry();
223 const qreal angle = it->m_angle * (1 - blendFactor);
224
225 const auto scale = viewport.scale();
226
227 // Projection matrix + rotate transform.
228 const QVector3D transformOrigin(screenRect.center());
229 QMatrix4x4 modelViewProjectionMatrix(viewport.projectionMatrix());
230 modelViewProjectionMatrix.translate(transformOrigin * scale);
231 modelViewProjectionMatrix.rotate(angle, 0, 0, 1);
232 modelViewProjectionMatrix.translate(-transformOrigin * scale);
233
234 glActiveTexture(GL_TEXTURE1);
235 it->m_prev.texture->bind();
236 glActiveTexture(GL_TEXTURE0);
237 it->m_current.texture->bind();
238
239 // Clear the background.
240 glClearColor(0, 0, 0, 0);
241 glClear(GL_COLOR_BUFFER_BIT);
242
243 GLVertexBuffer *vbo = texturedRectVbo(lerp(it->m_oldGeometry, screenRect, blendFactor), scale);
244 if (!vbo) {
245 return;
246 }
247
249 sm->pushShader(m_shader.get());
250 m_shader->setUniform(m_modelViewProjectioMatrixLocation, modelViewProjectionMatrix);
251 m_shader->setUniform(m_blendFactorLocation, float(blendFactor));
252 m_shader->setUniform(m_currentTextureLocation, 0);
253 m_shader->setUniform(m_previousTextureLocation, 1);
254
255 vbo->bindArrays();
256 vbo->draw(GL_TRIANGLES, 0, 6);
257 vbo->unbindArrays();
258 sm->popShader();
259
260 glActiveTexture(GL_TEXTURE1);
261 it->m_prev.texture->unbind();
262 glActiveTexture(GL_TEXTURE0);
263 it->m_current.texture->unbind();
264
266}
267
269{
270 return !m_states.isEmpty() && !m_capturing;
271}
272
273} // namespace KWin
274
275#include "moc_screentransform.cpp"
Base class for all KWin effects.
Definition effect.h:535
void screenAdded(KWin::Output *screen)
Display * waylandDisplay() const
bool animationsSupported() const
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
void screenRemoved(KWin::Output *screen)
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
CompositingType compositingType
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
QList< Output * > screens() const
WorkspaceScene * scene() const
Q_SCRIPTABLE void addRepaintFull()
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
Vertex Buffer Object.
void draw(GLenum primitiveMode, int first, int count)
void setAttribLayout(std::span< const GLVertexAttrib > attribs, size_t stride)
static constexpr std::array GLVertex2DLayout
static GLVertexBuffer * streamingBuffer()
std::optional< std::span< T > > map(size_t count)
qreal scale() const
Definition output.cpp:455
void aboutToChange(OutputChangeSet *changeSet)
QRect geometry
Definition output.h:134
Kind kind() const
Definition output.cpp:64
QMatrix4x4 projectionMatrix() const
QRectF renderRect() const
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, KWin::Output *screen) override
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
bool isActive() const override
Manager for Shaders.
std::unique_ptr< GLShader > generateShaderFromFile(ShaderTraits traits, const QString &vertexFile=QString(), const QString &fragmentFile=QString())
static ShaderManager * instance()
GLShader * pushShader(ShaderTraits traits)
static double animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime)
Definition effect.cpp:483
@ OpenGLCompositing
Definition globals.h:37
qreal transformAngle(OutputTransform current, OutputTransform old)
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
Definition globals.h:243
EffectsHandler * effects