Reintroduce plugin support for asynchronous hardware specific textures
authorGunnar Sletta <gunnar.sletta@nokia.com>
Mon, 9 Jan 2012 07:43:34 +0000 (08:43 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 13 Jan 2012 08:27:22 +0000 (09:27 +0100)
Change-Id: Iad36542d2137e7a6470009c308ece3de389907c1
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
src/quick/scenegraph/qsgcontext.cpp
src/quick/scenegraph/qsgcontext_p.h
src/quick/scenegraph/qsgcontextplugin.cpp
src/quick/scenegraph/qsgcontextplugin_p.h
src/quick/util/qdeclarativepixmapcache.cpp

index 5af7c76..cad2cb0 100644 (file)
@@ -300,47 +300,6 @@ QSGRenderer *QSGContext::createRenderer()
 
 
 
-/*!
-    Return true if the image provider supports direct decoding of images,
-    straight into textures without going through a QImage first.
-
-    If the implementation returns true from this function, the decodeImageToTexture() function
-    will be called to read data from a QIODevice, rather than QML decoding
-    the image using QImageReader and passing the result to setImage().
-
-    \warning This function will be called from outside the GUI and rendering threads
-    and must not make use of OpenGL.
- */
-
-bool QSGContext::canDecodeImageToTexture() const
-{
-    return false;
-}
-
-
-
-/*!
-    Decode the data in \a dev directly to a texture provider of \a requestSize size.
-    The size of the decoded data should be written to \a impsize.
-
-    If the implementation fails to decode the image data, it should return 0. The
-    image data will then be decoded normally.
-
-    \warning This function will be called from outside the GUI and renderer threads
-    and must not make use of GL calls.
- */
-
-QSGTexture *QSGContext::decodeImageToTexture(QIODevice *dev,
-                                             QSize *size,
-                                             const QSize &requestSize)
-{
-    Q_UNUSED(dev);
-    Q_UNUSED(size);
-    Q_UNUSED(requestSize);
-    return 0;
-}
-
-
 
 QSurfaceFormat QSGContext::defaultSurfaceFormat() const
 {
index 0d69a4f..5e9aade 100644 (file)
@@ -102,11 +102,6 @@ public:
     virtual QSGGlyphNode *createGlyphNode();
     virtual QSGRenderer *createRenderer();
 
-    virtual bool canDecodeImageToTexture() const;
-    virtual QSGTexture *decodeImageToTexture(QIODevice *dev,
-                                             QSize *size,
-                                             const QSize &requestSize);
-
     virtual QSGTexture *createTexture(const QImage &image = QImage()) const;
     virtual QSize minimumFBOSize() const;
 
@@ -127,6 +122,8 @@ public:
 
     virtual QAnimationDriver *createAnimationDriver(QObject *parent);
 
+    static QDeclarativeTextureFactory *createTextureFactoryFromImage(const QImage &image);
+
 
 public slots:
     void textureFactoryDestroyed(QObject *o);
index a07793f..6bf6ac9 100644 (file)
@@ -61,44 +61,97 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
     (QSGContextFactoryInterface_iid, QLatin1String("/scenegraph")))
 #endif
 
-/*!
-    \fn QSGContext *QSGContext::createDefaultContext()
+struct QSGAdaptionPluginData
+{
+    QSGAdaptionPluginData()
+        : tried(false)
+        , factory(0)
+    {
+    }
 
-    Creates a default scene graph context for the current hardware.
-    This may load a device-specific plugin.
-*/
-QSGContext *QSGContext::createDefaultContext()
+    ~QSGAdaptionPluginData()
+    {
+        delete factory;
+    }
+
+    bool tried;
+    QSGContextFactoryInterface *factory;
+    QString deviceName;
+};
+
+QThreadStorage<QSGAdaptionPluginData> qsg_plugin_data;
+
+
+QSGAdaptionPluginData *contextFactory()
 {
-    const QStringList args = QGuiApplication::arguments();
-    QString device;
-    for (int index = 0; index < args.count(); ++index) {
-        if (args.at(index).startsWith(QLatin1String("--device="))) {
-            device = args.at(index).mid(9);
-            break;
+    QSGAdaptionPluginData &plugin = qsg_plugin_data.localData();
+    if (!plugin.tried) {
+        plugin.tried = true;
+        const QStringList args = QGuiApplication::arguments();
+        QString device;
+        for (int index = 0; index < args.count(); ++index) {
+            if (args.at(index).startsWith(QLatin1String("--device="))) {
+                device = args.at(index).mid(9);
+                break;
+            }
         }
-    }
-    if (device.isEmpty())
-        device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE"));
-    if (device.isEmpty())
-        return new QSGContext();
+        if (device.isEmpty())
+            device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE"));
 
 #if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
-    if (QSGContextFactoryInterface *factory
-            = qobject_cast<QSGContextFactoryInterface*>
-                (loader()->instance(device))) {
-        QSGContext *context = factory->create(device);
-        if (context)
-            return context;
-    }
+        if (!device.isEmpty()) {
+            plugin.factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(device));
+            plugin.deviceName = device;
+        }
 #ifndef QT_NO_DEBUG
-    qWarning("Could not create scene graph context for device '%s'"
-             " - check that plugins are installed correctly in %s",
-             qPrintable(device),
-             qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath)));
+        if (!device.isEmpty()) {
+            qWarning("Could not create scene graph context for device '%s'"
+                     " - check that plugins are installed correctly in %s",
+                     qPrintable(device),
+                     qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath)));
+        }
 #endif
+
 #endif // QT_NO_LIBRARY || QT_NO_SETTINGS
+    }
+    return &plugin;
+}
 
+
+
+/*!
+    \fn QSGContext *QSGContext::createDefaultContext()
+
+    Creates a default scene graph context for the current hardware.
+    This may load a device-specific plugin.
+*/
+QSGContext *QSGContext::createDefaultContext()
+{
+    QSGAdaptionPluginData *plugin = contextFactory();
+    if (plugin->factory)
+        return plugin->factory->create(plugin->deviceName);
     return new QSGContext();
 }
 
+
+
+/*!
+    \fn QDeclarativeTextureFactory *createTextureFactoryFromImage(const QImage &image)
+
+    Calls into the scene graph adaptation if available and creates a texture
+    factory. The primary purpose of this function is to reimplement hardware
+    specific asynchronous texture frameskip-less uploads that can happen on
+    the image providers thread.
+ */
+
+QDeclarativeTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image)
+{
+    QSGAdaptionPluginData *plugin = contextFactory();
+    if (plugin->factory)
+        return plugin->factory->createTextureFactoryFromImage(image);
+    return 0;
+}
+
+
+
 QT_END_NAMESPACE
index a480ee5..21924c9 100644 (file)
@@ -46,6 +46,8 @@
 #include <QtCore/qplugin.h>
 #include <QtCore/qfactoryinterface.h>
 
+#include <QDeclarativeImageProvider>
+
 QT_BEGIN_HEADER
 
 QT_BEGIN_NAMESPACE
@@ -55,6 +57,8 @@ class QSGContext;
 struct Q_QUICK_EXPORT QSGContextFactoryInterface : public QFactoryInterface
 {
     virtual QSGContext *create(const QString &key) const = 0;
+
+    virtual QDeclarativeTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0;
 };
 
 #define QSGContextFactoryInterface_iid \
@@ -71,6 +75,8 @@ public:
 
     virtual QStringList keys() const = 0;
     virtual QSGContext *create(const QString &key) const = 0;
+
+    virtual QDeclarativeTextureFactory *createTextureFactoryFromImage(const QImage &image) { return 0; }
 };
 
 QT_END_NAMESPACE
index 006d720..cb29ec6 100644 (file)
@@ -48,6 +48,7 @@
 #include <private/qdeclarativeengine_p.h>
 
 #include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgcontext_p.h>
 
 #include <QCoreApplication>
 #include <QImageReader>
@@ -64,7 +65,6 @@
 #include <QtCore/qdebug.h>
 #include <private/qobject_p.h>
 #include <QSslError>
-#include <QOpenGLContext>
 
 #define IMAGEREQUEST_MAX_REQUEST_COUNT       8
 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16
@@ -430,7 +430,11 @@ void QDeclarativePixmapReader::networkRequestDone(QNetworkReply *reply)
         // send completion event to the QDeclarativePixmapReply
         mutex.lock();
         if (!cancelled.contains(job)) {
-            job->postReply(error, errorString, readSize, image);
+            QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
+            if (factory)
+                job->postReply(error, errorString, readSize, factory, image);
+            else
+                job->postReply(error, errorString, readSize, image);
         }
         mutex.unlock();
     }
@@ -529,8 +533,14 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c
                 errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString());
             }
             mutex.lock();
-            if (!cancelled.contains(runningJob))
-                runningJob->postReply(errorCode, errorStr, readSize, image);
+            if (!cancelled.contains(runningJob)) {
+                QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
+                if (factory)
+                    runningJob->postReply(errorCode, errorStr, readSize, factory, image);
+                else
+                    runningJob->postReply(errorCode, errorStr, readSize, image);
+            }
+
             mutex.unlock();
         } else {
             QDeclarativeTextureFactory *t = ep->getTextureFromProvider(url, &readSize, requestSize);
@@ -564,8 +574,13 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c
                 errorCode = QDeclarativePixmapReply::Loading;
             }
             mutex.lock();
-            if (!cancelled.contains(runningJob))
-                runningJob->postReply(errorCode, errorStr, readSize, image);
+            if (!cancelled.contains(runningJob)) {
+                QDeclarativeTextureFactory *factory = QSGContext::createTextureFactoryFromImage(image);
+                if (factory)
+                    runningJob->postReply(errorCode, errorStr, readSize, factory, image);
+                else
+                    runningJob->postReply(errorCode, errorStr, readSize, image);
+            }
             mutex.unlock();
         } else {
             // Network resource