From da54d17352dfc4070eb6fa105ce853e3d35490ef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Aug 2014 15:15:38 +0200 Subject: First implementation of images and rectangles Change-Id: Ia905d6dfe3d9922ef820085fedc5195be8ace1da Reviewed-by: Lars Knoll --- .gitignore | 5 + softwarecontext/context.cpp | 84 ++++++++- softwarecontext/context.h | 22 +++ softwarecontext/imagenode.cpp | 366 ++++++++++++++++++++++++++++++++++++ softwarecontext/imagenode.h | 35 ++++ softwarecontext/pixmaptexture.cpp | 32 ++++ softwarecontext/pixmaptexture.h | 24 +++ softwarecontext/pluginmain.cpp | 5 +- softwarecontext/pluginmain.h | 2 +- softwarecontext/rectanglenode.cpp | 50 +++++ softwarecontext/rectanglenode.h | 33 ++++ softwarecontext/renderloop.cpp | 230 ++++++++++++++++++++++ softwarecontext/renderloop.h | 53 ++++++ softwarecontext/softwarecontext.pro | 12 +- 14 files changed, 944 insertions(+), 9 deletions(-) create mode 100644 .gitignore create mode 100644 softwarecontext/imagenode.cpp create mode 100644 softwarecontext/imagenode.h create mode 100644 softwarecontext/pixmaptexture.cpp create mode 100644 softwarecontext/pixmaptexture.h create mode 100644 softwarecontext/rectanglenode.cpp create mode 100644 softwarecontext/rectanglenode.h create mode 100644 softwarecontext/renderloop.cpp create mode 100644 softwarecontext/renderloop.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..3230b75d6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +moc_* +Makefile +*.pro.user +*.so diff --git a/softwarecontext/context.cpp b/softwarecontext/context.cpp index e65b854af8..7de0aa5cde 100644 --- a/softwarecontext/context.cpp +++ b/softwarecontext/context.cpp @@ -41,6 +41,10 @@ #include "context.h" +#include "rectanglenode.h" +#include "imagenode.h" +#include "pixmaptexture.h" + #include #include @@ -62,8 +66,72 @@ static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); namespace SoftwareContext { +Renderer::Renderer(QSGRenderContext *context) + : QSGRenderer(context) +{ +} + +void Renderer::renderScene(GLuint fboId) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void Renderer::render() +{ + QWindow *currentWindow = static_cast(m_context)->currentWindow; + if (!backingStore) + backingStore.reset(new QBackingStore(currentWindow)); + + if (backingStore->size() != currentWindow->size()) + backingStore->resize(currentWindow->size()); + + const QRect rect(0, 0, currentWindow->width(), currentWindow->height()); + backingStore->beginPaint(rect); + + QPaintDevice *device = backingStore->paintDevice(); + QPainter painter(device); + painter.setRenderHint(QPainter::Antialiasing); + + painter.fillRect(rect, Qt::white); + renderNode(&painter, rootNode()); + + backingStore->endPaint(); + backingStore->flush(rect); +} + +void Renderer::renderNode(QPainter *painter, QSGNode *node) +{ + bool restore = false; + + if (node->type() == QSGNode::TransformNodeType) { + QSGTransformNode *tn = static_cast(node); + painter->save(); + restore = true; + painter->setTransform(tn->matrix().toTransform(), /*combine*/true); + } else if (node->type() == QSGNode::ClipNodeType) { + QSGClipNode *cn = static_cast(node); + painter->save(); + restore = true; + painter->setClipRect(cn->clipRect(), Qt::IntersectClip); + } + + node->paint(painter); + + for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) + renderNode(painter, child); + + if (restore) + painter->restore(); +} + RenderContext::RenderContext(QSGContext *ctx) : QSGRenderContext(ctx) + , currentWindow(0) { } Context::Context(QObject *parent) @@ -71,6 +139,16 @@ Context::Context(QObject *parent) { } +QSGRectangleNode *Context::createRectangleNode() +{ + return new RectangleNode(); +} + +QSGImageNode *Context::createImageNode() +{ + return new ImageNode(); +} + void RenderContext::initialize(QOpenGLContext *context) { QSGRenderContext::initialize(context); @@ -83,7 +161,7 @@ void RenderContext::invalidate() QSGTexture *RenderContext::createTexture(const QImage &image) const { - return QSGRenderContext::createTexture(image); + return new PixmapTexture(image); } QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const @@ -93,7 +171,7 @@ QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const QSGRenderer *RenderContext::createRenderer() { - return QSGRenderContext::createRenderer(); + return new Renderer(this); } @@ -103,6 +181,4 @@ void RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo) } - - } // namespace diff --git a/softwarecontext/context.h b/softwarecontext/context.h index 6ab405c890..75cf02d767 100644 --- a/softwarecontext/context.h +++ b/softwarecontext/context.h @@ -44,12 +44,29 @@ #define CONTEXT_H #include +#include #include #include +#include namespace SoftwareContext { +class Renderer : public QSGRenderer +{ +public: + Renderer(QSGRenderContext *context); + + virtual void renderScene(GLuint fboId = 0); + + virtual void render(); + +private: + void renderNode(QPainter *painter, QSGNode *node); + + QScopedPointer backingStore; +}; + class RenderContext : public QSGRenderContext { public: @@ -60,6 +77,8 @@ public: QSGTexture *createTexture(const QImage &image) const; QSGTexture *createTextureNoAtlas(const QImage &image) const; QSGRenderer *createRenderer(); + + QWindow *currentWindow; }; class Context : public QSGContext @@ -70,6 +89,9 @@ public: QSGRenderContext *createRenderContext() { return new RenderContext(this); } + virtual QSGRectangleNode *createRectangleNode(); + virtual QSGImageNode *createImageNode(); + private: }; diff --git a/softwarecontext/imagenode.cpp b/softwarecontext/imagenode.cpp new file mode 100644 index 0000000000..1dddbaef10 --- /dev/null +++ b/softwarecontext/imagenode.cpp @@ -0,0 +1,366 @@ +#include "imagenode.h" + +#include "pixmaptexture.h" +#include +#include + +// Helper from widgets/styles/qdrawutil.cpp + +typedef QVarLengthArray QPixmapFragmentsArray; + +struct QTileRules +{ + inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) + : horizontal(horizontalRule), vertical(verticalRule) {} + inline QTileRules(Qt::TileRule rule = Qt::StretchTile) + : horizontal(rule), vertical(rule) {} + Qt::TileRule horizontal; + Qt::TileRule vertical; +}; + +#ifndef Q_QDOC +// For internal use only. +namespace QDrawBorderPixmap +{ + enum DrawingHint + { + OpaqueTopLeft = 0x0001, + OpaqueTop = 0x0002, + OpaqueTopRight = 0x0004, + OpaqueLeft = 0x0008, + OpaqueCenter = 0x0010, + OpaqueRight = 0x0020, + OpaqueBottomLeft = 0x0040, + OpaqueBottom = 0x0080, + OpaqueBottomRight = 0x0100, + OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, + OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, + OpaqueFrame = OpaqueCorners | OpaqueEdges, + OpaqueAll = OpaqueCenter | OpaqueFrame + }; + + Q_DECLARE_FLAGS(DrawingHints, DrawingHint) +} +#endif + +static void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, + const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) +{ + QPainter::PixmapFragment d; + d.opacity = 1.0; + d.rotation = 0.0; + + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; + + // source center + const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); + const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); + const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; + const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; + const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; + const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; + // target center + const int targetCenterTop = targetRect.top() + targetMargins.top(); + const int targetCenterLeft = targetRect.left() + targetMargins.left(); + const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; + const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; + const int targetCenterWidth = targetCenterRight - targetCenterLeft; + const int targetCenterHeight = targetCenterBottom - targetCenterTop; + + QVarLengthArray xTarget; // x-coordinates of target rectangles + QVarLengthArray yTarget; // y-coordinates of target rectangles + + int columns = 3; + int rows = 3; + if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) + columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); + if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) + rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); + + xTarget.resize(columns + 1); + yTarget.resize(rows + 1); + + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + if (painter->paintEngine()->type() != QPaintEngine::OpenGL + && painter->paintEngine()->type() != QPaintEngine::OpenGL2 + && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { + painter->setRenderHint(QPainter::Antialiasing, false); + } + + xTarget[0] = targetRect.left(); + xTarget[1] = targetCenterLeft; + xTarget[columns - 1] = targetCenterRight; + xTarget[columns] = targetRect.left() + targetRect.width(); + + yTarget[0] = targetRect.top(); + yTarget[1] = targetCenterTop; + yTarget[rows - 1] = targetCenterBottom; + yTarget[rows] = targetRect.top() + targetRect.height(); + + qreal dx = targetCenterWidth; + qreal dy = targetCenterHeight; + + switch (rules.horizontal) { + case Qt::StretchTile: + dx = targetCenterWidth; + break; + case Qt::RepeatTile: + dx = sourceCenterWidth; + break; + case Qt::RoundTile: + dx = targetCenterWidth / qreal(columns - 2); + break; + } + + for (int i = 2; i < columns - 1; ++i) + xTarget[i] = xTarget[i - 1] + dx; + + switch (rules.vertical) { + case Qt::StretchTile: + dy = targetCenterHeight; + break; + case Qt::RepeatTile: + dy = sourceCenterHeight; + break; + case Qt::RoundTile: + dy = targetCenterHeight / qreal(rows - 2); + break; + } + + for (int i = 2; i < rows - 1; ++i) + yTarget[i] = yTarget[i - 1] + dy; + + // corners + if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.left(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.right(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopRight) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.left(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.right(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomRight) + opaqueData.append(d); + else + translucentData.append(d); + } + + // horizontal edges + if (targetCenterWidth > 0 && sourceCenterWidth > 0) { + if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceRect.top(); + d.width = sourceCenterWidth; + d.height = sourceMargins.top(); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterBottom; + d.width = sourceCenterWidth; + d.height = sourceMargins.bottom(); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + } + + // vertical edges + if (targetCenterHeight > 0 && sourceCenterHeight > 0) { + if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.left(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.right(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + } + + // center + if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterTop; + d.width = sourceCenterWidth; + d.height = sourceCenterHeight; + d.scaleX = dx / d.width; + d.scaleY = dy / d.height; + + qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; + qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; + + for (int j = 1; j < rows - 1; ++j) { + d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = repeatWidth; + } + if (rules.vertical == Qt::RepeatTile) { + for (int i = 1; i < columns - 1; ++i) + data[data.size() - i].height = repeatHeight; + } + } + + if (opaqueData.size()) + painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); + if (translucentData.size()) + painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); + + if (oldAA) + painter->setRenderHint(QPainter::Antialiasing, true); +} + +ImageNode::ImageNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + + +void ImageNode::setTargetRect(const QRectF &rect) +{ + m_targetRect = rect; +} + +void ImageNode::setInnerTargetRect(const QRectF &rect) +{ + m_innerTargetRect = rect; +} + +void ImageNode::setInnerSourceRect(const QRectF &rect) +{ + m_innerSourceRect = rect; +} + +void ImageNode::setSubSourceRect(const QRectF &rect) +{ + m_subSourceRect = 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(); +} + +void ImageNode::setMirror(bool mirror) +{ +} + +void ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ +} + +void ImageNode::setFiltering(QSGTexture::Filtering filtering) +{ +} + +void ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ +} + +void ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ +} + +void ImageNode::update() +{ +} + +void ImageNode::paint(QPainter *painter) +{ + painter->drawPixmap(m_targetRect, m_pixmap, m_innerSourceRect); +} diff --git a/softwarecontext/imagenode.h b/softwarecontext/imagenode.h new file mode 100644 index 0000000000..956e4f75d8 --- /dev/null +++ b/softwarecontext/imagenode.h @@ -0,0 +1,35 @@ +#ifndef IMAGENODE_H +#define IMAGENODE_H + +#include +#include + +class ImageNode : public QSGImageNode +{ +public: + ImageNode(); + + virtual void setTargetRect(const QRectF &rect); + virtual void setInnerTargetRect(const QRectF &rect); + virtual void setInnerSourceRect(const QRectF &rect); + virtual void setSubSourceRect(const QRectF &rect); + virtual void setTexture(QSGTexture *texture); + virtual void setMirror(bool mirror); + virtual void setMipmapFiltering(QSGTexture::Filtering filtering); + virtual void setFiltering(QSGTexture::Filtering filtering); + virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode); + virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); + virtual void update(); + + virtual void paint(QPainter *painter); + +private: + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + QPixmap m_pixmap; +}; + +#endif // IMAGENODE_H diff --git a/softwarecontext/pixmaptexture.cpp b/softwarecontext/pixmaptexture.cpp new file mode 100644 index 0000000000..48befd3e7c --- /dev/null +++ b/softwarecontext/pixmaptexture.cpp @@ -0,0 +1,32 @@ +#include "pixmaptexture.h" + +PixmapTexture::PixmapTexture(const QImage &image) + : m_pixmap(QPixmap::fromImage(image)) +{ +} + + +int PixmapTexture::textureId() const +{ + return 0; +} + +QSize PixmapTexture::textureSize() const +{ + return m_pixmap.size(); +} + +bool PixmapTexture::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool PixmapTexture::hasMipmaps() const +{ + return false; +} + +void PixmapTexture::bind() +{ + Q_UNREACHABLE(); +} diff --git a/softwarecontext/pixmaptexture.h b/softwarecontext/pixmaptexture.h new file mode 100644 index 0000000000..314005f82a --- /dev/null +++ b/softwarecontext/pixmaptexture.h @@ -0,0 +1,24 @@ +#ifndef PIXMAPTEXTURE_H +#define PIXMAPTEXTURE_H + +#include + +class PixmapTexture : public QSGTexture +{ + Q_OBJECT +public: + PixmapTexture(const QImage &image); + + virtual int textureId() const; + virtual QSize textureSize() const; + virtual bool hasAlphaChannel() const; + virtual bool hasMipmaps() const; + virtual void bind(); + + QPixmap pixmap() const { return m_pixmap; } + +private: + QPixmap m_pixmap; +}; + +#endif // PIXMAPTEXTURE_H diff --git a/softwarecontext/pluginmain.cpp b/softwarecontext/pluginmain.cpp index 192e6225bc..4fcfef0859 100644 --- a/softwarecontext/pluginmain.cpp +++ b/softwarecontext/pluginmain.cpp @@ -42,6 +42,7 @@ #include "pluginmain.h" #include "context.h" +#include "renderloop.h" ContextPlugin::ContextPlugin(QObject *parent) : QSGContextPlugin(parent) @@ -60,9 +61,9 @@ QSGContext *ContextPlugin::create(const QString &) const return instance; } -QQuickTextureFactory *ContextPlugin::createTextureFactoryFromImage(const QImage &image) +QSGRenderLoop *ContextPlugin::createWindowManager() { - return 0; + return new RenderLoop(); } SoftwareContext::Context *ContextPlugin::instance = 0; diff --git a/softwarecontext/pluginmain.h b/softwarecontext/pluginmain.h index f8ebf3cb9b..940f1de595 100644 --- a/softwarecontext/pluginmain.h +++ b/softwarecontext/pluginmain.h @@ -60,7 +60,7 @@ public: QStringList keys() const; QSGContext *create(const QString &key) const; - QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); + QSGRenderLoop *createWindowManager(); static SoftwareContext::Context *instance; }; diff --git a/softwarecontext/rectanglenode.cpp b/softwarecontext/rectanglenode.cpp new file mode 100644 index 0000000000..6dde7058c2 --- /dev/null +++ b/softwarecontext/rectanglenode.cpp @@ -0,0 +1,50 @@ +#include "rectanglenode.h" + +RectangleNode::RectangleNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void RectangleNode::setRect(const QRectF &rect) +{ + m_rect = rect; +} + +void RectangleNode::setColor(const QColor &color) +{ + m_brush = color; +} + +void RectangleNode::setPenColor(const QColor &color) +{ + m_pen.setColor(color); +} + +void RectangleNode::setPenWidth(qreal width) +{ + m_pen.setWidthF(width); +} + +void RectangleNode::setGradientStops(const QGradientStops &stops) +{ +} + +void RectangleNode::setRadius(qreal radius) +{ +} + +void RectangleNode::setAligned(bool aligned) +{ +} + +void RectangleNode::update() +{ +} + +void RectangleNode::paint(QPainter *painter) +{ + painter->setPen(m_pen); + painter->setBrush(m_brush); + painter->drawRect(m_rect); +} diff --git a/softwarecontext/rectanglenode.h b/softwarecontext/rectanglenode.h new file mode 100644 index 0000000000..1b5f1af0a0 --- /dev/null +++ b/softwarecontext/rectanglenode.h @@ -0,0 +1,33 @@ +#ifndef RECTANGLENODE_H +#define RECTANGLENODE_H + +#include + +#include +#include + +class RectangleNode : public QSGRectangleNode +{ +public: + RectangleNode(); + + virtual void setRect(const QRectF &rect); + virtual void setColor(const QColor &color); + virtual void setPenColor(const QColor &color); + virtual void setPenWidth(qreal width); + virtual void setGradientStops(const QGradientStops &stops); + virtual void setRadius(qreal radius); + virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } + virtual void setAligned(bool aligned); + + virtual void update(); + + virtual void paint(QPainter *); + +private: + QPen m_pen; + QBrush m_brush; + QRectF m_rect; +}; + +#endif // RECTANGLENODE_H diff --git a/softwarecontext/renderloop.cpp b/softwarecontext/renderloop.cpp new file mode 100644 index 0000000000..fd5137e945 --- /dev/null +++ b/softwarecontext/renderloop.cpp @@ -0,0 +1,230 @@ +#include "renderloop.h" + +#include "context.h" +#include +#include +#include + +// Used for very high-level info about the renderering and gl context +// Includes GL_VERSION, type of render loop, atlas size, etc. +Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.info") + +// Used to debug the renderloop logic. Primarily useful for platform integrators +// and when investigating the render loop logic. +Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop") + + +// GLSL shader compilation +Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation") + +// polish, animations, sync, render and swap in the render loop +Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop") + +// Texture uploads and swizzling +Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture") + +// Glyph preparation (only for distance fields atm) +Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph") + +// Timing inside the renderer base class +Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer") + +RenderLoop::RenderLoop() + : eventPending(false) +{ + sg = QSGContext::createDefaultContext(); + rc = sg->createRenderContext(); +} + +RenderLoop::~RenderLoop() +{ + delete rc; + delete sg; +} + +void RenderLoop::show(QQuickWindow *window) +{ + WindowData data; + data.updatePending = false; + data.grabOnly = false; + m_windows[window] = data; + + maybeUpdate(window); +} + +void RenderLoop::hide(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows.remove(window); + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->fireAboutToStop(); + cd->cleanupNodesOnShutdown(); + + if (m_windows.size() == 0) { + if (!cd->persistentSceneGraph) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + } +} + +void RenderLoop::windowDestroyed(QQuickWindow *window) +{ + hide(window); + if (m_windows.size() == 0) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +void RenderLoop::renderWindow(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) + return; + + WindowData &data = const_cast(m_windows[window]); + + // ### create QPainter and set up pointer to current window/painter + static_cast(cd->context)->currentWindow = window; + + bool alsoSwap = data.updatePending; + data.updatePending = false; + + if (!data.grabOnly) { + cd->flushDelayedTouchEvent(); + // Event delivery/processing triggered the window to be deleted or stop rendering. + if (!m_windows.contains(window)) + return; + } + QElapsedTimer renderTimer; + qint64 renderTime = 0, syncTime = 0, polishTime = 0; + bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; + if (profileFrames) + renderTimer.start(); + + cd->polishItems(); + + if (profileFrames) { + polishTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishFrame, (polishTime)); + } + + emit window->afterAnimating(); + + cd->syncSceneGraph(); + + if (profileFrames) + syncTime = renderTimer.nsecsElapsed(); + + cd->renderSceneGraph(window->size()); + + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + + if (data.grabOnly) { + // #### grabContent = qt_gl_read_framebuffer(window->size() * window->devicePixelRatio(), false, false); + data.grabOnly = false; + } + + if (alsoSwap && window->isVisible()) { +// ### gl->swapBuffers(window); + cd->fireFrameSwapped(); + } + + qint64 swapTime = 0; + if (profileFrames) + swapTime = renderTimer.nsecsElapsed(); + + if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) { + static QTime lastFrameTime = QTime::currentTime(); + qCDebug(QSG_LOG_TIME_RENDERLOOP, + "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + int(swapTime / 1000000), + int(polishTime / 1000000), + int((syncTime - polishTime) / 1000000), + int((renderTime - syncTime) / 1000000), + int((swapTime - renderTime) / 10000000), + int(lastFrameTime.msecsTo(QTime::currentTime()))); + lastFrameTime = QTime::currentTime(); + } + + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, ( + syncTime - polishTime, + renderTime - syncTime, + swapTime - renderTime)); + + // Might have been set during syncSceneGraph() + if (data.updatePending) + maybeUpdate(window); +} + +void RenderLoop::exposureChanged(QQuickWindow *window) +{ + if (window->isExposed()) { + m_windows[window].updatePending = true; + renderWindow(window); + } +} + +QImage RenderLoop::grab(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return QImage(); + + m_windows[window].grabOnly = true; + + renderWindow(window); + + QImage grabbed = grabContent; + grabContent = QImage(); + return grabbed; +} + + + +void RenderLoop::maybeUpdate(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows[window].updatePending = true; + + if (!eventPending) { + const int exhaust_delay = 5; + m_update_timer = startTimer(exhaust_delay, Qt::PreciseTimer); + eventPending = true; + } +} + +QSurface::SurfaceType RenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + + + +QSGContext *RenderLoop::sceneGraphContext() const +{ + return sg; +} + + +bool RenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + eventPending = false; + killTimer(m_update_timer); + m_update_timer = 0; + for (QHash::const_iterator it = m_windows.constBegin(); + it != m_windows.constEnd(); ++it) { + const WindowData &data = it.value(); + if (data.updatePending) + renderWindow(it.key()); + } + return true; + } + return QObject::event(e); +} diff --git a/softwarecontext/renderloop.h b/softwarecontext/renderloop.h new file mode 100644 index 0000000000..8475a0b65b --- /dev/null +++ b/softwarecontext/renderloop.h @@ -0,0 +1,53 @@ +#ifndef RENDERLOOP_H +#define RENDERLOOP_H + +#include + +class RenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + RenderLoop(); + ~RenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + + void renderWindow(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + QImage grab(QQuickWindow *window); + + void maybeUpdate(QQuickWindow *window); + void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. + + void releaseResources(QQuickWindow *) { } + + virtual QSurface::SurfaceType windowSurfaceType() const; + + QAnimationDriver *animationDriver() const { return 0; } + + QSGContext *sceneGraphContext() const; + QSGRenderContext *createRenderContext(QSGContext *) const { return rc; } + + bool event(QEvent *); + + struct WindowData { + bool updatePending : 1; + bool grabOnly : 1; + }; + + QHash m_windows; + + QSGContext *sg; + QSGRenderContext *rc; + + QImage grabContent; + int m_update_timer; + + bool eventPending; + +}; + +#endif // RENDERLOOP_H diff --git a/softwarecontext/softwarecontext.pro b/softwarecontext/softwarecontext.pro index 1d1aea4df1..0c6486fc8b 100644 --- a/softwarecontext/softwarecontext.pro +++ b/softwarecontext/softwarecontext.pro @@ -7,11 +7,19 @@ QT += gui-private core-private quick-private qml-private SOURCES += \ context.cpp \ - pluginmain.cpp + pluginmain.cpp \ + renderloop.cpp \ + rectanglenode.cpp \ + imagenode.cpp \ + pixmaptexture.cpp HEADERS += \ context.h \ - pluginmain.h + pluginmain.h \ + renderloop.h \ + rectanglenode.h \ + imagenode.h \ + pixmaptexture.h OTHER_FILES += softwarecontext.json -- cgit v1.2.3