effects/zoom: Render scene in an offscreen texture
This fixes integration of the zoom effect with the blur effect and qtquick effects such as overview or window view. The main con of the proposed solution is that offscreen rendering can be a bit overkill. But on the other hand, it's the most robust way to ensure that fullscreen effects integrate correctly with the zoom effect. For example, without it, the quick scene effect would need to figure out what screen views to paint for the given screen. There are also more finicky cases, e.g. the screen transform effect. BUG: 447002 BUG: 447670 BUG: 453467 Tested-by: Merge Service <https://invent.kde.org/plasma/kwin/-/merge_requests/2451> Part-of: <https://invent.kde.org/plasma/kwin/-/merge_requests/2451>
This commit is contained in:
parent
cc487d4292
commit
221f114d80
2 changed files with 113 additions and 24 deletions
|
@ -117,6 +117,7 @@ ZoomEffect::ZoomEffect()
|
|||
connect(&timeline, &QTimeLine::frameChanged, this, &ZoomEffect::timelineFrameChanged);
|
||||
connect(effects, &EffectsHandler::mouseChanged, this, &ZoomEffect::slotMouseChanged);
|
||||
connect(effects, &EffectsHandler::windowDamaged, this, &ZoomEffect::slotWindowDamaged);
|
||||
connect(effects, &EffectsHandler::screenRemoved, this, &ZoomEffect::slotScreenRemoved);
|
||||
|
||||
#if HAVE_ACCESSIBILITY
|
||||
m_accessibilityIntegration = new ZoomAccessibilityIntegration(this);
|
||||
|
@ -131,6 +132,7 @@ ZoomEffect::~ZoomEffect()
|
|||
{
|
||||
// switch off and free resources
|
||||
showCursor();
|
||||
qDeleteAll(m_offscreenData);
|
||||
// Save the zoom value.
|
||||
ZoomConfig::setInitialZoom(target_zoom);
|
||||
ZoomConfig::self()->save();
|
||||
|
@ -261,8 +263,59 @@ void ZoomEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseco
|
|||
effects->prePaintScreen(data, presentTime);
|
||||
}
|
||||
|
||||
ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(EffectScreen *screen)
|
||||
{
|
||||
const QRect rect = effects->renderTargetRect();
|
||||
const qreal devicePixelRatio = effects->renderTargetScale();
|
||||
const QSize nativeSize = rect.size() * devicePixelRatio;
|
||||
|
||||
OffscreenData *&data = m_offscreenData[effects->waylandDisplay() ? screen : nullptr];
|
||||
if (!data) {
|
||||
data = new OffscreenData;
|
||||
}
|
||||
if (!data->texture || data->texture->size() != nativeSize) {
|
||||
data->texture.reset(new GLTexture(GL_RGBA8, nativeSize));
|
||||
data->texture->setFilter(GL_NEAREST);
|
||||
data->texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
data->framebuffer.reset(new GLFramebuffer(data->texture.data()));
|
||||
}
|
||||
if (!data->vbo || data->viewport != rect) {
|
||||
data->vbo.reset(new GLVertexBuffer(GLVertexBuffer::Static));
|
||||
data->viewport = rect;
|
||||
|
||||
QVector<float> verts;
|
||||
QVector<float> texcoords;
|
||||
|
||||
// The v-coordinate is flipped because projection matrix is "flipped."
|
||||
texcoords << 1.0 << 1.0;
|
||||
verts << rect.x() + rect.width() << rect.y();
|
||||
texcoords << 0.0 << 1.0;
|
||||
verts << rect.x() << rect.y();
|
||||
texcoords << 0.0 << 0.0;
|
||||
verts << rect.x() << rect.y() + rect.height();
|
||||
|
||||
texcoords << 1.0 << 0.0;
|
||||
verts << rect.x() + rect.width() << rect.y() + rect.height();
|
||||
texcoords << 1.0 << 1.0;
|
||||
verts << rect.x() + rect.width() << rect.y();
|
||||
texcoords << 0.0 << 0.0;
|
||||
verts << rect.x() << rect.y() + rect.height();
|
||||
|
||||
data->vbo->setData(6, 2, verts.constData(), texcoords.constData());
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void ZoomEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data)
|
||||
{
|
||||
OffscreenData *offscreenData = ensureOffscreenData(data.screen());
|
||||
|
||||
// Render the scene in an offscreen texture and then upscale it.
|
||||
GLFramebuffer::pushFramebuffer(offscreenData->framebuffer.data());
|
||||
effects->paintScreen(mask, region, data);
|
||||
GLFramebuffer::popFramebuffer();
|
||||
|
||||
if (zoom != 1.0) {
|
||||
data *= QVector2D(zoom, zoom);
|
||||
const QSize screenSize = effects->virtualScreenSize();
|
||||
|
@ -324,37 +377,52 @@ void ZoomEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &d
|
|||
prevPoint = focusPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
effects->paintScreen(mask, region, data);
|
||||
// Render transformed offscreen texture.
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (zoom != 1.0 && mousePointer != MousePointerHide) {
|
||||
GLTexture *cursorTexture = ensureCursorTexture();
|
||||
if (cursorTexture) {
|
||||
const auto cursor = effects->cursorImage();
|
||||
QMatrix4x4 matrix;
|
||||
matrix.translate(data.translation());
|
||||
matrix.scale(data.scale());
|
||||
|
||||
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix() * matrix);
|
||||
for (OffscreenData *data : std::as_const(m_offscreenData)) {
|
||||
data->texture->bind();
|
||||
data->vbo->render(GL_TRIANGLES);
|
||||
data->texture->unbind();
|
||||
}
|
||||
ShaderManager::instance()->popShader();
|
||||
|
||||
if (mousePointer != MousePointerHide) {
|
||||
// Draw the mouse-texture at the position matching to zoomed-in image of the desktop. Hiding the
|
||||
// previous mouse-cursor and drawing our own fake mouse-cursor is needed to be able to scale the
|
||||
// mouse-cursor up and to re-position those mouse-cursor to match to the chosen zoom-level.
|
||||
QSize cursorSize = cursor.image().size() / cursor.image().devicePixelRatio();
|
||||
if (mousePointer == MousePointerScale) {
|
||||
cursorSize *= zoom;
|
||||
|
||||
GLTexture *cursorTexture = ensureCursorTexture();
|
||||
if (cursorTexture) {
|
||||
const auto cursor = effects->cursorImage();
|
||||
QSize cursorSize = cursor.image().size() / cursor.image().devicePixelRatio();
|
||||
if (mousePointer == MousePointerScale) {
|
||||
cursorSize *= zoom;
|
||||
}
|
||||
|
||||
const QPoint p = effects->cursorPos() - cursor.hotSpot();
|
||||
QRect rect(p * zoom + QPoint(data.xTranslation(), data.yTranslation()), cursorSize);
|
||||
|
||||
cursorTexture->bind();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
QMatrix4x4 mvp = data.projectionMatrix();
|
||||
mvp.translate(rect.x(), rect.y());
|
||||
s->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
|
||||
cursorTexture->render(rect);
|
||||
ShaderManager::instance()->popShader();
|
||||
cursorTexture->unbind();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
const QPoint p = effects->cursorPos() - cursor.hotSpot();
|
||||
QRect rect(p * zoom + QPoint(data.xTranslation(), data.yTranslation()), cursorSize);
|
||||
|
||||
cursorTexture->bind();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
QMatrix4x4 mvp = data.projectionMatrix();
|
||||
mvp.translate(rect.x(), rect.y());
|
||||
s->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
|
||||
cursorTexture->render(rect);
|
||||
ShaderManager::instance()->popShader();
|
||||
cursorTexture->unbind();
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -502,6 +570,14 @@ void ZoomEffect::slotWindowDamaged()
|
|||
}
|
||||
}
|
||||
|
||||
void ZoomEffect::slotScreenRemoved(EffectScreen *screen)
|
||||
{
|
||||
if (OffscreenData *offscreenData = m_offscreenData.take(screen)) {
|
||||
effects->makeOpenGLContextCurrent();
|
||||
delete offscreenData;
|
||||
}
|
||||
}
|
||||
|
||||
void ZoomEffect::moveFocus(const QPoint &point)
|
||||
{
|
||||
if (zoom == 1.0) {
|
||||
|
|
|
@ -24,7 +24,9 @@ namespace KWin
|
|||
class ZoomAccessibilityIntegration;
|
||||
#endif
|
||||
|
||||
class GLFramebuffer;
|
||||
class GLTexture;
|
||||
class GLVertexBuffer;
|
||||
|
||||
class ZoomEffect
|
||||
: public Effect
|
||||
|
@ -94,6 +96,7 @@ private Q_SLOTS:
|
|||
Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
|
||||
Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
|
||||
void slotWindowDamaged();
|
||||
void slotScreenRemoved(EffectScreen *screen);
|
||||
|
||||
private:
|
||||
void showCursor();
|
||||
|
@ -101,7 +104,16 @@ private:
|
|||
void moveZoom(int x, int y);
|
||||
|
||||
private:
|
||||
struct OffscreenData
|
||||
{
|
||||
QScopedPointer<GLTexture> texture;
|
||||
QScopedPointer<GLFramebuffer> framebuffer;
|
||||
QScopedPointer<GLVertexBuffer> vbo;
|
||||
QRect viewport;
|
||||
};
|
||||
|
||||
GLTexture *ensureCursorTexture();
|
||||
OffscreenData *ensureOffscreenData(EffectScreen *screen);
|
||||
void markCursorTextureDirty();
|
||||
|
||||
#if HAVE_ACCESSIBILITY
|
||||
|
@ -138,6 +150,7 @@ private:
|
|||
int xMove, yMove;
|
||||
double moveFactor;
|
||||
std::chrono::milliseconds lastPresentTime;
|
||||
QHash<EffectScreen *, OffscreenData *> m_offscreenData;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in a new issue