Introduce dedicated PaintRedirector subclasses for OpenGL/XRender

PaintRedirector is turned into an abstract class providing a factory
method which returns either an instance of
* OpenGLPaintRedirector
* NativeXRenderPaintRedirector
* RasterXRenderPaintRedirector

OpenGLPaintRedirector is basically doing exactly the same as the parent
class used to do before. Though the idea is to extend the functionality
to have the PaintRedirector write directly into OpenGL textures to limit
copying the complete decorations.

NativeXRenderPaintRedirector is similar to OpenGLPaintRedirector by
rendering into a QPixmap and providing the pictureHandle for the QPixmap
to SceneXRender.

RasterXRenderPaintRedirector is providing the functionality for the case
that the QPixmap/XPixmap relationship is not present. From the QPixmap
containing the pending decoration paint a QImage is created and then the
relevent parts are copied directly into the decoration pixmap.

REVIEW: 109074
This commit is contained in:
Martin Gräßlin 2013-02-18 09:07:42 +01:00
parent e9eb48c735
commit fba7504063
5 changed files with 310 additions and 65 deletions

View file

@ -452,7 +452,7 @@ void Client::updateDecoration(bool check_workspace_pos, bool force)
move(calculateGravitation(false));
plainResize(sizeForClientSize(clientSize()), ForceGeometrySet);
if (Compositor::compositing()) {
paintRedirector = new PaintRedirector(this, decoration->widget());
paintRedirector = PaintRedirector::create(this, decoration->widget());
discardWindowPixmap();
}
emit geometryShapeChanged(this, oldgeom);

View file

@ -28,6 +28,7 @@ DEALINGS IN THE SOFTWARE.
#include "client.h"
#include "deleted.h"
#include "effects.h"
#include <kwinxrenderutils.h>
#include <kdebug.h>
#include <QPaintEngine>
#include <qevent.h>
@ -37,25 +38,30 @@ DEALINGS IN THE SOFTWARE.
namespace KWin
{
PaintRedirector *PaintRedirector::create(Client *c, QWidget *widget)
{
if (effects->isOpenGLCompositing()) {
return new OpenGLPaintRedirector(c, widget);
} else {
if (!Extensions::nonNativePixmaps()) {
return new NativeXRenderPaintRedirector(c, widget);
}
return new RasterXRenderPaintRedirector(c, widget);
}
}
PaintRedirector::PaintRedirector(Client *c, QWidget* w)
: QObject(w)
, widget(w)
, recursionCheck(false)
, m_client(c)
, m_responsibleForPixmap(!effects->isOpenGLCompositing())
, m_requiresRepaint(false)
{
added(w);
resizePixmaps();
}
PaintRedirector::~PaintRedirector()
{
if (m_responsibleForPixmap) {
for (int i=0; i<PixmapCount; ++i) {
XFreePixmap(display(), m_pixmaps[i].handle());
}
}
}
void PaintRedirector::reparent(Deleted *d)
@ -176,34 +182,35 @@ void PaintRedirector::ensurePixmapsPainted()
return;
QPixmap p = performPendingPaint();
preparePaint(p);
QRect rects[PixmapCount];
m_client->layoutDecorationRects(rects[LeftPixmap], rects[TopPixmap], rects[RightPixmap], rects[BottomPixmap], Client::DecorationRelative);
for (int i=0; i<PixmapCount; ++i) {
repaintPixmap(m_pixmaps[i], rects[i], p, pending);
repaintPixmap(DecorationPixmap(i), rects[i], p, pending);
}
pending = QRegion();
scheduled = QRegion();
XSync(display(), false);
xcb_flush(connection());
}
void PaintRedirector::repaintPixmap(QPixmap &pix, const QRect &r, const QPixmap &src, QRegion reg)
void PaintRedirector::preparePaint(const QPixmap &pending)
{
Q_UNUSED(pending)
}
void PaintRedirector::repaintPixmap(DecorationPixmap border, const QRect &r, const QPixmap &src, QRegion reg)
{
if (!r.isValid())
return;
QRect b = reg.boundingRect();
const QRect b = reg.boundingRect();
reg &= r;
if (reg.isEmpty())
return;
QPainter pt(&pix);
pt.translate(-r.topLeft());
pt.setCompositionMode(QPainter::CompositionMode_Source);
pt.setClipRegion(reg);
pt.drawPixmap(b.topLeft(), src);
pt.end();
paint(border, r, b, src, reg);
}
void PaintRedirector::resizePixmaps()
@ -212,24 +219,7 @@ void PaintRedirector::resizePixmaps()
m_client->layoutDecorationRects(rects[LeftPixmap], rects[TopPixmap], rects[RightPixmap], rects[BottomPixmap], Client::DecorationRelative);
for (int i=0; i<PixmapCount; ++i) {
if (m_pixmaps[i].size() == rects[i].size()) {
// size unchanged, no need to recreate, but repaint
m_pixmaps[i].fill(Qt::transparent);
continue;
}
if (!m_responsibleForPixmap) {
m_pixmaps[i] = QPixmap(rects[i].size());
} else {
if (!m_pixmaps[i].isNull() && m_pixmaps[i].paintEngine()->type() == QPaintEngine::X11) {
XFreePixmap(display(), m_pixmaps[i].handle());
}
Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
rects[i].size().width(), rects[i].height(),
32);
m_pixmaps[i] = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
}
// Make sure the pixmaps are created with alpha channels
m_pixmaps[i].fill(Qt::transparent);
resize(DecorationPixmap(i), rects[i].size());
}
// repaint
@ -238,6 +228,150 @@ void PaintRedirector::resizePixmaps()
}
}
const QPixmap *PaintRedirector::pixmap(PaintRedirector::DecorationPixmap border) const
{
Q_UNUSED(border)
return NULL;
}
xcb_render_picture_t PaintRedirector::picture(PaintRedirector::DecorationPixmap border) const
{
Q_UNUSED(border)
return XCB_RENDER_PICTURE_NONE;
}
OpenGLPaintRedirector::OpenGLPaintRedirector(Client *c, QWidget *widget)
: PaintRedirector(c, widget)
{
resizePixmaps();
}
OpenGLPaintRedirector::~OpenGLPaintRedirector()
{
}
const QPixmap *OpenGLPaintRedirector::pixmap(PaintRedirector::DecorationPixmap border) const
{
return &m_pixmaps[border];
}
void OpenGLPaintRedirector::resize(PaintRedirector::DecorationPixmap border, const QSize &size)
{
if (m_pixmaps[border].size() != size) {
m_pixmaps[border] = QPixmap(size);
}
m_pixmaps[border].fill(Qt::transparent);
}
void OpenGLPaintRedirector::paint(PaintRedirector::DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg)
{
QPainter pt(&m_pixmaps[border]);
pt.translate(-r.topLeft());
pt.setCompositionMode(QPainter::CompositionMode_Source);
pt.setClipRegion(reg);
pt.drawPixmap(b.topLeft(), src);
pt.end();
}
RasterXRenderPaintRedirector::RasterXRenderPaintRedirector(Client *c, QWidget *widget)
: PaintRedirector(c, widget)
, m_gc(0)
{
for (int i=0; i<PixmapCount; ++i) {
m_pixmaps[i] = XCB_PIXMAP_NONE;
m_pictures[i] = NULL;
}
resizePixmaps();
}
RasterXRenderPaintRedirector::~RasterXRenderPaintRedirector()
{
for (int i=0; i<PixmapCount; ++i) {
if (m_pixmaps[i] != XCB_PIXMAP_NONE) {
xcb_free_pixmap(connection(), m_pixmaps[i]);
}
delete m_pictures[i];
}
if (m_gc != 0) {
xcb_free_gc(connection(), m_gc);
}
}
xcb_render_picture_t RasterXRenderPaintRedirector::picture(PaintRedirector::DecorationPixmap border) const
{
return *m_pictures[border];
}
void RasterXRenderPaintRedirector::resize(PaintRedirector::DecorationPixmap border, const QSize &size)
{
if (m_sizes[border] != size) {
if (m_pixmaps[border] != XCB_PIXMAP_NONE) {
xcb_free_pixmap(connection(), m_pixmaps[border]);
}
m_pixmaps[border] = xcb_generate_id(connection());
xcb_create_pixmap(connection(), 32, m_pixmaps[border], rootWindow(), size.width(), size.height());
delete m_pictures[border];
m_pictures[border] = new XRenderPicture(m_pixmaps[border], 32);
}
// fill transparent
xcb_rectangle_t rect = {0, 0, uint16_t(size.width()), uint16_t(size.height())};
xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, *m_pictures[border], preMultiply(Qt::transparent), 1, &rect);
}
void RasterXRenderPaintRedirector::preparePaint(const QPixmap &pending)
{
m_tempImage = pending.toImage();
}
void RasterXRenderPaintRedirector::paint(PaintRedirector::DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg)
{
Q_UNUSED(src)
// clip the sub area
const QRect bounding = reg.boundingRect();
const QPoint offset = bounding.topLeft() - r.topLeft();
if (m_gc == 0) {
m_gc = xcb_generate_id(connection());
xcb_create_gc(connection(), m_gc, m_pixmaps[border], 0, NULL);
}
const QImage img(m_tempImage.copy(QRect(bounding.topLeft() - b.topLeft(), bounding.size())));
xcb_put_image(connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmaps[border], m_gc,
img.width(), img.height(), offset.x(), offset.y(), 0, 32, img.byteCount(), img.constBits());
}
NativeXRenderPaintRedirector::NativeXRenderPaintRedirector(Client *c, QWidget *widget)
: PaintRedirector(c, widget)
{
resizePixmaps();
}
NativeXRenderPaintRedirector::~NativeXRenderPaintRedirector()
{
}
xcb_render_picture_t NativeXRenderPaintRedirector::picture(PaintRedirector::DecorationPixmap border) const
{
return m_pixmaps[border].x11PictureHandle();
}
void NativeXRenderPaintRedirector::resize(PaintRedirector::DecorationPixmap border, const QSize &size)
{
if (m_pixmaps[border].size() != size) {
m_pixmaps[border] = QPixmap(size);
}
m_pixmaps[border].fill(Qt::transparent);
}
void NativeXRenderPaintRedirector::paint(PaintRedirector::DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg)
{
QPainter pt(&m_pixmaps[border]);
pt.translate(-r.topLeft());
pt.setCompositionMode(QPainter::CompositionMode_Source);
pt.setClipRegion(reg);
pt.drawPixmap(b.topLeft(), src);
pt.end();
}
} // namespace
#include "paintredirector.moc"

View file

@ -30,12 +30,16 @@ DEALINGS IN THE SOFTWARE.
#include <qtimer.h>
#include <qwidget.h>
#include <qbasictimer.h>
// xcb
#include <xcb/render.h>
namespace KWin
{
// forward declarations
class Client;
class Deleted;
class XRenderPicture;
// This class redirects all painting of a given widget (including its children)
// into a paint device (QPixmap).
@ -51,7 +55,6 @@ public:
LeftPixmap,
PixmapCount
};
PaintRedirector(Client *c, QWidget* widget);
virtual ~PaintRedirector();
QPixmap performPendingPaint();
virtual bool eventFilter(QObject* o, QEvent* e);
@ -66,18 +69,14 @@ public:
}
void resizePixmaps();
const QPixmap *topDecoPixmap() const {
return &m_pixmaps[TopPixmap];
}
const QPixmap *leftDecoPixmap() const {
return &m_pixmaps[LeftPixmap];
}
const QPixmap *bottomDecoPixmap() const {
return &m_pixmaps[BottomPixmap];
}
const QPixmap *rightDecoPixmap() const {
return &m_pixmaps[RightPixmap];
}
template <typename T>
T topDecoPixmap() const;
template <typename T>
T leftDecoPixmap() const;
template <typename T>
T bottomDecoPixmap() const;
template <typename T>
T rightDecoPixmap() const;
/**
* Used by Deleted::copyToDeleted() to move the PaintRedirector to the Deleted.
@ -85,16 +84,24 @@ public:
* is created.
**/
void reparent(Deleted *d);
static PaintRedirector *create(Client *c, QWidget* widget);
public slots:
void ensurePixmapsPainted();
protected:
PaintRedirector(Client *c, QWidget* widget);
virtual const QPixmap *pixmap(DecorationPixmap border) const;
virtual xcb_render_picture_t picture(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size) = 0;
virtual void preparePaint(const QPixmap &pending);
virtual void paint(DecorationPixmap border, const QRect& r, const QRect &b, const QPixmap& src, const QRegion &reg) = 0;
private:
void added(QWidget* widget);
void removed(QWidget* widget);
bool isToolTip(QWidget* widget) const;
void timerEvent(QTimerEvent* event);
void repaintPixmap(QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg);
void repaintPixmap(DecorationPixmap border, const QRect& r, const QPixmap& src, QRegion reg);
QWidget* widget;
QRegion pending;
QRegion scheduled;
@ -103,12 +110,116 @@ private:
QBasicTimer cleanupTimer;
Client *m_client;
// we (instead of Qt) initialize the Pixmaps, and have to free them
bool m_responsibleForPixmap;
bool m_requiresRepaint;
};
class OpenGLPaintRedirector : public PaintRedirector
{
Q_OBJECT
public:
OpenGLPaintRedirector(Client *c, QWidget *widget);
virtual ~OpenGLPaintRedirector();
protected:
virtual const QPixmap *pixmap(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size);
virtual void paint(DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg);
private:
QPixmap m_pixmaps[PixmapCount];
};
class NativeXRenderPaintRedirector : public PaintRedirector
{
Q_OBJECT
public:
NativeXRenderPaintRedirector(Client *c, QWidget *widget);
virtual ~NativeXRenderPaintRedirector();
protected:
virtual xcb_render_picture_t picture(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size);
virtual void paint(DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg);
private:
QPixmap m_pixmaps[PixmapCount];
};
class RasterXRenderPaintRedirector : public PaintRedirector
{
Q_OBJECT
public:
RasterXRenderPaintRedirector(Client *c, QWidget *widget);
virtual ~RasterXRenderPaintRedirector();
protected:
virtual xcb_render_picture_t picture(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size);
virtual void paint(DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg);
virtual void preparePaint(const QPixmap &pending);
private:
QSize m_sizes[PixmapCount];
xcb_pixmap_t m_pixmaps[PixmapCount];
xcb_gcontext_t m_gc;
XRenderPicture* m_pictures[PixmapCount];
QImage m_tempImage;
};
template <>
inline
const QPixmap *PaintRedirector::bottomDecoPixmap() const
{
return pixmap(BottomPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::leftDecoPixmap() const
{
return pixmap(LeftPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::rightDecoPixmap() const
{
return pixmap(RightPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::topDecoPixmap() const
{
return pixmap(TopPixmap);
}
template <>
inline
xcb_render_picture_t PaintRedirector::bottomDecoPixmap() const
{
return picture(BottomPixmap);
}
template <>
inline
xcb_render_picture_t PaintRedirector::leftDecoPixmap() const
{
return picture(LeftPixmap);
}
template <>
inline
xcb_render_picture_t PaintRedirector::rightDecoPixmap() const
{
return picture(RightPixmap);
}
template <>
inline
xcb_render_picture_t PaintRedirector::topDecoPixmap() const
{
return picture(TopPixmap);
}
} // namespace
#endif

View file

@ -1116,10 +1116,10 @@ void SceneOpenGL::Window::paintDecorations(const WindowPaintData &data, const QR
t->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative);
const QPixmap *left = redirector->leftDecoPixmap();
const QPixmap *top = redirector->topDecoPixmap();
const QPixmap *right = redirector->rightDecoPixmap();
const QPixmap *bottom = redirector->bottomDecoPixmap();
const QPixmap *left = redirector->leftDecoPixmap<const QPixmap*>();
const QPixmap *top = redirector->topDecoPixmap<const QPixmap*>();
const QPixmap *right = redirector->rightDecoPixmap<const QPixmap*>();
const QPixmap *bottom = redirector->bottomDecoPixmap<const QPixmap*>();
WindowQuadList topList, leftList, rightList, bottomList;

View file

@ -581,10 +581,10 @@ void SceneXrender::Window::performPaint(int mask, QRegion region, WindowPaintDat
//BEGIN deco preparations
bool noBorder = true;
const QPixmap *left = NULL;
const QPixmap *top = NULL;
const QPixmap *right = NULL;
const QPixmap *bottom = NULL;
xcb_render_picture_t left = XCB_RENDER_PICTURE_NONE;
xcb_render_picture_t top = XCB_RENDER_PICTURE_NONE;
xcb_render_picture_t right = XCB_RENDER_PICTURE_NONE;
xcb_render_picture_t bottom = XCB_RENDER_PICTURE_NONE;
PaintRedirector *redirector = NULL;
QRect dtr, dlr, drr, dbr;
if (client || deleted) {
@ -600,10 +600,10 @@ void SceneXrender::Window::performPaint(int mask, QRegion region, WindowPaintDat
}
if (redirector) {
redirector->ensurePixmapsPainted();
left = redirector->leftDecoPixmap();
top = redirector->topDecoPixmap();
right = redirector->rightDecoPixmap();
bottom = redirector->bottomDecoPixmap();
left = redirector->leftDecoPixmap<xcb_render_picture_t>();
top = redirector->topDecoPixmap<xcb_render_picture_t>();
right = redirector->rightDecoPixmap<xcb_render_picture_t>();
bottom = redirector->bottomDecoPixmap<xcb_render_picture_t>();
}
if (!noBorder) {
MAP_RECT_TO_TARGET(dtr);
@ -677,7 +677,7 @@ XRenderComposite(display(), PictOpOver, m_xrenderShadow->shadowPixmap(SceneXRend
}
#define RENDER_DECO_PART(_PART_, _RECT_) \
XRenderComposite(display(), PictOpOver, _PART_->x11PictureHandle(), decorationAlpha, renderTarget,\
XRenderComposite(display(), PictOpOver, _PART_, decorationAlpha, renderTarget,\
0, 0, 0, 0, _RECT_.x(), _RECT_.y(), _RECT_.width(), _RECT_.height())
if (client || deleted) {