scene: Decouple scene bits from Shadow class

Currently the Shadow class is scene specific, which adds coupling
between Window and scene bits.

This change introduces ShadowTextureProvider that contains scene
specific stuff so the Shadow acts like a data source and is not coupled
to concrete scene.
This commit is contained in:
Vlad Zahorodnii 2023-01-17 20:22:28 +02:00
parent 21d410710c
commit 0e906ec182
10 changed files with 122 additions and 119 deletions

View file

@ -159,9 +159,9 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
if (auto shadowItem = qobject_cast<ShadowItem *>(item)) {
if (!geometry.isEmpty()) {
SceneOpenGLShadow *shadow = static_cast<SceneOpenGLShadow *>(shadowItem->shadow());
OpenGLShadowTextureProvider *textureProvider = static_cast<OpenGLShadowTextureProvider *>(shadowItem->textureProvider());
context->renderNodes.append(RenderNode{
.texture = shadow->shadowTexture(),
.texture = textureProvider->shadowTexture(),
.geometry = geometry,
.transformMatrix = context->transformStack.top(),
.opacity = context->opacityStack.top(),

View file

@ -5,16 +5,28 @@
*/
#include "scene/shadowitem.h"
#include "composite.h"
#include "deleted.h"
#include "scene/workspacescene.h"
#include "shadow.h"
namespace KWin
{
ShadowTextureProvider::ShadowTextureProvider(Shadow *shadow)
: m_shadow(shadow)
{
}
ShadowTextureProvider::~ShadowTextureProvider()
{
}
ShadowItem::ShadowItem(Shadow *shadow, Window *window, Scene *scene, Item *parent)
: Item(scene, parent)
, m_window(window)
, m_shadow(shadow)
, m_textureProvider(Compositor::self()->scene()->createShadowTextureProvider(shadow))
{
connect(window, &Window::windowClosed, this, &ShadowItem::handleWindowClosed);
@ -35,6 +47,11 @@ Shadow *ShadowItem::shadow() const
return m_shadow;
}
ShadowTextureProvider *ShadowItem::textureProvider() const
{
return m_textureProvider.get();
}
void ShadowItem::updateGeometry()
{
const QRectF rect = m_shadow->rect() + m_shadow->offset();
@ -48,6 +65,7 @@ void ShadowItem::handleTextureChanged()
{
scheduleRepaint(rect());
discardQuads();
m_textureDirty = true;
}
void ShadowItem::handleWindowClosed(Window *original, Deleted *deleted)
@ -287,4 +305,12 @@ WindowQuadList ShadowItem::buildQuads() const
return quads;
}
void ShadowItem::preprocess()
{
if (m_textureDirty) {
m_textureDirty = false;
m_textureProvider->update();
}
}
} // namespace KWin

View file

@ -15,6 +15,20 @@ class Deleted;
class Shadow;
class Window;
class KWIN_EXPORT ShadowTextureProvider
{
public:
explicit ShadowTextureProvider(Shadow *shadow);
virtual ~ShadowTextureProvider();
Shadow *shadow() const { return m_shadow; }
virtual void update() = 0;
protected:
Shadow *m_shadow;
};
/**
* The ShadowItem class represents a nine-tile patch server-side drop-shadow.
*/
@ -27,9 +41,11 @@ public:
~ShadowItem() override;
Shadow *shadow() const;
ShadowTextureProvider *textureProvider() const;
protected:
WindowQuadList buildQuads() const override;
void preprocess() override;
private Q_SLOTS:
void handleTextureChanged();
@ -39,6 +55,8 @@ private Q_SLOTS:
private:
Window *m_window;
Shadow *m_shadow = nullptr;
std::unique_ptr<ShadowTextureProvider> m_textureProvider;
bool m_textureDirty = true;
};
} // namespace KWin

View file

@ -38,6 +38,7 @@ class RenderLoop;
class WorkspaceScene;
class Shadow;
class ShadowItem;
class ShadowTextureProvider;
class SurfaceItem;
class WindowItem;
@ -57,21 +58,12 @@ public:
void postPaint() override;
void paint(RenderTarget *renderTarget, const QRegion &region) override;
/**
* @brief Creates the Scene specific Shadow subclass.
*
* An implementing class has to create a proper instance. It is not allowed to
* return @c null.
*
* @param window The Window for which the Shadow needs to be created.
*/
virtual std::unique_ptr<Shadow> createShadow(Window *window) = 0;
virtual bool makeOpenGLContextCurrent();
virtual void doneOpenGLContextCurrent();
virtual bool supportsNativeFence() const;
virtual DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *) = 0;
virtual std::unique_ptr<ShadowTextureProvider> createShadowTextureProvider(Shadow *shadow) = 0;
/**
* Whether the Scene is able to drive animations.

View file

@ -19,6 +19,7 @@
#include "core/output.h"
#include "decorations/decoratedclient.h"
#include "scene/itemrenderer_opengl.h"
#include "shadow.h"
#include "window.h"
#include <cmath>
@ -69,16 +70,16 @@ bool WorkspaceSceneOpenGL::supportsNativeFence() const
return m_backend->supportsNativeFence();
}
std::unique_ptr<Shadow> WorkspaceSceneOpenGL::createShadow(Window *window)
{
return std::make_unique<SceneOpenGLShadow>(window);
}
DecorationRenderer *WorkspaceSceneOpenGL::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
return new SceneOpenGLDecorationRenderer(impl);
}
std::unique_ptr<ShadowTextureProvider> WorkspaceSceneOpenGL::createShadowTextureProvider(Shadow *shadow)
{
return std::make_unique<OpenGLShadowTextureProvider>(shadow);
}
bool WorkspaceSceneOpenGL::animationsSupported() const
{
return !GLPlatform::instance()->isSoftwareEmulation();
@ -99,15 +100,15 @@ public:
DecorationShadowTextureCache(const DecorationShadowTextureCache &) = delete;
static DecorationShadowTextureCache &instance();
void unregister(SceneOpenGLShadow *shadow);
std::shared_ptr<GLTexture> getTexture(SceneOpenGLShadow *shadow);
void unregister(ShadowTextureProvider *provider);
std::shared_ptr<GLTexture> getTexture(ShadowTextureProvider *provider);
private:
DecorationShadowTextureCache() = default;
struct Data
{
std::shared_ptr<GLTexture> texture;
QVector<SceneOpenGLShadow *> shadows;
QVector<ShadowTextureProvider *> providers;
};
QHash<KDecoration2::DecorationShadow *, Data> m_cache;
};
@ -123,22 +124,22 @@ DecorationShadowTextureCache::~DecorationShadowTextureCache()
Q_ASSERT(m_cache.isEmpty());
}
void DecorationShadowTextureCache::unregister(SceneOpenGLShadow *shadow)
void DecorationShadowTextureCache::unregister(ShadowTextureProvider *provider)
{
auto it = m_cache.begin();
while (it != m_cache.end()) {
auto &d = it.value();
// check whether the Vector of Shadows contains our shadow and remove all of them
auto glIt = d.shadows.begin();
while (glIt != d.shadows.end()) {
if (*glIt == shadow) {
glIt = d.shadows.erase(glIt);
auto glIt = d.providers.begin();
while (glIt != d.providers.end()) {
if (*glIt == provider) {
glIt = d.providers.erase(glIt);
} else {
glIt++;
}
}
// if there are no shadows any more we can erase the cache entry
if (d.shadows.isEmpty()) {
if (d.providers.isEmpty()) {
it = m_cache.erase(it);
} else {
it++;
@ -146,31 +147,32 @@ void DecorationShadowTextureCache::unregister(SceneOpenGLShadow *shadow)
}
}
std::shared_ptr<GLTexture> DecorationShadowTextureCache::getTexture(SceneOpenGLShadow *shadow)
std::shared_ptr<GLTexture> DecorationShadowTextureCache::getTexture(ShadowTextureProvider *provider)
{
Shadow *shadow = provider->shadow();
Q_ASSERT(shadow->hasDecorationShadow());
unregister(shadow);
unregister(provider);
const auto &decoShadow = shadow->decorationShadow().toStrongRef();
Q_ASSERT(!decoShadow.isNull());
auto it = m_cache.find(decoShadow.data());
if (it != m_cache.end()) {
Q_ASSERT(!it.value().shadows.contains(shadow));
it.value().shadows << shadow;
Q_ASSERT(!it.value().providers.contains(provider));
it.value().providers << provider;
return it.value().texture;
}
Data d;
d.shadows << shadow;
d.providers << provider;
d.texture = std::make_shared<GLTexture>(shadow->decorationShadowImage());
m_cache.insert(decoShadow.data(), d);
return d.texture;
}
SceneOpenGLShadow::SceneOpenGLShadow(Window *window)
: Shadow(window)
OpenGLShadowTextureProvider::OpenGLShadowTextureProvider(Shadow *shadow)
: ShadowTextureProvider(shadow)
{
}
SceneOpenGLShadow::~SceneOpenGLShadow()
OpenGLShadowTextureProvider::~OpenGLShadowTextureProvider()
{
WorkspaceScene *scene = Compositor::self()->scene();
if (scene) {
@ -180,30 +182,28 @@ SceneOpenGLShadow::~SceneOpenGLShadow()
}
}
bool SceneOpenGLShadow::prepareBackend()
void OpenGLShadowTextureProvider::update()
{
if (hasDecorationShadow()) {
if (m_shadow->hasDecorationShadow()) {
// simplifies a lot by going directly to
WorkspaceScene *scene = Compositor::self()->scene();
scene->makeOpenGLContextCurrent();
m_texture = DecorationShadowTextureCache::instance().getTexture(this);
return true;
return;
}
const QSize top(shadowElement(ShadowElementTop).size());
const QSize topRight(shadowElement(ShadowElementTopRight).size());
const QSize right(shadowElement(ShadowElementRight).size());
const QSize bottom(shadowElement(ShadowElementBottom).size());
const QSize bottomLeft(shadowElement(ShadowElementBottomLeft).size());
const QSize left(shadowElement(ShadowElementLeft).size());
const QSize topLeft(shadowElement(ShadowElementTopLeft).size());
const QSize bottomRight(shadowElement(ShadowElementBottomRight).size());
const QSize top(m_shadow->shadowElement(Shadow::ShadowElementTop).size());
const QSize topRight(m_shadow->shadowElement(Shadow::ShadowElementTopRight).size());
const QSize right(m_shadow->shadowElement(Shadow::ShadowElementRight).size());
const QSize bottom(m_shadow->shadowElement(Shadow::ShadowElementBottom).size());
const QSize bottomLeft(m_shadow->shadowElement(Shadow::ShadowElementBottomLeft).size());
const QSize left(m_shadow->shadowElement(Shadow::ShadowElementLeft).size());
const QSize topLeft(m_shadow->shadowElement(Shadow::ShadowElementTopLeft).size());
const QSize bottomRight(m_shadow->shadowElement(Shadow::ShadowElementBottomRight).size());
const int width = std::max({topLeft.width(), left.width(), bottomLeft.width()}) + std::max(top.width(), bottom.width()) + std::max({topRight.width(), right.width(), bottomRight.width()});
const int height = std::max({topLeft.height(), top.height(), topRight.height()}) + std::max(left.height(), right.height()) + std::max({bottomLeft.height(), bottom.height(), bottomRight.height()});
if (width == 0 || height == 0) {
return false;
return;
}
QImage image(width, height, QImage::Format_ARGB32);
@ -215,16 +215,16 @@ bool SceneOpenGLShadow::prepareBackend()
QPainter p;
p.begin(&image);
p.drawImage(QRectF(0, 0, topLeft.width(), topLeft.height()), shadowElement(ShadowElementTopLeft));
p.drawImage(QRectF(innerRectLeft, 0, top.width(), top.height()), shadowElement(ShadowElementTop));
p.drawImage(QRectF(width - topRight.width(), 0, topRight.width(), topRight.height()), shadowElement(ShadowElementTopRight));
p.drawImage(QRectF(0, 0, topLeft.width(), topLeft.height()), m_shadow->shadowElement(Shadow::ShadowElementTopLeft));
p.drawImage(QRectF(innerRectLeft, 0, top.width(), top.height()), m_shadow->shadowElement(Shadow::ShadowElementTop));
p.drawImage(QRectF(width - topRight.width(), 0, topRight.width(), topRight.height()), m_shadow->shadowElement(Shadow::ShadowElementTopRight));
p.drawImage(QRectF(0, innerRectTop, left.width(), left.height()), shadowElement(ShadowElementLeft));
p.drawImage(QRectF(width - right.width(), innerRectTop, right.width(), right.height()), shadowElement(ShadowElementRight));
p.drawImage(QRectF(0, innerRectTop, left.width(), left.height()), m_shadow->shadowElement(Shadow::ShadowElementLeft));
p.drawImage(QRectF(width - right.width(), innerRectTop, right.width(), right.height()), m_shadow->shadowElement(Shadow::ShadowElementRight));
p.drawImage(QRectF(0, height - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()), shadowElement(ShadowElementBottomLeft));
p.drawImage(QRectF(innerRectLeft, height - bottom.height(), bottom.width(), bottom.height()), shadowElement(ShadowElementBottom));
p.drawImage(QRectF(width - bottomRight.width(), height - bottomRight.height(), bottomRight.width(), bottomRight.height()), shadowElement(ShadowElementBottomRight));
p.drawImage(QRectF(0, height - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()), m_shadow->shadowElement(Shadow::ShadowElementBottomLeft));
p.drawImage(QRectF(innerRectLeft, height - bottom.height(), bottom.width(), bottom.height()), m_shadow->shadowElement(Shadow::ShadowElementBottom));
p.drawImage(QRectF(width - bottomRight.width(), height - bottomRight.height(), bottomRight.width(), bottomRight.height()), m_shadow->shadowElement(Shadow::ShadowElementBottomRight));
p.end();
@ -251,8 +251,6 @@ bool SceneOpenGLShadow::prepareBackend()
}
}
WorkspaceScene *scene = Compositor::self()->scene();
scene->makeOpenGLContextCurrent();
m_texture = std::make_shared<GLTexture>(image);
if (m_texture->internalFormat() == GL_R8) {
@ -260,8 +258,6 @@ bool SceneOpenGLShadow::prepareBackend()
m_texture->bind();
m_texture->setSwizzle(GL_ZERO, GL_ZERO, GL_ZERO, GL_RED);
}
return true;
}
SceneOpenGLDecorationRenderer::SceneOpenGLDecorationRenderer(Decoration::DecoratedClientImpl *client)

View file

@ -13,8 +13,8 @@
#include "openglbackend.h"
#include "scene/decorationitem.h"
#include "scene/shadowitem.h"
#include "scene/workspacescene.h"
#include "shadow.h"
#include "kwinglutils.h"
@ -30,11 +30,11 @@ public:
explicit WorkspaceSceneOpenGL(OpenGLBackend *backend);
~WorkspaceSceneOpenGL() override;
std::unique_ptr<Shadow> createShadow(Window *window) override;
bool makeOpenGLContextCurrent() override;
void doneOpenGLContextCurrent() override;
bool supportsNativeFence() const override;
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
std::unique_ptr<ShadowTextureProvider> createShadowTextureProvider(Shadow *shadow) override;
bool animationsSupported() const override;
OpenGLBackend *backend() const
@ -55,20 +55,18 @@ private:
* This class extends Shadow by the Elements required for OpenGL rendering.
* @author Martin Gräßlin <mgraesslin@kde.org>
*/
class SceneOpenGLShadow
: public Shadow
class OpenGLShadowTextureProvider : public ShadowTextureProvider
{
public:
explicit SceneOpenGLShadow(Window *window);
~SceneOpenGLShadow() override;
explicit OpenGLShadowTextureProvider(Shadow *shadow);
~OpenGLShadowTextureProvider() override;
GLTexture *shadowTexture()
{
return m_texture.get();
}
protected:
bool prepareBackend() override;
void update() override;
private:
std::shared_ptr<GLTexture> m_texture;

View file

@ -36,33 +36,28 @@ WorkspaceSceneQPainter::~WorkspaceSceneQPainter()
{
}
std::unique_ptr<Shadow> WorkspaceSceneQPainter::createShadow(Window *window)
{
return std::make_unique<SceneQPainterShadow>(window);
}
DecorationRenderer *WorkspaceSceneQPainter::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
return new SceneQPainterDecorationRenderer(impl);
}
std::unique_ptr<ShadowTextureProvider> WorkspaceSceneQPainter::createShadowTextureProvider(Shadow *shadow)
{
return std::make_unique<QPainterShadowTextureProvider>(shadow);
}
//****************************************
// QPainterShadow
//****************************************
SceneQPainterShadow::SceneQPainterShadow(Window *window)
: Shadow(window)
QPainterShadowTextureProvider::QPainterShadowTextureProvider(Shadow *shadow)
: ShadowTextureProvider(shadow)
{
}
SceneQPainterShadow::~SceneQPainterShadow()
void QPainterShadowTextureProvider::update()
{
}
bool SceneQPainterShadow::prepareBackend()
{
return true;
}
//****************************************
// QPainterDecorationRenderer
//****************************************

View file

@ -11,8 +11,8 @@
#include "qpainterbackend.h"
#include "scene/decorationitem.h"
#include "scene/shadowitem.h"
#include "scene/workspacescene.h"
#include "shadow.h"
namespace KWin
{
@ -25,8 +25,8 @@ public:
explicit WorkspaceSceneQPainter(QPainterBackend *backend);
~WorkspaceSceneQPainter() override;
std::unique_ptr<Shadow> createShadow(Window *window) override;
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
std::unique_ptr<ShadowTextureProvider> createShadowTextureProvider(Shadow *shadow) override;
bool animationsSupported() const override
{
@ -42,14 +42,12 @@ private:
QPainterBackend *m_backend;
};
class SceneQPainterShadow : public Shadow
class QPainterShadowTextureProvider : public ShadowTextureProvider
{
public:
SceneQPainterShadow(Window *window);
~SceneQPainterShadow() override;
explicit QPainterShadowTextureProvider(Shadow *shadow);
protected:
bool prepareBackend() override;
void update() override;
};
class SceneQPainterDecorationRenderer : public DecorationRenderer

View file

@ -10,9 +10,7 @@
#include "shadow.h"
// kwin
#include "atoms.h"
#include "composite.h"
#include "internalwindow.h"
#include "scene/workspacescene.h"
#include "wayland/shadow_interface.h"
#include "wayland/shmclientbuffer.h"
#include "wayland/surface_interface.h"
@ -60,7 +58,7 @@ std::unique_ptr<Shadow> Shadow::createShadowFromX11(Window *window)
{
auto data = Shadow::readX11ShadowProperty(window->window());
if (!data.isEmpty()) {
auto shadow = Compositor::self()->scene()->createShadow(window);
auto shadow = std::make_unique<Shadow>(window);
if (!shadow->init(data)) {
return nullptr;
}
@ -75,7 +73,7 @@ std::unique_ptr<Shadow> Shadow::createShadowFromDecoration(Window *window)
if (!window->decoration()) {
return nullptr;
}
auto shadow = Compositor::self()->scene()->createShadow(window);
auto shadow = std::make_unique<Shadow>(window);
if (!shadow->init(window->decoration())) {
return nullptr;
}
@ -92,7 +90,7 @@ std::unique_ptr<Shadow> Shadow::createShadowFromWayland(Window *window)
if (!s) {
return nullptr;
}
auto shadow = Compositor::self()->scene()->createShadow(window);
auto shadow = std::make_unique<Shadow>(window);
if (!shadow->init(s)) {
return nullptr;
}
@ -109,7 +107,7 @@ std::unique_ptr<Shadow> Shadow::createShadowFromInternalWindow(Window *window)
if (!handle) {
return nullptr;
}
auto shadow = Compositor::self()->scene()->createShadow(window);
auto shadow = std::make_unique<Shadow>(window);
if (!shadow->init(handle)) {
return nullptr;
}
@ -170,9 +168,6 @@ bool Shadow::init(const QVector<uint32_t> &data)
data[ShadowElementsCount + 1],
data[ShadowElementsCount + 2]);
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
Q_EMIT textureChanged();
return true;
}
@ -196,9 +191,6 @@ bool Shadow::init(KDecoration2::Decoration *decoration)
m_offset = m_decorationShadow->padding();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
Q_EMIT textureChanged();
return true;
}
@ -229,9 +221,6 @@ bool Shadow::init(const QPointer<KWaylandServer::ShadowInterface> &shadow)
m_offset = shadow->offset().toMargins();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
Q_EMIT textureChanged();
return true;
}
@ -263,10 +252,6 @@ bool Shadow::init(const QWindow *window)
m_offset = window->property("kwin_shadow_padding").value<QMargins>();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
Q_EMIT textureChanged();
return true;
}

View file

@ -47,6 +47,7 @@ class KWIN_EXPORT Shadow : public QObject
{
Q_OBJECT
public:
explicit Shadow(Window *window);
~Shadow() override;
/**
@ -58,7 +59,7 @@ public:
* delete the Shadow.
* @returns @c true when the shadow has been updated, @c false if the property is not set anymore.
*/
virtual bool updateShadow();
bool updateShadow();
/**
* Factory Method to create the shadow from the property.
@ -112,6 +113,10 @@ public:
{
return m_offset;
}
inline const QImage &shadowElement(ShadowElements element) const
{
return m_shadowElements[element];
}
Q_SIGNALS:
void offsetChanged();
@ -121,16 +126,6 @@ Q_SIGNALS:
public Q_SLOTS:
void geometryChanged();
protected:
Shadow(Window *window);
inline const QImage &shadowElement(ShadowElements element) const
{
return m_shadowElements[element];
};
virtual bool prepareBackend() = 0;
private:
static std::unique_ptr<Shadow> createShadowFromX11(Window *window);
static std::unique_ptr<Shadow> createShadowFromDecoration(Window *window);