From 62949ea655512c9c5974a0789f59f8729bef7d8f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Aug 2014 13:59:58 +0200 Subject: Add support for software layers Change-Id: Iefac991ea5b9124cc9dd482e809180aa5f3d96d7 Reviewed-by: Lars Knoll --- softwarecontext/context.cpp | 38 +++++++ softwarecontext/context.h | 16 +++ softwarecontext/imagenode.cpp | 54 +++++++--- softwarecontext/imagenode.h | 7 +- softwarecontext/pixmaptexture.h | 2 +- softwarecontext/softwarecontext.pro | 6 +- softwarecontext/softwarelayer.cpp | 200 ++++++++++++++++++++++++++++++++++++ softwarecontext/softwarelayer.h | 67 ++++++++++++ 8 files changed, 371 insertions(+), 19 deletions(-) create mode 100644 softwarecontext/softwarelayer.cpp create mode 100644 softwarecontext/softwarelayer.h diff --git a/softwarecontext/context.cpp b/softwarecontext/context.cpp index 69e05951c9..de0e644a80 100644 --- a/softwarecontext/context.cpp +++ b/softwarecontext/context.cpp @@ -47,6 +47,7 @@ #include "glyphnode.h" #include "ninepatchnode.h" #include "renderingvisitor.h" +#include "softwarelayer.h" #include #include @@ -107,6 +108,38 @@ void Renderer::render() backingStore->flush(rect); } +void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) +{ + + QSGRenderer::nodeChanged(node, state); +} + +PixmapRenderer::PixmapRenderer(QSGRenderContext *context) + : QSGRenderer(context) +{ + +} + +void PixmapRenderer::renderScene(GLuint) +{ + Q_UNREACHABLE(); +} + +void PixmapRenderer::render() +{ + Q_UNREACHABLE(); +} + +void PixmapRenderer::render(QPixmap *target) +{ + const QRect rect(0, 0, target->width(), target->height()); + target->fill(clearColor()); + QPainter painter(target); + painter.setRenderHint(QPainter::Antialiasing); + + RenderingVisitor(&painter).visitChildren(rootNode()); +} + RenderContext::RenderContext(QSGContext *ctx) : QSGRenderContext(ctx) , currentWindow(0) @@ -143,6 +176,11 @@ QSGNinePatchNode *Context::createQStyleNode() return new NinePatchNode(); } +QSGLayer *Context::createLayer(QSGRenderContext *renderContext) +{ + return new SoftwareLayer(renderContext); +} + void RenderContext::initialize(QOpenGLContext *context) { QSGRenderContext::initialize(context); diff --git a/softwarecontext/context.h b/softwarecontext/context.h index 75d75ac369..c2dfa469cb 100644 --- a/softwarecontext/context.h +++ b/softwarecontext/context.h @@ -62,8 +62,23 @@ public: virtual void render(); + void nodeChanged(QSGNode *node, QSGNode::DirtyState state); + private: QScopedPointer backingStore; + QRect m_dirtyRect; +}; + +class PixmapRenderer : public QSGRenderer +{ +public: + PixmapRenderer(QSGRenderContext *context); + + virtual void renderScene(GLuint fboId = 0); + + virtual void render(); + + void render(QPixmap *target); }; class RenderContext : public QSGRenderContext @@ -93,6 +108,7 @@ public: virtual QSGImageNode *createImageNode(); virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode); virtual QSGNinePatchNode *createQStyleNode(); + virtual QSGLayer *createLayer(QSGRenderContext *renderContext); private: }; diff --git a/softwarecontext/imagenode.cpp b/softwarecontext/imagenode.cpp index e56bfa7f11..a5cf996ebe 100644 --- a/softwarecontext/imagenode.cpp +++ b/softwarecontext/imagenode.cpp @@ -1,6 +1,7 @@ #include "imagenode.h" #include "pixmaptexture.h" +#include "softwarelayer.h" #include #include @@ -267,7 +268,10 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin } ImageNode::ImageNode() - : m_mirror(false) + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_texture(0) + , m_mirror(false) , m_smooth(true) , m_tileHorizontal(false) , m_tileVertical(false) @@ -299,12 +303,7 @@ void ImageNode::setSubSourceRect(const QRectF &rect) void ImageNode::setTexture(QSGTexture *texture) { - PixmapTexture *pt = qobject_cast(texture); - if (!pt) { - qWarning() << "Image used with invalid texture format."; - return; - } - m_pixmap = pt->pixmap(); + m_texture = texture; } void ImageNode::setMirror(bool mirror) @@ -336,6 +335,18 @@ void ImageNode::update() { } +void ImageNode::preprocess() +{ + bool doDirty = false; + QSGLayer *t = qobject_cast(m_texture); + if (t) { + doDirty = t->updateTexture(); + markDirty(DirtyGeometry); + } + if (doDirty) + markDirty(DirtyMaterial); +} + static Qt::TileRule getTileRule(qreal factor) { int ifactor = qRound(factor); @@ -352,29 +363,42 @@ void ImageNode::paint(QPainter *painter) { painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + const QPixmap &pm = pixmap(); + if (m_innerTargetRect != m_targetRect) { // border image QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); - SoftwareContext::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), + SoftwareContext::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), margins, tilerules, QDrawBorderPixmap::DrawingHints(0)); return; } if (m_tileHorizontal || m_tileVertical) { painter->save(); - qreal sx = m_targetRect.width()/(m_subSourceRect.width()*m_pixmap.width()); - qreal sy = m_targetRect.height()/(m_subSourceRect.height()*m_pixmap.height()); + qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); + qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); QMatrix transform(sx, 0, 0, sy, 0, 0); painter->setMatrix(transform, true); painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), - m_pixmap, - QPointF(m_subSourceRect.left()*m_pixmap.width(), m_subSourceRect.top()*m_pixmap.height())); + pm, + QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); painter->restore(); } else { - QRectF sr(m_subSourceRect.left()*m_pixmap.width(), m_subSourceRect.top()*m_pixmap.height(), - m_subSourceRect.width()*m_pixmap.width(), m_subSourceRect.height()*m_pixmap.height()); - painter->drawPixmap(m_targetRect, m_pixmap, sr); + QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), + m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); + painter->drawPixmap(m_targetRect, pm, sr); + } +} + +const QPixmap &ImageNode::pixmap() const +{ + if (PixmapTexture *pt = qobject_cast(m_texture)) { + return pt->pixmap(); + } else if (SoftwareLayer *layer = qobject_cast(m_texture)) { + return layer->pixmap(); + } else { + qFatal("Image used with invalid texture format."); } } diff --git a/softwarecontext/imagenode.h b/softwarecontext/imagenode.h index 6270d05f75..7b3f64b815 100644 --- a/softwarecontext/imagenode.h +++ b/softwarecontext/imagenode.h @@ -66,15 +66,20 @@ public: virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); virtual void update(); + virtual void preprocess(); + void paint(QPainter *painter); private: + const QPixmap &pixmap() const; + QRectF m_targetRect; QRectF m_innerTargetRect; QRectF m_innerSourceRect; QRectF m_subSourceRect; - QPixmap m_pixmap; + QSGTexture *m_texture; + bool m_mirror; bool m_smooth; bool m_tileHorizontal; diff --git a/softwarecontext/pixmaptexture.h b/softwarecontext/pixmaptexture.h index 314005f82a..fcf21c309e 100644 --- a/softwarecontext/pixmaptexture.h +++ b/softwarecontext/pixmaptexture.h @@ -15,7 +15,7 @@ public: virtual bool hasMipmaps() const; virtual void bind(); - QPixmap pixmap() const { return m_pixmap; } + const QPixmap &pixmap() const { return m_pixmap; } private: QPixmap m_pixmap; diff --git a/softwarecontext/softwarecontext.pro b/softwarecontext/softwarecontext.pro index d884729ffb..dbe948f151 100644 --- a/softwarecontext/softwarecontext.pro +++ b/softwarecontext/softwarecontext.pro @@ -14,7 +14,8 @@ SOURCES += \ pixmaptexture.cpp \ glyphnode.cpp \ renderingvisitor.cpp \ - ninepatchnode.cpp + ninepatchnode.cpp \ + softwarelayer.cpp HEADERS += \ context.h \ @@ -25,7 +26,8 @@ HEADERS += \ pixmaptexture.h \ glyphnode.h \ renderingvisitor.h \ - ninepatchnode.h + ninepatchnode.h \ + softwarelayer.h OTHER_FILES += softwarecontext.json diff --git a/softwarecontext/softwarelayer.cpp b/softwarecontext/softwarelayer.cpp new file mode 100644 index 0000000000..8913f2ec1f --- /dev/null +++ b/softwarecontext/softwarelayer.cpp @@ -0,0 +1,200 @@ +#include "softwarelayer.h" + +#include "context.h" + +SoftwareLayer::SoftwareLayer(QSGRenderContext *renderContext) + : m_item(0) + , m_shaderEffectNode(0) + , m_context(renderContext) + , m_renderer(0) + , m_device_pixel_ratio(1) + , m_live(true) + , m_grab(true) + , m_recursive(false) + , m_dirtyTexture(true) +{ + +} + +SoftwareLayer::~SoftwareLayer() +{ + invalidated(); +} + +int SoftwareLayer::textureId() const +{ + return 0; +} + +QSize SoftwareLayer::textureSize() const +{ + return m_pixmap.size(); +} + +bool SoftwareLayer::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool SoftwareLayer::hasMipmaps() const +{ + return false; +} + +void SoftwareLayer::bind() +{ +} + +bool SoftwareLayer::updateTexture() +{ + bool doGrab = (m_live || m_grab) && m_dirtyTexture; + if (doGrab) + grab(); + if (m_grab) + emit scheduledUpdateCompleted(); + m_grab = false; + return doGrab; +} + +void SoftwareLayer::setItem(QSGNode *item) +{ + if (item == m_item) + return; + m_item = item; + + if (m_live && !m_item) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setShaderEffectNode(QSGNode *node) +{ + m_shaderEffectNode = node; +} + +void SoftwareLayer::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + markDirtyTexture(); +} + +void SoftwareLayer::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + + if (m_live && m_size.isNull()) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + if (m_dirtyTexture) { + emit updateRequested(); + if (m_shaderEffectNode) + m_shaderEffectNode->markDirty(QSGNode::DirtyMaterial); + } +} + +QImage SoftwareLayer::toImage() const +{ + return m_pixmap.toImage(); +} + +void SoftwareLayer::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + + if (m_live && (!m_item || m_size.isNull())) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void SoftwareLayer::setFormat(GLenum) +{ +} + +void SoftwareLayer::setHasMipmaps(bool) +{ +} + +void SoftwareLayer::setDevicePixelRatio(qreal ratio) +{ + m_device_pixel_ratio = ratio; +} + +void SoftwareLayer::markDirtyTexture() +{ + m_dirtyTexture = true; + if (m_live || m_grab) { + emit updateRequested(); + if (m_shaderEffectNode) + m_shaderEffectNode->markDirty(QSGNode::DirtyMaterial); + } +} + +void SoftwareLayer::invalidated() +{ + delete m_renderer; + m_renderer = 0; +} + +void SoftwareLayer::grab() +{ + if (!m_item || m_size.isNull()) { + m_pixmap = QPixmap(); + m_dirtyTexture = false; + return; + } + QSGNode *root = m_item; + while (root->firstChild() && root->type() != QSGNode::RootNodeType) + root = root->firstChild(); + if (root->type() != QSGNode::RootNodeType) + return; + + if (!m_renderer) { + m_renderer = new SoftwareContext::PixmapRenderer(m_context); + connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture())); + } + m_renderer->setDevicePixelRatio(m_device_pixel_ratio); + m_renderer->setRootNode(static_cast(root)); + + if (m_pixmap.size() != m_size) + m_pixmap = QPixmap(m_size); + + // Render texture. + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update. + m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update. + + m_dirtyTexture = false; + + m_renderer->setDeviceRect(m_size); + m_renderer->setViewportRect(m_size); + QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); + m_renderer->setProjectionMatrixToRect(mirrored); + m_renderer->setClearColor(Qt::transparent); + + m_renderer->render(&m_pixmap); + + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. + + if (m_recursive) + markDirtyTexture(); // Continuously update if 'live' and 'recursive'. +} diff --git a/softwarecontext/softwarelayer.h b/softwarecontext/softwarelayer.h new file mode 100644 index 0000000000..b584841f87 --- /dev/null +++ b/softwarecontext/softwarelayer.h @@ -0,0 +1,67 @@ +#ifndef SOFTWARELAYER_H +#define SOFTWARELAYER_H + +#include +#include + +namespace SoftwareContext { +class PixmapRenderer; +} + +class SoftwareLayer : public QSGLayer +{ + Q_OBJECT +public: + SoftwareLayer(QSGRenderContext *renderContext); + ~SoftwareLayer(); + + const QPixmap &pixmap() const { return m_pixmap; } + + // QSGTexture interface +public: + virtual int textureId() const; + virtual QSize textureSize() const; + virtual bool hasAlphaChannel() const; + virtual bool hasMipmaps() const; + virtual void bind(); + + // QSGDynamicTexture interface +public: + virtual bool updateTexture(); + + // QSGLayer interface +public: + virtual void setItem(QSGNode *item); + virtual void setShaderEffectNode(QSGNode *node); + virtual void setRect(const QRectF &rect); + virtual void setSize(const QSize &size); + virtual void scheduleUpdate(); + virtual QImage toImage() const; + virtual void setLive(bool live); + virtual void setRecursive(bool recursive); + virtual void setFormat(GLenum); + virtual void setHasMipmaps(bool); + virtual void setDevicePixelRatio(qreal ratio); + +public slots: + virtual void markDirtyTexture(); + virtual void invalidated(); + +private: + void grab(); + + QSGNode *m_item; + QSGNode *m_shaderEffectNode; + QSGRenderContext *m_context; + SoftwareContext::PixmapRenderer *m_renderer; + QRectF m_rect; + QSize m_size; + QPixmap m_pixmap; + qreal m_device_pixel_ratio; + bool m_live; + bool m_grab; + bool m_recursive; + bool m_dirtyTexture; +}; + +#endif // SOFTWARELAYER_H -- cgit v1.2.3