KWin
Loading...
Searching...
No Matches
offscreeneffect.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
8#include "core/output.h"
9#include "core/rendertarget.h"
10#include "core/renderviewport.h"
12#include "opengl/gltexture.h"
13#include "opengl/glutils.h"
14
15namespace KWin
16{
17
19{
20public:
21 virtual ~OffscreenData();
22 void setDirty();
23 void setShader(GLShader *newShader);
25
26 void paint(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, const QRegion &region,
27 const WindowPaintData &data, const WindowQuadList &quads);
28
29 void maybeRender(EffectWindow *window);
30
31 std::unique_ptr<GLTexture> m_texture;
32 std::unique_ptr<GLFramebuffer> m_fbo;
33 bool m_isDirty = true;
34 GLShader *m_shader = nullptr;
36 QMetaObject::Connection m_windowDamagedConnection;
37};
38
40{
41public:
42 std::map<EffectWindow *, std::unique_ptr<OffscreenData>> windows;
43 QMetaObject::Connection windowDeletedConnection;
45};
46
48 : Effect(parent)
49 , d(std::make_unique<OffscreenEffectPrivate>())
50{
51}
52
54
59
61{
62 std::unique_ptr<OffscreenData> &offscreenData = d->windows[window];
63 if (offscreenData) {
64 return;
65 }
66 offscreenData = std::make_unique<OffscreenData>();
67 offscreenData->setVertexSnappingMode(d->vertexSnappingMode);
68
69 offscreenData->m_windowDamagedConnection =
70 connect(window, &EffectWindow::windowDamaged, this, &OffscreenEffect::handleWindowDamaged);
71
72 if (d->windows.size() == 1) {
73 setupConnections();
74 }
75}
76
78{
79 d->windows.erase(window);
80 if (d->windows.empty()) {
81 destroyConnections();
82 }
83}
84
86{
87 if (const auto it = d->windows.find(window); it != d->windows.end()) {
88 it->second->setShader(shader);
89 }
90}
91
93{
94}
95
97{
98 const QRectF logicalGeometry = window->expandedGeometry();
99 const qreal scale = window->screen()->scale();
100 const QSize textureSize = (logicalGeometry.size() * scale).toSize();
101
102 if (!m_texture || m_texture->size() != textureSize) {
103 m_texture = GLTexture::allocate(GL_RGBA8, textureSize);
104 if (!m_texture) {
105 return;
106 }
107 m_texture->setFilter(GL_LINEAR);
108 m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
109 m_fbo = std::make_unique<GLFramebuffer>(m_texture.get());
110 m_isDirty = true;
111 }
112
113 if (m_isDirty) {
114 RenderTarget renderTarget(m_fbo.get());
115 RenderViewport viewport(logicalGeometry, scale, renderTarget);
117 glClearColor(0.0, 0.0, 0.0, 0.0);
118 glClear(GL_COLOR_BUFFER_BIT);
119
120 QMatrix4x4 projectionMatrix;
121 projectionMatrix.ortho(QRectF(0, 0, textureSize.width(), textureSize.height()));
122
123 WindowPaintData data;
124 data.setXTranslation(-logicalGeometry.x());
125 data.setYTranslation(-logicalGeometry.y());
126 data.setOpacity(1.0);
127 data.setProjectionMatrix(projectionMatrix);
128
130 effects->drawWindow(renderTarget, viewport, window, mask, infiniteRegion(), data);
131
133 m_isDirty = false;
134 }
135}
136
138{
139 QObject::disconnect(m_windowDamagedConnection);
140}
141
143{
144 m_isDirty = true;
145}
146
148{
149 m_shader = newShader;
150}
151
156
157void OffscreenData::paint(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, const QRegion &region,
158 const WindowPaintData &data, const WindowQuadList &quads)
159{
161 ShaderBinder binder(shader);
162
163 const double scale = viewport.scale();
164
166 vbo->reset();
168
169 RenderGeometry geometry;
171 for (auto &quad : quads) {
172 geometry.appendWindowQuad(quad, scale);
173 }
175
176 const auto map = vbo->map<GLVertex2D>(geometry.size());
177 if (!map) {
178 return;
179 }
180 geometry.copy(*map);
181 vbo->unmap();
182
183 vbo->bindArrays();
184
185 const qreal rgb = data.brightness() * data.opacity();
186 const qreal a = data.opacity();
187
188 QMatrix4x4 mvp = data.projectionMatrix();
189 mvp.translate(std::round(window->x() * scale), std::round(window->y() * scale));
190
191 const auto toXYZ = renderTarget.colorDescription().colorimetry().toXYZ();
193 shader->setUniform(GLShader::Vec4Uniform::ModulationConstant, QVector4D(rgb, rgb, rgb, a));
195 shader->setUniform(GLShader::Vec3Uniform::PrimaryBrightness, QVector3D(toXYZ(1, 0), toXYZ(1, 1), toXYZ(1, 2)));
198 shader->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
199
200 const bool clipping = region != infiniteRegion();
201 const QRegion clipRegion = clipping ? viewport.mapToRenderTarget(region) : infiniteRegion();
202
203 if (clipping) {
204 glEnable(GL_SCISSOR_TEST);
205 }
206
207 glEnable(GL_BLEND);
208 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
209
210 m_texture->bind();
211 vbo->draw(clipRegion, GL_TRIANGLES, 0, geometry.count(), clipping);
212 m_texture->unbind();
213
214 glDisable(GL_BLEND);
215 if (clipping) {
216 glDisable(GL_SCISSOR_TEST);
217 }
218 vbo->unbindArrays();
219}
220
221void OffscreenEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const QRegion &region, WindowPaintData &data)
222{
223 const auto it = d->windows.find(window);
224 if (it == d->windows.end()) {
225 effects->drawWindow(renderTarget, viewport, window, mask, region, data);
226 return;
227 }
228 OffscreenData *offscreenData = it->second.get();
229
230 const QRectF expandedGeometry = window->expandedGeometry();
231 const QRectF frameGeometry = window->frameGeometry();
232
233 QRectF visibleRect = expandedGeometry;
234 visibleRect.moveTopLeft(expandedGeometry.topLeft() - frameGeometry.topLeft());
235 WindowQuad quad;
236 quad[0] = WindowVertex(visibleRect.topLeft(), QPointF(0, 0));
237 quad[1] = WindowVertex(visibleRect.topRight(), QPointF(1, 0));
238 quad[2] = WindowVertex(visibleRect.bottomRight(), QPointF(1, 1));
239 quad[3] = WindowVertex(visibleRect.bottomLeft(), QPointF(0, 1));
240
241 WindowQuadList quads;
242 quads.append(quad);
243 apply(window, mask, data, quads);
244
245 offscreenData->maybeRender(window);
246 offscreenData->paint(renderTarget, viewport, window, region, data, quads);
247}
248
249void OffscreenEffect::handleWindowDamaged(EffectWindow *window)
250{
251 if (const auto it = d->windows.find(window); it != d->windows.end()) {
252 it->second->setDirty();
253 }
254}
255
256void OffscreenEffect::handleWindowDeleted(EffectWindow *window)
257{
259 unredirect(window);
260}
261
262void OffscreenEffect::setupConnections()
263{
264 d->windowDeletedConnection =
265 connect(effects, &EffectsHandler::windowDeleted, this, &OffscreenEffect::handleWindowDeleted);
266}
267
268void OffscreenEffect::destroyConnections()
269{
270 disconnect(d->windowDeletedConnection);
271
272 d->windowDeletedConnection = {};
273}
274
276{
277 d->vertexSnappingMode = mode;
278 for (auto &window : std::as_const(d->windows)) {
279 window.second->setVertexSnappingMode(mode);
280 }
281}
282
284{
285public:
287};
288
290{
291public:
292 std::map<EffectWindow *, std::unique_ptr<CrossFadeWindowData>> windows;
293 qreal progress;
294};
295
297 : Effect(parent)
298 , d(std::make_unique<CrossFadeEffectPrivate>())
299{
300}
301
303
304void CrossFadeEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const QRegion &region, WindowPaintData &data)
305{
306 const auto it = d->windows.find(window);
307
308 // paint the new window (if applicable) underneath
309 if (data.crossFadeProgress() > 0 || it == d->windows.end()) {
310 Effect::drawWindow(renderTarget, viewport, window, mask, region, data);
311 }
312
313 if (it == d->windows.end()) {
314 return;
315 }
316 CrossFadeWindowData *offscreenData = it->second.get();
317
318 // paint old snapshot on top
319 WindowPaintData previousWindowData = data;
320 previousWindowData.setOpacity((1.0 - data.crossFadeProgress()) * data.opacity());
321
322 const QRectF expandedGeometry = window->expandedGeometry();
323 const QRectF frameGeometry = window->frameGeometry();
324
325 // This is for the case of *non* live effect, when the window buffer we saved has a different size
326 // compared to the size the window has now. The "old" window will be rendered scaled to the current
327 // window geometry, but everything will be scaled, also the shadow if there is any, making the window
328 // frame not line up anymore with window->frameGeometry()
329 // to fix that, we consider how much the shadow will have scaled, and use that as margins to the
330 // current frame geometry. this causes the scaled window to visually line up perfectly with frameGeometry,
331 // having the scaled shadow all outside of it.
332 const qreal widthRatio = offscreenData->frameGeometryAtCapture.width() / frameGeometry.width();
333 const qreal heightRatio = offscreenData->frameGeometryAtCapture.height() / frameGeometry.height();
334
335 const QMarginsF margins(
336 (expandedGeometry.x() - frameGeometry.x()) / widthRatio,
337 (expandedGeometry.y() - frameGeometry.y()) / heightRatio,
338 (frameGeometry.right() - expandedGeometry.right()) / widthRatio,
339 (frameGeometry.bottom() - expandedGeometry.bottom()) / heightRatio);
340
341 QRectF visibleRect = QRectF(QPointF(0, 0), frameGeometry.size()) - margins;
342
343 WindowQuad quad;
344 quad[0] = WindowVertex(visibleRect.topLeft(), QPointF(0, 0));
345 quad[1] = WindowVertex(visibleRect.topRight(), QPointF(1, 0));
346 quad[2] = WindowVertex(visibleRect.bottomRight(), QPointF(1, 1));
347 quad[3] = WindowVertex(visibleRect.bottomLeft(), QPointF(0, 1));
348
349 WindowQuadList quads;
350 quads.append(quad);
351 offscreenData->paint(renderTarget, viewport, window, region, previousWindowData, quads);
352}
353
355{
356 if (d->windows.empty()) {
357 connect(effects, &EffectsHandler::windowDeleted, this, &CrossFadeEffect::handleWindowDeleted);
358 }
359
360 std::unique_ptr<CrossFadeWindowData> &offscreenData = d->windows[window];
361 if (offscreenData) {
362 return;
363 }
364 offscreenData = std::make_unique<CrossFadeWindowData>();
365
366 // Avoid including blur and contrast effects. During a normal painting cycle they
367 // won't be included, but since we call effects->drawWindow() outside usual compositing
368 // cycle, we have to prevent backdrop effects kicking in.
369 const QVariant blurRole = window->data(WindowForceBlurRole);
370 window->setData(WindowForceBlurRole, QVariant());
371 const QVariant contrastRole = window->data(WindowForceBackgroundContrastRole);
372 window->setData(WindowForceBackgroundContrastRole, QVariant());
373
375 offscreenData->maybeRender(window);
376 offscreenData->frameGeometryAtCapture = window->frameGeometry();
377
378 window->setData(WindowForceBlurRole, blurRole);
379 window->setData(WindowForceBackgroundContrastRole, contrastRole);
380}
381
383{
384 d->windows.erase(window);
385 if (d->windows.empty()) {
386 disconnect(effects, &EffectsHandler::windowDeleted, this, &CrossFadeEffect::handleWindowDeleted);
387 }
388}
389
390void CrossFadeEffect::handleWindowDeleted(EffectWindow *window)
391{
392 unredirect(window);
393}
394
396{
397 if (const auto it = d->windows.find(window); it != d->windows.end()) {
398 it->second->setShader(shader);
399 }
400}
401
402} // namespace KWin
403
404#include "moc_offscreeneffect.cpp"
const Colorimetry & colorimetry() const
const QMatrix4x4 & toXYZ() const
void unredirect(EffectWindow *window)
~CrossFadeEffect() override
void setShader(EffectWindow *window, GLShader *shader)
void redirect(EffectWindow *window)
CrossFadeEffect(QObject *parent=nullptr)
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const QRegion &region, WindowPaintData &data) override
std::map< EffectWindow *, std::unique_ptr< CrossFadeWindowData > > windows
Base class for all KWin effects.
Definition effect.h:535
Representation of a window used by/for Effect classes.
void windowDamaged(KWin::EffectWindow *w)
Q_SCRIPTABLE void setData(int role, const QVariant &data)
Q_SCRIPTABLE QVariant data(int role) const
KWin::Output * screen
QRectF frameGeometry() const
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
void windowDeleted(KWin::EffectWindow *w)
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
bool setColorspaceUniformsFromSRGB(const ColorDescription &dst)
Definition glshader.cpp:457
bool setUniform(const char *name, float value)
Definition glshader.cpp:301
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)
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const QRegion &region, WindowPaintData &data) override
void unredirect(EffectWindow *window)
void redirect(EffectWindow *window)
virtual void apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads)
OffscreenEffect(QObject *parent=nullptr)
~OffscreenEffect() override
void setVertexSnappingMode(RenderGeometry::VertexSnappingMode mode)
void setShader(EffectWindow *window, GLShader *shader)
std::map< EffectWindow *, std::unique_ptr< OffscreenData > > windows
RenderGeometry::VertexSnappingMode vertexSnappingMode
QMetaObject::Connection windowDeletedConnection
qreal scale() const
Definition output.cpp:455
void postProcessTextureCoordinates(const QMatrix4x4 &textureMatrix)
void appendWindowQuad(const WindowQuad &quad, qreal deviceScale)
void copy(std::span< GLVertex2D > destination)
void setVertexSnappingMode(VertexSnappingMode mode)
const ColorDescription & colorDescription() const
QRectF mapToRenderTarget(const QRectF &logicalGeometry) const
GLShader * shader(ShaderTraits traits)
static ShaderManager * instance()
Class representing one area of a window.
Vertex class.
void setYTranslation(qreal translate)
Definition effect.cpp:106
void setProjectionMatrix(const QMatrix4x4 &matrix)
Definition effect.cpp:327
qreal crossFadeProgress() const
Definition effect.cpp:299
virtual void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
Definition effect.cpp:457
qreal saturation() const
Definition effect.cpp:264
void setXTranslation(qreal translate)
Definition effect.cpp:101
QMatrix4x4 toMatrix(qreal deviceScale) const
Definition effect.cpp:191
void setOpacity(qreal opacity)
Definition effect.cpp:279
QMatrix4x4 projectionMatrix() const
Definition effect.cpp:332
qreal brightness() const
Definition effect.cpp:269
qreal opacity() const
Definition effect.cpp:259
@ PAINT_WINDOW_TRANSFORMED
Definition effect.h:552
@ PAINT_WINDOW_TRANSLUCENT
Definition effect.h:548
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
@ WindowForceBackgroundContrastRole
For fullscreen effects to enforce the background contrast,.
@ WindowForceBlurRole
For fullscreen effects to enforce blurring of windows,.
@ NormalizedCoordinates
Definition gltexture.h:35
EffectsHandler * effects
void setVertexSnappingMode(RenderGeometry::VertexSnappingMode mode)
void setShader(GLShader *newShader)
QMetaObject::Connection m_windowDamagedConnection
std::unique_ptr< GLFramebuffer > m_fbo
void maybeRender(EffectWindow *window)
void paint(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, const QRegion &region, const WindowPaintData &data, const WindowQuadList &quads)
RenderGeometry::VertexSnappingMode m_vertexSnappingMode
std::unique_ptr< GLTexture > m_texture