diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
commit | 13e88fe2b9b1680cb161a249289c3ba998f08c0c (patch) | |
tree | 496a9d88c69b441e8c88aa0416b327faca3a1532 /src/quick/items/context2d | |
parent | a2dad3ddee9c4bf274a7c6469342e4104605ceeb (diff) | |
parent | 470ba767663e4ad9d3183fb56ee89361354dfefb (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
src/quick/items/qquickitem.cpp
src/quick/items/qquicktext.cpp
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
Change-Id: I0bc5786098193c2c40b6fd8905de75d90f6ed0cf
Diffstat (limited to 'src/quick/items/context2d')
-rw-r--r-- | src/quick/items/context2d/qquickcanvascontext_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcanvasitem.cpp | 64 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcanvasitem_p.h | 8 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2d.cpp | 60 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2d_p.h | 6 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp | 34 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2dtexture.cpp | 193 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcontext2dtexture_p.h | 70 |
9 files changed, 257 insertions, 182 deletions
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h index 559f41e546..2d8fbb5f85 100644 --- a/src/quick/items/context2d/qquickcanvascontext_p.h +++ b/src/quick/items/context2d/qquickcanvascontext_p.h @@ -71,8 +71,6 @@ public: virtual void setV8Engine(QV8Engine *engine) = 0; virtual QV4::ReturnedValue v4value() const = 0; - virtual QSGDynamicTexture *texture() const = 0; - virtual QImage toImage(const QRectF& bounds) = 0; Q_SIGNALS: diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index ee1b5f316c..be9c592228 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -44,8 +44,10 @@ #include <private/qquickitem_p.h> #include <private/qquickcanvascontext_p.h> #include <private/qquickcontext2d_p.h> +#include <private/qquickcontext2dtexture_p.h> #include <qsgsimpletexturenode.h> #include <QtQuick/private/qquickpixmapcache_p.h> +#include <QtGui/QGuiApplication> #include <qqmlinfo.h> #include <private/qqmlengine_p.h> @@ -58,19 +60,15 @@ QT_BEGIN_NAMESPACE -QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image, QQuickWindow *window) +QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image) : m_pixmap(0) , m_image(image) - , m_texture(0) - , m_window(window) { } -QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window) +QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap) : m_pixmap(pixmap) - , m_texture(0) - , m_window(window) { } @@ -78,8 +76,6 @@ QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *windo QQuickCanvasPixmap::~QQuickCanvasPixmap() { delete m_pixmap; - if (m_texture) - m_texture->deleteLater(); } qreal QQuickCanvasPixmap::width() const @@ -105,18 +101,6 @@ bool QQuickCanvasPixmap::isValid() const return !m_image.isNull(); } -QSGTexture *QQuickCanvasPixmap::texture() -{ - if (!m_texture) { - if (m_pixmap) { - Q_ASSERT(m_pixmap->textureFactory()); - m_texture = m_pixmap->textureFactory()->createTexture(m_window); - } else { - m_texture = m_window->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas); - } - } - return m_texture; -} QImage QQuickCanvasPixmap::image() { if (m_image.isNull() && m_pixmap) @@ -194,7 +178,7 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() , hasCanvasWindow(false) , available(false) , renderTarget(QQuickCanvasItem::Image) - , renderStrategy(QQuickCanvasItem::Cooperative) + , renderStrategy(QQuickCanvasItem::Immediate) { implicitAntialiasing = true; } @@ -246,10 +230,14 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() The Canvas.FramebufferObject render target utilizes OpenGL hardware acceleration rather than rendering into system memory, which in many cases - results in faster rendering. + results in faster rendering. Canvas.FramebufferObject relies on the + OpenGL extensions \c GL_EXT_framebuffer_multisample and + \c GL_EXT_framebuffer_blit for antialiasing. It will also use more + graphics memory when rendering strategy is anything other than + Canvas.Cooperative. The default render target is Canvas.Image and the default renderStrategy is - Canvas.Cooperative. + Canvas.Immediate. \section1 Tiled Canvas The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize @@ -489,7 +477,7 @@ void QQuickCanvasItem::setCanvasWindow(const QRectF& rect) context will choose appropriate options and Canvas will signal the change to the properties. - The default render target is \c Canvas.FramebufferObject. + The default render target is \c Canvas.Image. */ QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const { @@ -531,7 +519,7 @@ void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target) the GUI thread. Selecting \c Canvas.Cooperative, does not guarantee rendering will occur on a thread separate from the GUI thread. - The default value is \c Canvas.Cooperative. + The default value is \c Canvas.Immediate. \sa renderTarget */ @@ -689,6 +677,15 @@ void QQuickCanvasItem::updatePolish() } } +class QQuickCanvasNode : public QSGSimpleTextureNode +{ +public: + ~QQuickCanvasNode() + { + delete texture(); + } +}; + QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { Q_D(QQuickCanvasItem); @@ -698,9 +695,9 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData return 0; } - QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode); + QQuickCanvasNode *node = static_cast<QQuickCanvasNode*>(oldNode); if (!node) - node = new QSGSimpleTextureNode; + node = new QQuickCanvasNode(); if (d->smooth) node->setFiltering(QSGTexture::Linear); @@ -712,8 +709,15 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData d->context->flush(); } - node->setTexture(d->context->texture()); - node->markDirty(QSGNode::DirtyMaterial); + QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context); + QQuickContext2DTexture *factory = ctx->texture(); + QSGTexture *texture = factory->textureForNextFrame(node->texture()); + if (!texture) { + delete node; + return 0; + } + + node->setTexture(texture); node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size())); return node; } @@ -916,7 +920,7 @@ void QQuickCanvasItem::loadImage(const QUrl& url) if (!d->pixmaps.contains(fullPathUrl)) { QQuickPixmap* pix = new QQuickPixmap(); QQmlRefPointer<QQuickCanvasPixmap> canvasPix; - canvasPix.take(new QQuickCanvasPixmap(pix, d->window)); + canvasPix.take(new QQuickCanvasPixmap(pix)); d->pixmaps.insert(fullPathUrl, canvasPix); pix->load(qmlEngine(this) diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index 2ec36cfe73..3baf68d418 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -52,17 +52,15 @@ QT_BEGIN_NAMESPACE class QQuickCanvasContext; class QQuickCanvasItemPrivate; -class QSGTexture; class QQuickPixmap; class QQuickCanvasPixmap : public QQmlRefCount { public: - QQuickCanvasPixmap(const QImage& image, QQuickWindow *window); - QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window); + QQuickCanvasPixmap(const QImage& image); + QQuickCanvasPixmap(QQuickPixmap *pixmap); ~QQuickCanvasPixmap(); - QSGTexture *texture(); QImage image(); qreal width() const; @@ -73,8 +71,6 @@ public: private: QQuickPixmap *m_pixmap; QImage m_image; - QSGTexture *m_texture; - QQuickWindow *m_window; }; class QQuickCanvasItem : public QQuickItem diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 2a9e0f2ac2..b6eb2db33d 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -484,7 +484,7 @@ public: QQuickJSContext2D(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QQuickContext2D* context; @@ -653,7 +653,7 @@ public: , patternRepeatX(false) , patternRepeatY(false) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QBrush brush; bool patternRepeatX:1; @@ -870,7 +870,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object QQuickJSContext2DPixelData(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); flags &= ~SimpleArray; } @@ -893,7 +893,7 @@ struct QQuickJSContext2DImageData : public QV4::Object QQuickJSContext2DImageData(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); pixelData = QV4::Primitive::undefinedValue(); QV4::Scope scope(engine); @@ -2985,14 +2985,14 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) { QImage img = canvas->toImage(); if (!img.isNull()) - pixmap.take(new QQuickCanvasPixmap(img, canvas->window())); + pixmap.take(new QQuickCanvasPixmap(img)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } } else if (QV4::Referenced<QQuickJSContext2DImageData> imageData = arg->asRef<QQuickJSContext2DImageData>()) { QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->pixelData.as<QQuickJSContext2DPixelData>()); if (pix && !pix->image.isNull()) { - pixmap.take(new QQuickCanvasPixmap(pix->image, r->context->canvas()->window())); + pixmap.take(new QQuickCanvasPixmap(pix->image)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } @@ -3165,7 +3165,7 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(QV4::Managed *m, uint QV4::Scope scope(v4); QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>()); if (!m) - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) { if (hasProperty) @@ -3200,7 +3200,7 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>()); if (!r) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return; } @@ -4084,6 +4084,15 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args QQuickWindow *window = canvasItem->window(); m_renderStrategy = canvasItem->renderStrategy(); +#ifdef Q_OS_WIN + if (m_renderTarget == QQuickCanvasItem::FramebufferObject + && (m_renderStrategy != QQuickCanvasItem::Cooperative)) { + // On windows a context needs to be unbound set up sharing, so + // for simplicity we disallow FBO + !coop here. + m_renderTarget = QQuickCanvasItem::Image; + } +#endif + switch (m_renderTarget) { case QQuickCanvasItem::Image: m_texture = new QQuickContext2DImageTexture; @@ -4099,6 +4108,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_texture->setCanvasSize(canvasItem->canvasSize().toSize()); m_texture->setSmooth(canvasItem->smooth()); m_texture->setAntialiasing(canvasItem->antialiasing()); + m_texture->setOnCustomThread(m_renderStrategy == QQuickCanvasItem::Threaded); m_thread = QThread::currentThread(); QThread *renderThread = m_thread; @@ -4129,28 +4139,31 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing) { - QMetaObject::invokeMethod(m_texture - , "canvasChanged" - , Qt::AutoConnection - , Q_ARG(QSize, canvasSize) - , Q_ARG(QSize, tileSize) - , Q_ARG(QRect, canvasWindow) - , Q_ARG(QRect, dirtyRect) - , Q_ARG(bool, smooth) - , Q_ARG(bool, antialiasing)); + if (m_texture->thread() == QThread::currentThread()) { + m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth, antialiasing); + } else { + QEvent *e = new QQuickContext2DTexture::CanvasChangeEvent(canvasSize, + tileSize, + canvasWindow, + dirtyRect, + smooth, + antialiasing); + QCoreApplication::postEvent(m_texture, e); + } } void QQuickContext2D::flush() { - if (m_buffer) - QMetaObject::invokeMethod(m_texture, - "paint", - Qt::AutoConnection, - Q_ARG(QQuickContext2DCommandBuffer*, m_buffer)); + if (m_buffer) { + if (m_texture->thread() == QThread::currentThread()) + m_texture->paint(m_buffer); + else + QCoreApplication::postEvent(m_texture, new QQuickContext2DTexture::PaintEvent(m_buffer)); + } m_buffer = new QQuickContext2DCommandBuffer(); } -QSGDynamicTexture *QQuickContext2D::texture() const +QQuickContext2DTexture *QQuickContext2D::texture() const { return m_texture; } @@ -4164,6 +4177,7 @@ QImage QQuickContext2D::toImage(const QRectF& bounds) qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode"; return QImage(); } else { + QCoreApplication::postEvent(m_texture, new QEvent(QEvent::Type(QEvent::User + 10))); QMetaObject::invokeMethod(m_texture, "grabImage", Qt::BlockingQueuedConnection, diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 4bef50d9cd..6399da3dee 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -74,6 +74,8 @@ class QOpenGLContext; class QQuickContext2D : public QQuickCanvasContext { + Q_OBJECT + public: Q_DISABLE_COPY(QQuickContext2D) @@ -171,8 +173,8 @@ public: void prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing); void flush(); void sync(); - QThread *thread() const {return m_thread;} - QSGDynamicTexture *texture() const; + QThread *thread() const { return m_thread; } + QQuickContext2DTexture *texture() const; QImage toImage(const QRectF& bounds); QV4::ReturnedValue v4value() const; diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index d2f4e3317d..06a0713365 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -420,38 +420,8 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s Q_ASSERT(!pix.isNull()); const bool hasShadow = HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - if (p->paintEngine()->type() != QPaintEngine::OpenGL2 || hasShadow){ - //TODO: generate shadow blur with shaders - qt_drawImage(p, state, pix->image(), sr, dr, hasShadow); - } else if (pix->texture()){ - QSGTexture *tex = pix->texture(); - QSGDynamicTexture *dynamicTexture = qobject_cast<QSGDynamicTexture *>(tex); - if (dynamicTexture) - dynamicTexture->updateTexture(); - - if (tex->textureId()) { - - if (sr.width() < 0) - sr.setWidth(tex->textureSize().width()); - if (sr.height() < 0) - sr.setHeight(tex->textureSize().height()); - - if (dr.width() < 0) - dr.setWidth(sr.width()); - if (dr.height() < 0) - dr.setHeight(sr.height()); - - qreal srBottom = sr.bottom(); - sr.setBottom(sr.top()); - sr.setTop(srBottom); - - tex->bind(); - if (p->paintEngine()->type() == QPaintEngine::OpenGL2) { - QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx *>(p->paintEngine()); - engine->drawTexture(dr, tex->textureId(), tex->textureSize(), sr); - } - } - } + //TODO: generate shadow blur with shaders + qt_drawImage(p, state, pix->image(), sr, dr, hasShadow); break; } case QQuickContext2D::GetImageData: diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h index 29cdc73708..9e79333a0c 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -58,8 +58,6 @@ public: void reset(); void clear(); - void lockQueue() { queueLock.lock(); } - void unlockQueue() { queueLock.unlock(); } inline int size() {return commands.size();} inline bool isEmpty() const {return commands.isEmpty(); } inline bool hasNext() const {return cmdIdx < commands.size(); } diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 8dc9978089..8dd48b4988 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -50,6 +50,7 @@ #include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObjectFormat> #include <QtCore/QThread> +#include <QtGui/QGuiApplication> QT_BEGIN_NAMESPACE @@ -90,7 +91,6 @@ struct GLAcquireContext { QQuickContext2DTexture::QQuickContext2DTexture() : m_context(0) , m_item(0) - , m_dirtyCanvas(false) , m_canvasWindowChanged(false) , m_dirtyTexture(false) , m_smooth(true) @@ -105,23 +105,20 @@ QQuickContext2DTexture::~QQuickContext2DTexture() clearTiles(); } -QSize QQuickContext2DTexture::textureSize() const -{ - return m_canvasWindow.size(); -} - void QQuickContext2DTexture::markDirtyTexture() { + if (m_onCustomThread) + m_mutex.lock(); m_dirtyTexture = true; - updateTexture(); emit textureChanged(); + if (m_onCustomThread) + m_mutex.unlock(); } bool QQuickContext2DTexture::setCanvasSize(const QSize &size) { if (m_canvasSize != size) { m_canvasSize = size; - m_dirtyCanvas = true; return true; } return false; @@ -131,7 +128,6 @@ bool QQuickContext2DTexture::setTileSize(const QSize &size) { if (m_tileSize != size) { m_tileSize = size; - m_dirtyCanvas = true; return true; } return false; @@ -195,7 +191,6 @@ void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& if (canvasSize == canvasWindow.size()) { m_tiledCanvas = false; - m_dirtyCanvas = false; } else { m_tiledCanvas = true; } @@ -309,7 +304,6 @@ QRect QQuickContext2DTexture::createTiles(const QRect& window) m_tiles.clear(); if (window.isEmpty()) { - m_dirtyCanvas = false; return QRect(); } @@ -351,7 +345,6 @@ QRect QQuickContext2DTexture::createTiles(const QRect& window) qDeleteAll(oldTiles); - m_dirtyCanvas = false; return r; } @@ -366,6 +359,20 @@ QSize QQuickContext2DTexture::adjustedTileSize(const QSize &ts) return ts; } +bool QQuickContext2DTexture::event(QEvent *e) +{ + if ((int) e->type() == QEvent::User + 1) { + PaintEvent *pe = static_cast<PaintEvent *>(e); + paint(pe->buffer); + return true; + } else if ((int) e->type() == QEvent::User + 2) { + CanvasChangeEvent *ce = static_cast<CanvasChangeEvent *>(e); + canvasChanged(ce->canvasSize, ce->tileSize, ce->canvasWindow, ce->dirtyRect, ce->smooth, ce->antialiasing); + return true; + } + return QObject::event(e); +} + static inline QSize npotAdjustedSize(const QSize &size) { static bool checked = false; @@ -391,6 +398,9 @@ QQuickContext2DFBOTexture::QQuickContext2DFBOTexture() , m_multisampledFbo(0) , m_paint_device(0) { + m_displayTextures[0] = 0; + m_displayTextures[1] = 0; + m_displayTexture = -1; } QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() @@ -403,17 +413,52 @@ QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() delete m_fbo; delete m_multisampledFbo; delete m_paint_device; + + glDeleteTextures(2, m_displayTextures); } -QSize QQuickContext2DFBOTexture::adjustedTileSize(const QSize &ts) +QSGTexture *QQuickContext2DFBOTexture::textureForNextFrame(QSGTexture *lastTexture) { - return npotAdjustedSize(ts); + QSGPlainTexture *texture = static_cast<QSGPlainTexture *>(lastTexture); + + if (m_onCustomThread) + m_mutex.lock(); + + if (m_fbo) { + if (!texture) { + texture = new QSGPlainTexture(); + texture->setHasMipmaps(false); + texture->setHasAlphaChannel(true); + texture->setOwnsTexture(false); + m_dirtyTexture = true; + } + + if (m_dirtyTexture) { + if (!m_context->glContext()) { + // on a rendering thread, use the fbo directly... + texture->setTextureId(m_fbo->texture()); + } else { + // on GUI or custom thread, use display textures... + m_displayTexture = m_displayTexture == 0 ? 1 : 0; + texture->setTextureId(m_displayTextures[m_displayTexture]); + } + texture->setTextureSize(m_fbo->size()); + m_dirtyTexture = false; + } + + } + + if (m_onCustomThread) { + m_condition.wakeOne(); + m_mutex.unlock(); + } + + return texture; } -void QQuickContext2DFBOTexture::bind() +QSize QQuickContext2DFBOTexture::adjustedTileSize(const QSize &ts) { - glBindTexture(GL_TEXTURE_2D, textureId()); - updateBindOptions(); + return npotAdjustedSize(ts); } QRectF QQuickContext2DFBOTexture::normalizedTextureSubRect() const @@ -424,20 +469,6 @@ QRectF QQuickContext2DFBOTexture::normalizedTextureSubRect() const , qreal(m_canvasWindow.height()) / m_fboSize.height()); } - -int QQuickContext2DFBOTexture::textureId() const -{ - return m_fbo? m_fbo->texture() : 0; -} - - -bool QQuickContext2DFBOTexture::updateTexture() -{ - bool textureUpdated = m_dirtyTexture; - m_dirtyTexture = false; - return textureUpdated; -} - QQuickContext2DTile* QQuickContext2DFBOTexture::createTile() const { return new QQuickContext2DFBOTile(); @@ -461,7 +492,6 @@ bool QQuickContext2DFBOTexture::doMultisampling() const void QQuickContext2DFBOTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); - if (!m_fbo) { m_context->setGrabbedImage(QImage()); return; @@ -531,7 +561,6 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() } else { QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); } } @@ -541,7 +570,6 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() else m_fbo->bind(); - if (!m_paint_device) { QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size()); gl_device->setPaintFlipped(true); @@ -557,25 +585,47 @@ void QQuickContext2DFBOTexture::endPainting() QQuickContext2DTexture::endPainting(); if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); + + if (m_context->glContext()) { + /* When rendering happens on the render thread, the fbo's texture is + * used directly for display. If we are on the GUI thread or a + * dedicated Canvas render thread, we need to decouple the FBO from + * the texture we are displaying in the SG rendering thread to avoid + * stalls and read/write issues in the GL pipeline as the FBO's texture + * could then potentially be used in different threads. + * + * We could have gotten away with only one display texture, but this + * would have implied that beginPainting would have to wait for SG + * to release that texture. + */ + + if (m_onCustomThread) + m_mutex.lock(); + + if (m_displayTextures[0] == 0) { + m_displayTexture = 1; + glGenTextures(2, m_displayTextures); + } + + m_fbo->bind(); + GLuint target = m_displayTexture == 0 ? 1 : 0; + glBindTexture(GL_TEXTURE_2D, m_displayTextures[target]); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, m_fbo->width(), m_fbo->height(), 0); + + if (m_onCustomThread) + m_mutex.unlock(); + } + + m_fbo->bindDefault(); } QQuickContext2DImageTexture::QQuickContext2DImageTexture() : QQuickContext2DTexture() - , m_texture(0) { } QQuickContext2DImageTexture::~QQuickContext2DImageTexture() { - if (m_texture && m_texture->thread() != QThread::currentThread()) - m_texture->deleteLater(); - else - delete m_texture; -} - -int QQuickContext2DImageTexture::textureId() const -{ - return imageTexture()->textureId(); } QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const @@ -583,22 +633,6 @@ QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const return QQuickCanvasItem::Image; } -void QQuickContext2DImageTexture::bind() -{ - imageTexture()->setFiltering(filtering()); - imageTexture()->bind(); -} - -bool QQuickContext2DImageTexture::updateTexture() -{ - bool textureUpdated = m_dirtyTexture; - if (m_dirtyTexture) { - imageTexture()->setImage(m_image); - m_dirtyTexture = false; - } - return textureUpdated; -} - QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const { return new QQuickContext2DImageTile(); @@ -608,19 +642,32 @@ void QQuickContext2DImageTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); Q_ASSERT(m_context); - QImage grabbed = m_image.copy(rf.toRect()); + QImage grabbed = m_displayImage.copy(rf.toRect()); m_context->setGrabbedImage(grabbed); } -QSGPlainTexture *QQuickContext2DImageTexture::imageTexture() const +QSGTexture *QQuickContext2DImageTexture::textureForNextFrame(QSGTexture *last) { - if (!m_texture) { - QQuickContext2DImageTexture *that = const_cast<QQuickContext2DImageTexture *>(this); - that->m_texture = new QSGPlainTexture; - that->m_texture->setOwnsTexture(true); - that->m_texture->setHasMipmaps(false); + QSGPlainTexture *texture = static_cast<QSGPlainTexture *>(last); + + if (m_onCustomThread) + m_mutex.lock(); + + if (!texture) { + texture = new QSGPlainTexture(); + texture->setHasMipmaps(false); + texture->setHasAlphaChannel(true); + m_dirtyTexture = true; + } + if (m_dirtyTexture) { + texture->setImage(m_displayImage); + m_dirtyTexture = false; } - return m_texture; + + if (m_onCustomThread) + m_mutex.unlock(); + + return texture; } QPaintDevice* QQuickContext2DImageTexture::beginPainting() @@ -639,6 +686,16 @@ QPaintDevice* QQuickContext2DImageTexture::beginPainting() return &m_image; } +void QQuickContext2DImageTexture::endPainting() +{ + QQuickContext2DTexture::endPainting(); + if (m_onCustomThread) + m_mutex.lock(); + m_displayImage = m_image; + if (m_onCustomThread) + m_mutex.unlock(); +} + void QQuickContext2DImageTexture::compositeTile(QQuickContext2DTile* tile) { Q_ASSERT(!tile->dirty()); diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 2a5b7a318a..cf0e8e3fa9 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -58,16 +58,44 @@ QT_BEGIN_NAMESPACE class QQuickContext2DTile; class QQuickContext2DCommandBuffer; -class QQuickContext2DTexture : public QSGDynamicTexture +class QQuickContext2DTexture : public QObject { Q_OBJECT public: + class PaintEvent : public QEvent { + public: + PaintEvent(QQuickContext2DCommandBuffer *b) : QEvent(QEvent::Type(QEvent::User + 1)), buffer(b) {} + QQuickContext2DCommandBuffer *buffer; + }; + + class CanvasChangeEvent : public QEvent { + public: + CanvasChangeEvent(const QSize &cSize, + const QSize &tSize, + const QRect &cWindow, + const QRect &dRect, + bool sm, + bool aa) + : QEvent(QEvent::Type(QEvent::User + 2)) + , canvasSize(cSize) + , tileSize(tSize) + , canvasWindow(cWindow) + , dirtyRect(dRect) + , smooth(sm) + , antialiasing(aa) + { + } + QSize canvasSize; + QSize tileSize; + QRect canvasWindow; + QRect dirtyRect; + bool smooth; + bool antialiasing; + }; + QQuickContext2DTexture(); ~QQuickContext2DTexture(); - virtual bool hasAlphaChannel() const {return true;} - virtual bool hasMipmaps() const {return false;} - virtual QSize textureSize() const; virtual QQuickCanvasItem::RenderTarget renderTarget() const = 0; static QRect tiledRect(const QRectF& window, const QSize& tileSize); @@ -78,15 +106,20 @@ public: void setAntialiasing(bool antialiasing); bool setDirtyRect(const QRect &dirtyRect); bool canvasDestroyed(); + void setOnCustomThread(bool is) { m_onCustomThread = is; } + + // Called during sync() on the scene graph thread while GUI is blocked. + virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame) = 0; + bool event(QEvent *e); Q_SIGNALS: void textureChanged(); public Q_SLOTS: - void markDirtyTexture(); - void setItem(QQuickCanvasItem* item); void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing); void paint(QQuickContext2DCommandBuffer *ccb); + void markDirtyTexture(); + void setItem(QQuickCanvasItem* item); virtual void grabImage(const QRectF& region = QRectF()) = 0; protected: @@ -110,13 +143,16 @@ protected: QSize m_tileSize; QRect m_canvasWindow; - uint m_dirtyCanvas : 1; + QMutex m_mutex; + QWaitCondition m_condition; + uint m_canvasWindowChanged : 1; uint m_dirtyTexture : 1; uint m_smooth : 1; uint m_antialiasing : 1; uint m_tiledCanvas : 1; uint m_painting : 1; + uint m_onCustomThread : 1; // Not GUI and not SGRender }; class QQuickContext2DFBOTexture : public QQuickContext2DTexture @@ -126,17 +162,16 @@ class QQuickContext2DFBOTexture : public QQuickContext2DTexture public: QQuickContext2DFBOTexture(); ~QQuickContext2DFBOTexture(); - virtual int textureId() const; - virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; virtual QPaintDevice* beginPainting(); virtual void endPainting(); QRectF normalizedTextureSubRect() const; virtual QQuickCanvasItem::RenderTarget renderTarget() const; virtual void compositeTile(QQuickContext2DTile* tile); - virtual void bind(); QSize adjustedTileSize(const QSize &ts); + QSGTexture *textureForNextFrame(QSGTexture *); + public Q_SLOTS: virtual void grabImage(const QRectF& region = QRectF()); @@ -144,10 +179,12 @@ private: bool doMultisampling() const; QOpenGLFramebufferObject *m_fbo; QOpenGLFramebufferObject *m_multisampledFbo; - QMutex m_mutex; - QWaitCondition m_condition; QSize m_fboSize; QPaintDevice *m_paint_device; + + + GLuint m_displayTextures[2]; + int m_displayTexture; }; class QSGPlainTexture; @@ -158,24 +195,23 @@ class QQuickContext2DImageTexture : public QQuickContext2DTexture public: QQuickContext2DImageTexture(); ~QQuickContext2DImageTexture(); - virtual int textureId() const; - virtual void bind(); virtual QQuickCanvasItem::RenderTarget renderTarget() const; - virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; virtual QPaintDevice* beginPainting(); + virtual void endPainting(); virtual void compositeTile(QQuickContext2DTile* tile); + virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame); + public Q_SLOTS: virtual void grabImage(const QRectF& region = QRectF()); private: - QSGPlainTexture *imageTexture() const; QImage m_image; + QImage m_displayImage; QPainter m_painter; - QSGPlainTexture* m_texture; }; QT_END_NAMESPACE |