aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/context2d
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2013-12-16 17:05:21 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2013-12-16 17:05:21 +0100
commit13e88fe2b9b1680cb161a249289c3ba998f08c0c (patch)
tree496a9d88c69b441e8c88aa0416b327faca3a1532 /src/quick/items/context2d
parenta2dad3ddee9c4bf274a7c6469342e4104605ceeb (diff)
parent470ba767663e4ad9d3183fb56ee89361354dfefb (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.h2
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp64
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h8
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp60
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h6
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp34
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h2
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp193
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h70
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